4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2651 onRender : function(ct, position)
2653 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2656 var cfg = Roo.apply({}, this.getAutoCreate());
2659 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2661 //if (!cfg.name.length) {
2665 cfg.cls += ' ' + this.cls;
2668 cfg.style = this.style;
2670 this.el = Roo.get(document.body).createChild(cfg, position);
2672 //var type = this.el.dom.type;
2675 if(this.tabIndex !== undefined){
2676 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2679 this.dialogEl = this.el.select('.modal-dialog',true).first();
2680 this.bodyEl = this.el.select('.modal-body',true).first();
2681 this.closeEl = this.el.select('.modal-header .close', true).first();
2682 this.headerEl = this.el.select('.modal-header',true).first();
2683 this.titleEl = this.el.select('.modal-title',true).first();
2684 this.footerEl = this.el.select('.modal-footer',true).first();
2686 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2688 //this.el.addClass("x-dlg-modal");
2690 if (this.buttons.length) {
2691 Roo.each(this.buttons, function(bb) {
2692 var b = Roo.apply({}, bb);
2693 b.xns = b.xns || Roo.bootstrap;
2694 b.xtype = b.xtype || 'Button';
2695 if (typeof(b.listeners) == 'undefined') {
2696 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2699 var btn = Roo.factory(b);
2701 btn.render(this.el.select('.modal-footer div').first());
2705 // render the children.
2708 if(typeof(this.items) != 'undefined'){
2709 var items = this.items;
2712 for(var i =0;i < items.length;i++) {
2713 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2717 this.items = nitems;
2719 // where are these used - they used to be body/close/footer
2723 //this.el.addClass([this.fieldClass, this.cls]);
2727 getAutoCreate : function()
2731 html : this.html || ''
2736 cls : 'modal-title',
2740 if(this.specificTitle){
2746 if (this.allow_close) {
2758 if(this.size.length){
2759 size = 'modal-' + this.size;
2766 cls: "modal-dialog " + size,
2769 cls : "modal-content",
2772 cls : 'modal-header',
2777 cls : 'modal-footer',
2781 cls: 'btn-' + this.buttonPosition
2798 modal.cls += ' fade';
2804 getChildContainer : function() {
2809 getButtonContainer : function() {
2810 return this.el.select('.modal-footer div',true).first();
2813 initEvents : function()
2815 if (this.allow_close) {
2816 this.closeEl.on('click', this.hide, this);
2818 Roo.EventManager.onWindowResize(this.resize, this, true);
2825 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2827 if (this.fitwindow) {
2828 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2829 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2833 if(!this.fitwindow && this.max_width !== 0){
2835 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2839 this.setSize(w, this.height);
2843 if(!this.fit_content) {
2844 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2847 var body_childs = this.bodyEl.dom.childNodes;
2848 var full_height = this.headerEl.getHeight() + this.footerEl.getHeight();
2849 for(var i = 0; i < body_childs.length; i++) {
2851 // if(body_childs[i].classList.indexOf('roo-layout-region') * 1 != -1) {
2852 // var layout_childs = body_childs[i].childNodes;
2853 // for(var j = 0; j < layout_childs.length; j++) {
2858 full_height += body_childs[i].offsetHeight;
2861 this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2866 setSize : function(w,h)
2876 if (!this.rendered) {
2880 //this.el.setStyle('display', 'block');
2881 this.el.removeClass('hideing');
2882 this.el.addClass('show');
2884 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2887 this.el.addClass('in');
2890 this.el.addClass('in');
2893 // not sure how we can show data in here..
2895 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898 Roo.get(document.body).addClass("x-body-masked");
2900 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2901 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902 this.maskEl.addClass('show');
2906 this.fireEvent('show', this);
2908 // set zindex here - otherwise it appears to be ignored...
2909 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912 this.items.forEach( function(e) {
2913 e.layout ? e.layout() : false;
2921 if(this.fireEvent("beforehide", this) !== false){
2922 this.maskEl.removeClass('show');
2923 Roo.get(document.body).removeClass("x-body-masked");
2924 this.el.removeClass('in');
2925 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2927 if(this.animate){ // why
2928 this.el.addClass('hideing');
2930 if (!this.el.hasClass('hideing')) {
2931 return; // it's been shown again...
2933 this.el.removeClass('show');
2934 this.el.removeClass('hideing');
2938 this.el.removeClass('show');
2940 this.fireEvent('hide', this);
2943 isVisible : function()
2946 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950 addButton : function(str, cb)
2954 var b = Roo.apply({}, { html : str } );
2955 b.xns = b.xns || Roo.bootstrap;
2956 b.xtype = b.xtype || 'Button';
2957 if (typeof(b.listeners) == 'undefined') {
2958 b.listeners = { click : cb.createDelegate(this) };
2961 var btn = Roo.factory(b);
2963 btn.render(this.el.select('.modal-footer div').first());
2969 setDefaultButton : function(btn)
2971 //this.el.select('.modal-footer').()
2975 resizeTo: function(w,h)
2979 this.dialogEl.setWidth(w);
2980 if (this.diff === false) {
2981 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984 this.bodyEl.setHeight(h-this.diff);
2986 this.fireEvent('resize', this);
2989 setContentSize : function(w, h)
2993 onButtonClick: function(btn,e)
2996 this.fireEvent('btnclick', btn.name, e);
2999 * Set the title of the Dialog
3000 * @param {String} str new Title
3002 setTitle: function(str) {
3003 this.titleEl.dom.innerHTML = str;
3006 * Set the body of the Dialog
3007 * @param {String} str new Title
3009 setBody: function(str) {
3010 this.bodyEl.dom.innerHTML = str;
3013 * Set the body of the Dialog using the template
3014 * @param {Obj} data - apply this data to the template and replace the body contents.
3016 applyBody: function(obj)
3019 Roo.log("Error - using apply Body without a template");
3022 this.tmpl.overwrite(this.bodyEl, obj);
3028 Roo.apply(Roo.bootstrap.Modal, {
3030 * Button config that displays a single OK button
3039 * Button config that displays Yes and No buttons
3055 * Button config that displays OK and Cancel buttons
3070 * Button config that displays Yes, No and Cancel buttons
3094 * messagebox - can be used as a replace
3098 * @class Roo.MessageBox
3099 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3103 Roo.Msg.alert('Status', 'Changes saved successfully.');
3105 // Prompt for user data:
3106 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3108 // process text value...
3112 // Show a dialog using config options:
3114 title:'Save Changes?',
3115 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3116 buttons: Roo.Msg.YESNOCANCEL,
3123 Roo.bootstrap.MessageBox = function(){
3124 var dlg, opt, mask, waitTimer;
3125 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3126 var buttons, activeTextEl, bwidth;
3130 var handleButton = function(button){
3132 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3136 var handleHide = function(){
3138 dlg.el.removeClass(opt.cls);
3141 // Roo.TaskMgr.stop(waitTimer);
3142 // waitTimer = null;
3147 var updateButtons = function(b){
3150 buttons["ok"].hide();
3151 buttons["cancel"].hide();
3152 buttons["yes"].hide();
3153 buttons["no"].hide();
3154 //dlg.footer.dom.style.display = 'none';
3157 dlg.footerEl.dom.style.display = '';
3158 for(var k in buttons){
3159 if(typeof buttons[k] != "function"){
3162 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3163 width += buttons[k].el.getWidth()+15;
3173 var handleEsc = function(d, k, e){
3174 if(opt && opt.closable !== false){
3184 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3185 * @return {Roo.BasicDialog} The BasicDialog element
3187 getDialog : function(){
3189 dlg = new Roo.bootstrap.Modal( {
3192 //constraintoviewport:false,
3194 //collapsible : false,
3199 //buttonAlign:"center",
3200 closeClick : function(){
3201 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3204 handleButton("cancel");
3209 dlg.on("hide", handleHide);
3211 //dlg.addKeyListener(27, handleEsc);
3213 this.buttons = buttons;
3214 var bt = this.buttonText;
3215 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3216 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3217 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3218 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3220 bodyEl = dlg.bodyEl.createChild({
3222 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3223 '<textarea class="roo-mb-textarea"></textarea>' +
3224 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3226 msgEl = bodyEl.dom.firstChild;
3227 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3228 textboxEl.enableDisplayMode();
3229 textboxEl.addKeyListener([10,13], function(){
3230 if(dlg.isVisible() && opt && opt.buttons){
3233 }else if(opt.buttons.yes){
3234 handleButton("yes");
3238 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3239 textareaEl.enableDisplayMode();
3240 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3241 progressEl.enableDisplayMode();
3243 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3244 var pf = progressEl.dom.firstChild;
3246 pp = Roo.get(pf.firstChild);
3247 pp.setHeight(pf.offsetHeight);
3255 * Updates the message box body text
3256 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3257 * the XHTML-compliant non-breaking space character '&#160;')
3258 * @return {Roo.MessageBox} This message box
3260 updateText : function(text)
3262 if(!dlg.isVisible() && !opt.width){
3263 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3264 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3266 msgEl.innerHTML = text || ' ';
3268 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3269 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3271 Math.min(opt.width || cw , this.maxWidth),
3272 Math.max(opt.minWidth || this.minWidth, bwidth)
3275 activeTextEl.setWidth(w);
3277 if(dlg.isVisible()){
3278 dlg.fixedcenter = false;
3280 // to big, make it scroll. = But as usual stupid IE does not support
3283 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3284 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3285 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3287 bodyEl.dom.style.height = '';
3288 bodyEl.dom.style.overflowY = '';
3291 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3293 bodyEl.dom.style.overflowX = '';
3296 dlg.setContentSize(w, bodyEl.getHeight());
3297 if(dlg.isVisible()){
3298 dlg.fixedcenter = true;
3304 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3305 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3306 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3307 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3308 * @return {Roo.MessageBox} This message box
3310 updateProgress : function(value, text){
3312 this.updateText(text);
3315 if (pp) { // weird bug on my firefox - for some reason this is not defined
3316 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3317 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3323 * Returns true if the message box is currently displayed
3324 * @return {Boolean} True if the message box is visible, else false
3326 isVisible : function(){
3327 return dlg && dlg.isVisible();
3331 * Hides the message box if it is displayed
3334 if(this.isVisible()){
3340 * Displays a new message box, or reinitializes an existing message box, based on the config options
3341 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3342 * The following config object properties are supported:
3344 Property Type Description
3345 ---------- --------------- ------------------------------------------------------------------------------------
3346 animEl String/Element An id or Element from which the message box should animate as it opens and
3347 closes (defaults to undefined)
3348 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3349 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3350 closable Boolean False to hide the top-right close button (defaults to true). Note that
3351 progress and wait dialogs will ignore this property and always hide the
3352 close button as they can only be closed programmatically.
3353 cls String A custom CSS class to apply to the message box element
3354 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3355 displayed (defaults to 75)
3356 fn Function A callback function to execute after closing the dialog. The arguments to the
3357 function will be btn (the name of the button that was clicked, if applicable,
3358 e.g. "ok"), and text (the value of the active text field, if applicable).
3359 Progress and wait dialogs will ignore this option since they do not respond to
3360 user actions and can only be closed programmatically, so any required function
3361 should be called by the same code after it closes the dialog.
3362 icon String A CSS class that provides a background image to be used as an icon for
3363 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3364 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3365 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3366 modal Boolean False to allow user interaction with the page while the message box is
3367 displayed (defaults to true)
3368 msg String A string that will replace the existing message box body text (defaults
3369 to the XHTML-compliant non-breaking space character ' ')
3370 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3371 progress Boolean True to display a progress bar (defaults to false)
3372 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3373 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3374 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3375 title String The title text
3376 value String The string value to set into the active textbox element if displayed
3377 wait Boolean True to display a progress bar (defaults to false)
3378 width Number The width of the dialog in pixels
3385 msg: 'Please enter your address:',
3387 buttons: Roo.MessageBox.OKCANCEL,
3390 animEl: 'addAddressBtn'
3393 * @param {Object} config Configuration options
3394 * @return {Roo.MessageBox} This message box
3396 show : function(options)
3399 // this causes nightmares if you show one dialog after another
3400 // especially on callbacks..
3402 if(this.isVisible()){
3405 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3406 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3407 Roo.log("New Dialog Message:" + options.msg )
3408 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3409 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3412 var d = this.getDialog();
3414 d.setTitle(opt.title || " ");
3415 d.closeEl.setDisplayed(opt.closable !== false);
3416 activeTextEl = textboxEl;
3417 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3422 textareaEl.setHeight(typeof opt.multiline == "number" ?
3423 opt.multiline : this.defaultTextHeight);
3424 activeTextEl = textareaEl;
3433 progressEl.setDisplayed(opt.progress === true);
3434 this.updateProgress(0);
3435 activeTextEl.dom.value = opt.value || "";
3437 dlg.setDefaultButton(activeTextEl);
3439 var bs = opt.buttons;
3443 }else if(bs && bs.yes){
3444 db = buttons["yes"];
3446 dlg.setDefaultButton(db);
3448 bwidth = updateButtons(opt.buttons);
3449 this.updateText(opt.msg);
3451 d.el.addClass(opt.cls);
3453 d.proxyDrag = opt.proxyDrag === true;
3454 d.modal = opt.modal !== false;
3455 d.mask = opt.modal !== false ? mask : false;
3457 // force it to the end of the z-index stack so it gets a cursor in FF
3458 document.body.appendChild(dlg.el.dom);
3459 d.animateTarget = null;
3460 d.show(options.animEl);
3466 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3467 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3468 * and closing the message box when the process is complete.
3469 * @param {String} title The title bar text
3470 * @param {String} msg The message box body text
3471 * @return {Roo.MessageBox} This message box
3473 progress : function(title, msg){
3480 minWidth: this.minProgressWidth,
3487 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3488 * If a callback function is passed it will be called after the user clicks the button, and the
3489 * id of the button that was clicked will be passed as the only parameter to the callback
3490 * (could also be the top-right close button).
3491 * @param {String} title The title bar text
3492 * @param {String} msg The message box body text
3493 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3494 * @param {Object} scope (optional) The scope of the callback function
3495 * @return {Roo.MessageBox} This message box
3497 alert : function(title, msg, fn, scope)
3512 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3513 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3514 * You are responsible for closing the message box when the process is complete.
3515 * @param {String} msg The message box body text
3516 * @param {String} title (optional) The title bar text
3517 * @return {Roo.MessageBox} This message box
3519 wait : function(msg, title){
3530 waitTimer = Roo.TaskMgr.start({
3532 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3540 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3541 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3542 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3543 * @param {String} title The title bar text
3544 * @param {String} msg The message box body text
3545 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3546 * @param {Object} scope (optional) The scope of the callback function
3547 * @return {Roo.MessageBox} This message box
3549 confirm : function(title, msg, fn, scope){
3553 buttons: this.YESNO,
3562 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3563 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3564 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3565 * (could also be the top-right close button) and the text that was entered will be passed as the two
3566 * parameters to the callback.
3567 * @param {String} title The title bar text
3568 * @param {String} msg The message box body text
3569 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3570 * @param {Object} scope (optional) The scope of the callback function
3571 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3572 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3573 * @return {Roo.MessageBox} This message box
3575 prompt : function(title, msg, fn, scope, multiline){
3579 buttons: this.OKCANCEL,
3584 multiline: multiline,
3591 * Button config that displays a single OK button
3596 * Button config that displays Yes and No buttons
3599 YESNO : {yes:true, no:true},
3601 * Button config that displays OK and Cancel buttons
3604 OKCANCEL : {ok:true, cancel:true},
3606 * Button config that displays Yes, No and Cancel buttons
3609 YESNOCANCEL : {yes:true, no:true, cancel:true},
3612 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3615 defaultTextHeight : 75,
3617 * The maximum width in pixels of the message box (defaults to 600)
3622 * The minimum width in pixels of the message box (defaults to 100)
3627 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3628 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3631 minProgressWidth : 250,
3633 * An object containing the default button text strings that can be overriden for localized language support.
3634 * Supported properties are: ok, cancel, yes and no.
3635 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3648 * Shorthand for {@link Roo.MessageBox}
3650 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3651 Roo.Msg = Roo.Msg || Roo.MessageBox;
3660 * @class Roo.bootstrap.Navbar
3661 * @extends Roo.bootstrap.Component
3662 * Bootstrap Navbar class
3665 * Create a new Navbar
3666 * @param {Object} config The config object
3670 Roo.bootstrap.Navbar = function(config){
3671 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3675 * @event beforetoggle
3676 * Fire before toggle the menu
3677 * @param {Roo.EventObject} e
3679 "beforetoggle" : true
3683 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3692 getAutoCreate : function(){
3695 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3699 initEvents :function ()
3701 //Roo.log(this.el.select('.navbar-toggle',true));
3702 this.el.select('.navbar-toggle',true).on('click', function() {
3703 if(this.fireEvent('beforetoggle', this) !== false){
3704 this.el.select('.navbar-collapse',true).toggleClass('in');
3714 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3716 var size = this.el.getSize();
3717 this.maskEl.setSize(size.width, size.height);
3718 this.maskEl.enableDisplayMode("block");
3727 getChildContainer : function()
3729 if (this.el.select('.collapse').getCount()) {
3730 return this.el.select('.collapse',true).first();
3763 * @class Roo.bootstrap.NavSimplebar
3764 * @extends Roo.bootstrap.Navbar
3765 * Bootstrap Sidebar class
3767 * @cfg {Boolean} inverse is inverted color
3769 * @cfg {String} type (nav | pills | tabs)
3770 * @cfg {Boolean} arrangement stacked | justified
3771 * @cfg {String} align (left | right) alignment
3773 * @cfg {Boolean} main (true|false) main nav bar? default false
3774 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3776 * @cfg {String} tag (header|footer|nav|div) default is nav
3782 * Create a new Sidebar
3783 * @param {Object} config The config object
3787 Roo.bootstrap.NavSimplebar = function(config){
3788 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3791 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3807 getAutoCreate : function(){
3811 tag : this.tag || 'div',
3824 this.type = this.type || 'nav';
3825 if (['tabs','pills'].indexOf(this.type)!==-1) {
3826 cfg.cn[0].cls += ' nav-' + this.type
3830 if (this.type!=='nav') {
3831 Roo.log('nav type must be nav/tabs/pills')
3833 cfg.cn[0].cls += ' navbar-nav'
3839 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3840 cfg.cn[0].cls += ' nav-' + this.arrangement;
3844 if (this.align === 'right') {
3845 cfg.cn[0].cls += ' navbar-right';
3849 cfg.cls += ' navbar-inverse';
3876 * @class Roo.bootstrap.NavHeaderbar
3877 * @extends Roo.bootstrap.NavSimplebar
3878 * Bootstrap Sidebar class
3880 * @cfg {String} brand what is brand
3881 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3882 * @cfg {String} brand_href href of the brand
3883 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3884 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3885 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3886 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3889 * Create a new Sidebar
3890 * @param {Object} config The config object
3894 Roo.bootstrap.NavHeaderbar = function(config){
3895 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3899 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3906 desktopCenter : false,
3909 getAutoCreate : function(){
3912 tag: this.nav || 'nav',
3919 if (this.desktopCenter) {
3920 cn.push({cls : 'container', cn : []});
3927 cls: 'navbar-header',
3932 cls: 'navbar-toggle',
3933 'data-toggle': 'collapse',
3938 html: 'Toggle navigation'
3960 cls: 'collapse navbar-collapse',
3964 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3966 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3967 cfg.cls += ' navbar-' + this.position;
3969 // tag can override this..
3971 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3974 if (this.brand !== '') {
3977 href: this.brand_href ? this.brand_href : '#',
3978 cls: 'navbar-brand',
3986 cfg.cls += ' main-nav';
3994 getHeaderChildContainer : function()
3996 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3997 return this.el.select('.navbar-header',true).first();
4000 return this.getChildContainer();
4004 initEvents : function()
4006 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4008 if (this.autohide) {
4013 Roo.get(document).on('scroll',function(e) {
4014 var ns = Roo.get(document).getScroll().top;
4015 var os = prevScroll;
4019 ft.removeClass('slideDown');
4020 ft.addClass('slideUp');
4023 ft.removeClass('slideUp');
4024 ft.addClass('slideDown');
4045 * @class Roo.bootstrap.NavSidebar
4046 * @extends Roo.bootstrap.Navbar
4047 * Bootstrap Sidebar class
4050 * Create a new Sidebar
4051 * @param {Object} config The config object
4055 Roo.bootstrap.NavSidebar = function(config){
4056 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4059 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4061 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4063 getAutoCreate : function(){
4068 cls: 'sidebar sidebar-nav'
4090 * @class Roo.bootstrap.NavGroup
4091 * @extends Roo.bootstrap.Component
4092 * Bootstrap NavGroup class
4093 * @cfg {String} align (left|right)
4094 * @cfg {Boolean} inverse
4095 * @cfg {String} type (nav|pills|tab) default nav
4096 * @cfg {String} navId - reference Id for navbar.
4100 * Create a new nav group
4101 * @param {Object} config The config object
4104 Roo.bootstrap.NavGroup = function(config){
4105 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4108 Roo.bootstrap.NavGroup.register(this);
4112 * Fires when the active item changes
4113 * @param {Roo.bootstrap.NavGroup} this
4114 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4115 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4122 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4133 getAutoCreate : function()
4135 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4142 if (['tabs','pills'].indexOf(this.type)!==-1) {
4143 cfg.cls += ' nav-' + this.type
4145 if (this.type!=='nav') {
4146 Roo.log('nav type must be nav/tabs/pills')
4148 cfg.cls += ' navbar-nav'
4151 if (this.parent() && this.parent().sidebar) {
4154 cls: 'dashboard-menu sidebar-menu'
4160 if (this.form === true) {
4166 if (this.align === 'right') {
4167 cfg.cls += ' navbar-right';
4169 cfg.cls += ' navbar-left';
4173 if (this.align === 'right') {
4174 cfg.cls += ' navbar-right';
4178 cfg.cls += ' navbar-inverse';
4186 * sets the active Navigation item
4187 * @param {Roo.bootstrap.NavItem} the new current navitem
4189 setActiveItem : function(item)
4192 Roo.each(this.navItems, function(v){
4197 v.setActive(false, true);
4204 item.setActive(true, true);
4205 this.fireEvent('changed', this, item, prev);
4210 * gets the active Navigation item
4211 * @return {Roo.bootstrap.NavItem} the current navitem
4213 getActive : function()
4217 Roo.each(this.navItems, function(v){
4228 indexOfNav : function()
4232 Roo.each(this.navItems, function(v,i){
4243 * adds a Navigation item
4244 * @param {Roo.bootstrap.NavItem} the navitem to add
4246 addItem : function(cfg)
4248 var cn = new Roo.bootstrap.NavItem(cfg);
4250 cn.parentId = this.id;
4251 cn.onRender(this.el, null);
4255 * register a Navigation item
4256 * @param {Roo.bootstrap.NavItem} the navitem to add
4258 register : function(item)
4260 this.navItems.push( item);
4261 item.navId = this.navId;
4266 * clear all the Navigation item
4269 clearAll : function()
4272 this.el.dom.innerHTML = '';
4275 getNavItem: function(tabId)
4278 Roo.each(this.navItems, function(e) {
4279 if (e.tabId == tabId) {
4289 setActiveNext : function()
4291 var i = this.indexOfNav(this.getActive());
4292 if (i > this.navItems.length) {
4295 this.setActiveItem(this.navItems[i+1]);
4297 setActivePrev : function()
4299 var i = this.indexOfNav(this.getActive());
4303 this.setActiveItem(this.navItems[i-1]);
4305 clearWasActive : function(except) {
4306 Roo.each(this.navItems, function(e) {
4307 if (e.tabId != except.tabId && e.was_active) {
4308 e.was_active = false;
4315 getWasActive : function ()
4318 Roo.each(this.navItems, function(e) {
4333 Roo.apply(Roo.bootstrap.NavGroup, {
4337 * register a Navigation Group
4338 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4340 register : function(navgrp)
4342 this.groups[navgrp.navId] = navgrp;
4346 * fetch a Navigation Group based on the navigation ID
4347 * @param {string} the navgroup to add
4348 * @returns {Roo.bootstrap.NavGroup} the navgroup
4350 get: function(navId) {
4351 if (typeof(this.groups[navId]) == 'undefined') {
4353 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4355 return this.groups[navId] ;
4370 * @class Roo.bootstrap.NavItem
4371 * @extends Roo.bootstrap.Component
4372 * Bootstrap Navbar.NavItem class
4373 * @cfg {String} href link to
4374 * @cfg {String} html content of button
4375 * @cfg {String} badge text inside badge
4376 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4377 * @cfg {String} glyphicon name of glyphicon
4378 * @cfg {String} icon name of font awesome icon
4379 * @cfg {Boolean} active Is item active
4380 * @cfg {Boolean} disabled Is item disabled
4382 * @cfg {Boolean} preventDefault (true | false) default false
4383 * @cfg {String} tabId the tab that this item activates.
4384 * @cfg {String} tagtype (a|span) render as a href or span?
4385 * @cfg {Boolean} animateRef (true|false) link to element default false
4388 * Create a new Navbar Item
4389 * @param {Object} config The config object
4391 Roo.bootstrap.NavItem = function(config){
4392 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4397 * The raw click event for the entire grid.
4398 * @param {Roo.EventObject} e
4403 * Fires when the active item active state changes
4404 * @param {Roo.bootstrap.NavItem} this
4405 * @param {boolean} state the new state
4411 * Fires when scroll to element
4412 * @param {Roo.bootstrap.NavItem} this
4413 * @param {Object} options
4414 * @param {Roo.EventObject} e
4422 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4430 preventDefault : false,
4437 getAutoCreate : function(){
4446 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4448 if (this.disabled) {
4449 cfg.cls += ' disabled';
4452 if (this.href || this.html || this.glyphicon || this.icon) {
4456 href : this.href || "#",
4457 html: this.html || ''
4462 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4465 if(this.glyphicon) {
4466 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4471 cfg.cn[0].html += " <span class='caret'></span>";
4475 if (this.badge !== '') {
4477 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4485 initEvents: function()
4487 if (typeof (this.menu) != 'undefined') {
4488 this.menu.parentType = this.xtype;
4489 this.menu.triggerEl = this.el;
4490 this.menu = this.addxtype(Roo.apply({}, this.menu));
4493 this.el.select('a',true).on('click', this.onClick, this);
4495 if(this.tagtype == 'span'){
4496 this.el.select('span',true).on('click', this.onClick, this);
4499 // at this point parent should be available..
4500 this.parent().register(this);
4503 onClick : function(e)
4505 if (e.getTarget('.dropdown-menu-item')) {
4506 // did you click on a menu itemm.... - then don't trigger onclick..
4511 this.preventDefault ||
4514 Roo.log("NavItem - prevent Default?");
4518 if (this.disabled) {
4522 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4523 if (tg && tg.transition) {
4524 Roo.log("waiting for the transitionend");
4530 //Roo.log("fire event clicked");
4531 if(this.fireEvent('click', this, e) === false){
4535 if(this.tagtype == 'span'){
4539 //Roo.log(this.href);
4540 var ael = this.el.select('a',true).first();
4543 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4544 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4545 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4546 return; // ignore... - it's a 'hash' to another page.
4548 Roo.log("NavItem - prevent Default?");
4550 this.scrollToElement(e);
4554 var p = this.parent();
4556 if (['tabs','pills'].indexOf(p.type)!==-1) {
4557 if (typeof(p.setActiveItem) !== 'undefined') {
4558 p.setActiveItem(this);
4562 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4563 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4564 // remove the collapsed menu expand...
4565 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4569 isActive: function () {
4572 setActive : function(state, fire, is_was_active)
4574 if (this.active && !state && this.navId) {
4575 this.was_active = true;
4576 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4578 nv.clearWasActive(this);
4582 this.active = state;
4585 this.el.removeClass('active');
4586 } else if (!this.el.hasClass('active')) {
4587 this.el.addClass('active');
4590 this.fireEvent('changed', this, state);
4593 // show a panel if it's registered and related..
4595 if (!this.navId || !this.tabId || !state || is_was_active) {
4599 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4603 var pan = tg.getPanelByName(this.tabId);
4607 // if we can not flip to new panel - go back to old nav highlight..
4608 if (false == tg.showPanel(pan)) {
4609 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4611 var onav = nv.getWasActive();
4613 onav.setActive(true, false, true);
4622 // this should not be here...
4623 setDisabled : function(state)
4625 this.disabled = state;
4627 this.el.removeClass('disabled');
4628 } else if (!this.el.hasClass('disabled')) {
4629 this.el.addClass('disabled');
4635 * Fetch the element to display the tooltip on.
4636 * @return {Roo.Element} defaults to this.el
4638 tooltipEl : function()
4640 return this.el.select('' + this.tagtype + '', true).first();
4643 scrollToElement : function(e)
4645 var c = document.body;
4648 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4650 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4651 c = document.documentElement;
4654 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4660 var o = target.calcOffsetsTo(c);
4667 this.fireEvent('scrollto', this, options, e);
4669 Roo.get(c).scrollTo('top', options.value, true);
4682 * <span> icon </span>
4683 * <span> text </span>
4684 * <span>badge </span>
4688 * @class Roo.bootstrap.NavSidebarItem
4689 * @extends Roo.bootstrap.NavItem
4690 * Bootstrap Navbar.NavSidebarItem class
4691 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4692 * {Boolean} open is the menu open
4693 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4694 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4695 * {String} buttonSize (sm|md|lg)the extra classes for the button
4696 * {Boolean} showArrow show arrow next to the text (default true)
4698 * Create a new Navbar Button
4699 * @param {Object} config The config object
4701 Roo.bootstrap.NavSidebarItem = function(config){
4702 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4707 * The raw click event for the entire grid.
4708 * @param {Roo.EventObject} e
4713 * Fires when the active item active state changes
4714 * @param {Roo.bootstrap.NavSidebarItem} this
4715 * @param {boolean} state the new state
4723 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4725 badgeWeight : 'default',
4731 buttonWeight : 'default',
4737 getAutoCreate : function(){
4742 href : this.href || '#',
4748 if(this.buttonView){
4751 href : this.href || '#',
4752 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4765 cfg.cls += ' active';
4768 if (this.disabled) {
4769 cfg.cls += ' disabled';
4772 cfg.cls += ' open x-open';
4775 if (this.glyphicon || this.icon) {
4776 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4777 a.cn.push({ tag : 'i', cls : c }) ;
4780 if(!this.buttonView){
4783 html : this.html || ''
4790 if (this.badge !== '') {
4791 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4797 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4800 a.cls += ' dropdown-toggle treeview' ;
4806 initEvents : function()
4808 if (typeof (this.menu) != 'undefined') {
4809 this.menu.parentType = this.xtype;
4810 this.menu.triggerEl = this.el;
4811 this.menu = this.addxtype(Roo.apply({}, this.menu));
4814 this.el.on('click', this.onClick, this);
4816 if(this.badge !== ''){
4817 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4822 onClick : function(e)
4829 if(this.preventDefault){
4833 this.fireEvent('click', this);
4836 disable : function()
4838 this.setDisabled(true);
4843 this.setDisabled(false);
4846 setDisabled : function(state)
4848 if(this.disabled == state){
4852 this.disabled = state;
4855 this.el.addClass('disabled');
4859 this.el.removeClass('disabled');
4864 setActive : function(state)
4866 if(this.active == state){
4870 this.active = state;
4873 this.el.addClass('active');
4877 this.el.removeClass('active');
4882 isActive: function ()
4887 setBadge : function(str)
4893 this.badgeEl.dom.innerHTML = str;
4910 * @class Roo.bootstrap.Row
4911 * @extends Roo.bootstrap.Component
4912 * Bootstrap Row class (contains columns...)
4916 * @param {Object} config The config object
4919 Roo.bootstrap.Row = function(config){
4920 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4923 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4925 getAutoCreate : function(){
4944 * @class Roo.bootstrap.Element
4945 * @extends Roo.bootstrap.Component
4946 * Bootstrap Element class
4947 * @cfg {String} html contents of the element
4948 * @cfg {String} tag tag of the element
4949 * @cfg {String} cls class of the element
4950 * @cfg {Boolean} preventDefault (true|false) default false
4951 * @cfg {Boolean} clickable (true|false) default false
4954 * Create a new Element
4955 * @param {Object} config The config object
4958 Roo.bootstrap.Element = function(config){
4959 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4965 * When a element is chick
4966 * @param {Roo.bootstrap.Element} this
4967 * @param {Roo.EventObject} e
4973 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4978 preventDefault: false,
4981 getAutoCreate : function(){
4985 // cls: this.cls, double assign in parent class Component.js :: onRender
4992 initEvents: function()
4994 Roo.bootstrap.Element.superclass.initEvents.call(this);
4997 this.el.on('click', this.onClick, this);
5002 onClick : function(e)
5004 if(this.preventDefault){
5008 this.fireEvent('click', this, e);
5011 getValue : function()
5013 return this.el.dom.innerHTML;
5016 setValue : function(value)
5018 this.el.dom.innerHTML = value;
5033 * @class Roo.bootstrap.Pagination
5034 * @extends Roo.bootstrap.Component
5035 * Bootstrap Pagination class
5036 * @cfg {String} size xs | sm | md | lg
5037 * @cfg {Boolean} inverse false | true
5040 * Create a new Pagination
5041 * @param {Object} config The config object
5044 Roo.bootstrap.Pagination = function(config){
5045 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5048 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5054 getAutoCreate : function(){
5060 cfg.cls += ' inverse';
5066 cfg.cls += " " + this.cls;
5084 * @class Roo.bootstrap.PaginationItem
5085 * @extends Roo.bootstrap.Component
5086 * Bootstrap PaginationItem class
5087 * @cfg {String} html text
5088 * @cfg {String} href the link
5089 * @cfg {Boolean} preventDefault (true | false) default true
5090 * @cfg {Boolean} active (true | false) default false
5091 * @cfg {Boolean} disabled default false
5095 * Create a new PaginationItem
5096 * @param {Object} config The config object
5100 Roo.bootstrap.PaginationItem = function(config){
5101 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5106 * The raw click event for the entire grid.
5107 * @param {Roo.EventObject} e
5113 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5117 preventDefault: true,
5122 getAutoCreate : function(){
5128 href : this.href ? this.href : '#',
5129 html : this.html ? this.html : ''
5139 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5143 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5149 initEvents: function() {
5151 this.el.on('click', this.onClick, this);
5154 onClick : function(e)
5156 Roo.log('PaginationItem on click ');
5157 if(this.preventDefault){
5165 this.fireEvent('click', this, e);
5181 * @class Roo.bootstrap.Slider
5182 * @extends Roo.bootstrap.Component
5183 * Bootstrap Slider class
5186 * Create a new Slider
5187 * @param {Object} config The config object
5190 Roo.bootstrap.Slider = function(config){
5191 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5194 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5196 getAutoCreate : function(){
5200 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5204 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5216 * Ext JS Library 1.1.1
5217 * Copyright(c) 2006-2007, Ext JS, LLC.
5219 * Originally Released Under LGPL - original licence link has changed is not relivant.
5222 * <script type="text/javascript">
5227 * @class Roo.grid.ColumnModel
5228 * @extends Roo.util.Observable
5229 * This is the default implementation of a ColumnModel used by the Grid. It defines
5230 * the columns in the grid.
5233 var colModel = new Roo.grid.ColumnModel([
5234 {header: "Ticker", width: 60, sortable: true, locked: true},
5235 {header: "Company Name", width: 150, sortable: true},
5236 {header: "Market Cap.", width: 100, sortable: true},
5237 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5238 {header: "Employees", width: 100, sortable: true, resizable: false}
5243 * The config options listed for this class are options which may appear in each
5244 * individual column definition.
5245 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5247 * @param {Object} config An Array of column config objects. See this class's
5248 * config objects for details.
5250 Roo.grid.ColumnModel = function(config){
5252 * The config passed into the constructor
5254 this.config = config;
5257 // if no id, create one
5258 // if the column does not have a dataIndex mapping,
5259 // map it to the order it is in the config
5260 for(var i = 0, len = config.length; i < len; i++){
5262 if(typeof c.dataIndex == "undefined"){
5265 if(typeof c.renderer == "string"){
5266 c.renderer = Roo.util.Format[c.renderer];
5268 if(typeof c.id == "undefined"){
5271 if(c.editor && c.editor.xtype){
5272 c.editor = Roo.factory(c.editor, Roo.grid);
5274 if(c.editor && c.editor.isFormField){
5275 c.editor = new Roo.grid.GridEditor(c.editor);
5277 this.lookup[c.id] = c;
5281 * The width of columns which have no width specified (defaults to 100)
5284 this.defaultWidth = 100;
5287 * Default sortable of columns which have no sortable specified (defaults to false)
5290 this.defaultSortable = false;
5294 * @event widthchange
5295 * Fires when the width of a column changes.
5296 * @param {ColumnModel} this
5297 * @param {Number} columnIndex The column index
5298 * @param {Number} newWidth The new width
5300 "widthchange": true,
5302 * @event headerchange
5303 * Fires when the text of a header changes.
5304 * @param {ColumnModel} this
5305 * @param {Number} columnIndex The column index
5306 * @param {Number} newText The new header text
5308 "headerchange": true,
5310 * @event hiddenchange
5311 * Fires when a column is hidden or "unhidden".
5312 * @param {ColumnModel} this
5313 * @param {Number} columnIndex The column index
5314 * @param {Boolean} hidden true if hidden, false otherwise
5316 "hiddenchange": true,
5318 * @event columnmoved
5319 * Fires when a column is moved.
5320 * @param {ColumnModel} this
5321 * @param {Number} oldIndex
5322 * @param {Number} newIndex
5324 "columnmoved" : true,
5326 * @event columlockchange
5327 * Fires when a column's locked state is changed
5328 * @param {ColumnModel} this
5329 * @param {Number} colIndex
5330 * @param {Boolean} locked true if locked
5332 "columnlockchange" : true
5334 Roo.grid.ColumnModel.superclass.constructor.call(this);
5336 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5338 * @cfg {String} header The header text to display in the Grid view.
5341 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5342 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5343 * specified, the column's index is used as an index into the Record's data Array.
5346 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5347 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5350 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5351 * Defaults to the value of the {@link #defaultSortable} property.
5352 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5355 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5358 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5361 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5364 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5367 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5368 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5369 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5370 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5373 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5376 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5379 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5382 * @cfg {String} cursor (Optional)
5385 * @cfg {String} tooltip (Optional)
5388 * @cfg {Number} xs (Optional)
5391 * @cfg {Number} sm (Optional)
5394 * @cfg {Number} md (Optional)
5397 * @cfg {Number} lg (Optional)
5400 * Returns the id of the column at the specified index.
5401 * @param {Number} index The column index
5402 * @return {String} the id
5404 getColumnId : function(index){
5405 return this.config[index].id;
5409 * Returns the column for a specified id.
5410 * @param {String} id The column id
5411 * @return {Object} the column
5413 getColumnById : function(id){
5414 return this.lookup[id];
5419 * Returns the column for a specified dataIndex.
5420 * @param {String} dataIndex The column dataIndex
5421 * @return {Object|Boolean} the column or false if not found
5423 getColumnByDataIndex: function(dataIndex){
5424 var index = this.findColumnIndex(dataIndex);
5425 return index > -1 ? this.config[index] : false;
5429 * Returns the index for a specified column id.
5430 * @param {String} id The column id
5431 * @return {Number} the index, or -1 if not found
5433 getIndexById : function(id){
5434 for(var i = 0, len = this.config.length; i < len; i++){
5435 if(this.config[i].id == id){
5443 * Returns the index for a specified column dataIndex.
5444 * @param {String} dataIndex The column dataIndex
5445 * @return {Number} the index, or -1 if not found
5448 findColumnIndex : function(dataIndex){
5449 for(var i = 0, len = this.config.length; i < len; i++){
5450 if(this.config[i].dataIndex == dataIndex){
5458 moveColumn : function(oldIndex, newIndex){
5459 var c = this.config[oldIndex];
5460 this.config.splice(oldIndex, 1);
5461 this.config.splice(newIndex, 0, c);
5462 this.dataMap = null;
5463 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5466 isLocked : function(colIndex){
5467 return this.config[colIndex].locked === true;
5470 setLocked : function(colIndex, value, suppressEvent){
5471 if(this.isLocked(colIndex) == value){
5474 this.config[colIndex].locked = value;
5476 this.fireEvent("columnlockchange", this, colIndex, value);
5480 getTotalLockedWidth : function(){
5482 for(var i = 0; i < this.config.length; i++){
5483 if(this.isLocked(i) && !this.isHidden(i)){
5484 this.totalWidth += this.getColumnWidth(i);
5490 getLockedCount : function(){
5491 for(var i = 0, len = this.config.length; i < len; i++){
5492 if(!this.isLocked(i)){
5497 return this.config.length;
5501 * Returns the number of columns.
5504 getColumnCount : function(visibleOnly){
5505 if(visibleOnly === true){
5507 for(var i = 0, len = this.config.length; i < len; i++){
5508 if(!this.isHidden(i)){
5514 return this.config.length;
5518 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5519 * @param {Function} fn
5520 * @param {Object} scope (optional)
5521 * @return {Array} result
5523 getColumnsBy : function(fn, scope){
5525 for(var i = 0, len = this.config.length; i < len; i++){
5526 var c = this.config[i];
5527 if(fn.call(scope||this, c, i) === true){
5535 * Returns true if the specified column is sortable.
5536 * @param {Number} col The column index
5539 isSortable : function(col){
5540 if(typeof this.config[col].sortable == "undefined"){
5541 return this.defaultSortable;
5543 return this.config[col].sortable;
5547 * Returns the rendering (formatting) function defined for the column.
5548 * @param {Number} col The column index.
5549 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5551 getRenderer : function(col){
5552 if(!this.config[col].renderer){
5553 return Roo.grid.ColumnModel.defaultRenderer;
5555 return this.config[col].renderer;
5559 * Sets the rendering (formatting) function for a column.
5560 * @param {Number} col The column index
5561 * @param {Function} fn The function to use to process the cell's raw data
5562 * to return HTML markup for the grid view. The render function is called with
5563 * the following parameters:<ul>
5564 * <li>Data value.</li>
5565 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5566 * <li>css A CSS style string to apply to the table cell.</li>
5567 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5568 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5569 * <li>Row index</li>
5570 * <li>Column index</li>
5571 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5573 setRenderer : function(col, fn){
5574 this.config[col].renderer = fn;
5578 * Returns the width for the specified column.
5579 * @param {Number} col The column index
5582 getColumnWidth : function(col){
5583 return this.config[col].width * 1 || this.defaultWidth;
5587 * Sets the width for a column.
5588 * @param {Number} col The column index
5589 * @param {Number} width The new width
5591 setColumnWidth : function(col, width, suppressEvent){
5592 this.config[col].width = width;
5593 this.totalWidth = null;
5595 this.fireEvent("widthchange", this, col, width);
5600 * Returns the total width of all columns.
5601 * @param {Boolean} includeHidden True to include hidden column widths
5604 getTotalWidth : function(includeHidden){
5605 if(!this.totalWidth){
5606 this.totalWidth = 0;
5607 for(var i = 0, len = this.config.length; i < len; i++){
5608 if(includeHidden || !this.isHidden(i)){
5609 this.totalWidth += this.getColumnWidth(i);
5613 return this.totalWidth;
5617 * Returns the header for the specified column.
5618 * @param {Number} col The column index
5621 getColumnHeader : function(col){
5622 return this.config[col].header;
5626 * Sets the header for a column.
5627 * @param {Number} col The column index
5628 * @param {String} header The new header
5630 setColumnHeader : function(col, header){
5631 this.config[col].header = header;
5632 this.fireEvent("headerchange", this, col, header);
5636 * Returns the tooltip for the specified column.
5637 * @param {Number} col The column index
5640 getColumnTooltip : function(col){
5641 return this.config[col].tooltip;
5644 * Sets the tooltip for a column.
5645 * @param {Number} col The column index
5646 * @param {String} tooltip The new tooltip
5648 setColumnTooltip : function(col, tooltip){
5649 this.config[col].tooltip = tooltip;
5653 * Returns the dataIndex for the specified column.
5654 * @param {Number} col The column index
5657 getDataIndex : function(col){
5658 return this.config[col].dataIndex;
5662 * Sets the dataIndex for a column.
5663 * @param {Number} col The column index
5664 * @param {Number} dataIndex The new dataIndex
5666 setDataIndex : function(col, dataIndex){
5667 this.config[col].dataIndex = dataIndex;
5673 * Returns true if the cell is editable.
5674 * @param {Number} colIndex The column index
5675 * @param {Number} rowIndex The row index - this is nto actually used..?
5678 isCellEditable : function(colIndex, rowIndex){
5679 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5683 * Returns the editor defined for the cell/column.
5684 * return false or null to disable editing.
5685 * @param {Number} colIndex The column index
5686 * @param {Number} rowIndex The row index
5689 getCellEditor : function(colIndex, rowIndex){
5690 return this.config[colIndex].editor;
5694 * Sets if a column is editable.
5695 * @param {Number} col The column index
5696 * @param {Boolean} editable True if the column is editable
5698 setEditable : function(col, editable){
5699 this.config[col].editable = editable;
5704 * Returns true if the column is hidden.
5705 * @param {Number} colIndex The column index
5708 isHidden : function(colIndex){
5709 return this.config[colIndex].hidden;
5714 * Returns true if the column width cannot be changed
5716 isFixed : function(colIndex){
5717 return this.config[colIndex].fixed;
5721 * Returns true if the column can be resized
5724 isResizable : function(colIndex){
5725 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5728 * Sets if a column is hidden.
5729 * @param {Number} colIndex The column index
5730 * @param {Boolean} hidden True if the column is hidden
5732 setHidden : function(colIndex, hidden){
5733 this.config[colIndex].hidden = hidden;
5734 this.totalWidth = null;
5735 this.fireEvent("hiddenchange", this, colIndex, hidden);
5739 * Sets the editor for a column.
5740 * @param {Number} col The column index
5741 * @param {Object} editor The editor object
5743 setEditor : function(col, editor){
5744 this.config[col].editor = editor;
5748 Roo.grid.ColumnModel.defaultRenderer = function(value)
5750 if(typeof value == "object") {
5753 if(typeof value == "string" && value.length < 1){
5757 return String.format("{0}", value);
5760 // Alias for backwards compatibility
5761 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5764 * Ext JS Library 1.1.1
5765 * Copyright(c) 2006-2007, Ext JS, LLC.
5767 * Originally Released Under LGPL - original licence link has changed is not relivant.
5770 * <script type="text/javascript">
5774 * @class Roo.LoadMask
5775 * A simple utility class for generically masking elements while loading data. If the element being masked has
5776 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5777 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5778 * element's UpdateManager load indicator and will be destroyed after the initial load.
5780 * Create a new LoadMask
5781 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5782 * @param {Object} config The config object
5784 Roo.LoadMask = function(el, config){
5785 this.el = Roo.get(el);
5786 Roo.apply(this, config);
5788 this.store.on('beforeload', this.onBeforeLoad, this);
5789 this.store.on('load', this.onLoad, this);
5790 this.store.on('loadexception', this.onLoadException, this);
5791 this.removeMask = false;
5793 var um = this.el.getUpdateManager();
5794 um.showLoadIndicator = false; // disable the default indicator
5795 um.on('beforeupdate', this.onBeforeLoad, this);
5796 um.on('update', this.onLoad, this);
5797 um.on('failure', this.onLoad, this);
5798 this.removeMask = true;
5802 Roo.LoadMask.prototype = {
5804 * @cfg {Boolean} removeMask
5805 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5806 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5810 * The text to display in a centered loading message box (defaults to 'Loading...')
5814 * @cfg {String} msgCls
5815 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5817 msgCls : 'x-mask-loading',
5820 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5826 * Disables the mask to prevent it from being displayed
5828 disable : function(){
5829 this.disabled = true;
5833 * Enables the mask so that it can be displayed
5835 enable : function(){
5836 this.disabled = false;
5839 onLoadException : function()
5843 if (typeof(arguments[3]) != 'undefined') {
5844 Roo.MessageBox.alert("Error loading",arguments[3]);
5848 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5849 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5856 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5861 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5865 onBeforeLoad : function(){
5867 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5872 destroy : function(){
5874 this.store.un('beforeload', this.onBeforeLoad, this);
5875 this.store.un('load', this.onLoad, this);
5876 this.store.un('loadexception', this.onLoadException, this);
5878 var um = this.el.getUpdateManager();
5879 um.un('beforeupdate', this.onBeforeLoad, this);
5880 um.un('update', this.onLoad, this);
5881 um.un('failure', this.onLoad, this);
5892 * @class Roo.bootstrap.Table
5893 * @extends Roo.bootstrap.Component
5894 * Bootstrap Table class
5895 * @cfg {String} cls table class
5896 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5897 * @cfg {String} bgcolor Specifies the background color for a table
5898 * @cfg {Number} border Specifies whether the table cells should have borders or not
5899 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5900 * @cfg {Number} cellspacing Specifies the space between cells
5901 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5902 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5903 * @cfg {String} sortable Specifies that the table should be sortable
5904 * @cfg {String} summary Specifies a summary of the content of a table
5905 * @cfg {Number} width Specifies the width of a table
5906 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5908 * @cfg {boolean} striped Should the rows be alternative striped
5909 * @cfg {boolean} bordered Add borders to the table
5910 * @cfg {boolean} hover Add hover highlighting
5911 * @cfg {boolean} condensed Format condensed
5912 * @cfg {boolean} responsive Format condensed
5913 * @cfg {Boolean} loadMask (true|false) default false
5914 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5915 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5916 * @cfg {Boolean} rowSelection (true|false) default false
5917 * @cfg {Boolean} cellSelection (true|false) default false
5918 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5919 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5920 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5921 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5925 * Create a new Table
5926 * @param {Object} config The config object
5929 Roo.bootstrap.Table = function(config){
5930 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5935 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5936 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5937 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5938 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5940 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5942 this.sm.grid = this;
5943 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5944 this.sm = this.selModel;
5945 this.sm.xmodule = this.xmodule || false;
5948 if (this.cm && typeof(this.cm.config) == 'undefined') {
5949 this.colModel = new Roo.grid.ColumnModel(this.cm);
5950 this.cm = this.colModel;
5951 this.cm.xmodule = this.xmodule || false;
5954 this.store= Roo.factory(this.store, Roo.data);
5955 this.ds = this.store;
5956 this.ds.xmodule = this.xmodule || false;
5959 if (this.footer && this.store) {
5960 this.footer.dataSource = this.ds;
5961 this.footer = Roo.factory(this.footer);
5968 * Fires when a cell is clicked
5969 * @param {Roo.bootstrap.Table} this
5970 * @param {Roo.Element} el
5971 * @param {Number} rowIndex
5972 * @param {Number} columnIndex
5973 * @param {Roo.EventObject} e
5977 * @event celldblclick
5978 * Fires when a cell is double clicked
5979 * @param {Roo.bootstrap.Table} this
5980 * @param {Roo.Element} el
5981 * @param {Number} rowIndex
5982 * @param {Number} columnIndex
5983 * @param {Roo.EventObject} e
5985 "celldblclick" : true,
5988 * Fires when a row is clicked
5989 * @param {Roo.bootstrap.Table} this
5990 * @param {Roo.Element} el
5991 * @param {Number} rowIndex
5992 * @param {Roo.EventObject} e
5996 * @event rowdblclick
5997 * Fires when a row is double clicked
5998 * @param {Roo.bootstrap.Table} this
5999 * @param {Roo.Element} el
6000 * @param {Number} rowIndex
6001 * @param {Roo.EventObject} e
6003 "rowdblclick" : true,
6006 * Fires when a mouseover occur
6007 * @param {Roo.bootstrap.Table} this
6008 * @param {Roo.Element} el
6009 * @param {Number} rowIndex
6010 * @param {Number} columnIndex
6011 * @param {Roo.EventObject} e
6016 * Fires when a mouseout occur
6017 * @param {Roo.bootstrap.Table} this
6018 * @param {Roo.Element} el
6019 * @param {Number} rowIndex
6020 * @param {Number} columnIndex
6021 * @param {Roo.EventObject} e
6026 * Fires when a row is rendered, so you can change add a style to it.
6027 * @param {Roo.bootstrap.Table} this
6028 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6032 * @event rowsrendered
6033 * Fires when all the rows have been rendered
6034 * @param {Roo.bootstrap.Table} this
6036 'rowsrendered' : true,
6038 * @event contextmenu
6039 * The raw contextmenu event for the entire grid.
6040 * @param {Roo.EventObject} e
6042 "contextmenu" : true,
6044 * @event rowcontextmenu
6045 * Fires when a row is right clicked
6046 * @param {Roo.bootstrap.Table} this
6047 * @param {Number} rowIndex
6048 * @param {Roo.EventObject} e
6050 "rowcontextmenu" : true,
6052 * @event cellcontextmenu
6053 * Fires when a cell is right clicked
6054 * @param {Roo.bootstrap.Table} this
6055 * @param {Number} rowIndex
6056 * @param {Number} cellIndex
6057 * @param {Roo.EventObject} e
6059 "cellcontextmenu" : true,
6061 * @event headercontextmenu
6062 * Fires when a header is right clicked
6063 * @param {Roo.bootstrap.Table} this
6064 * @param {Number} columnIndex
6065 * @param {Roo.EventObject} e
6067 "headercontextmenu" : true
6071 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6097 rowSelection : false,
6098 cellSelection : false,
6101 // Roo.Element - the tbody
6103 // Roo.Element - thead element
6106 container: false, // used by gridpanel...
6112 auto_hide_footer : false,
6114 getAutoCreate : function()
6116 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6123 if (this.scrollBody) {
6124 cfg.cls += ' table-body-fixed';
6127 cfg.cls += ' table-striped';
6131 cfg.cls += ' table-hover';
6133 if (this.bordered) {
6134 cfg.cls += ' table-bordered';
6136 if (this.condensed) {
6137 cfg.cls += ' table-condensed';
6139 if (this.responsive) {
6140 cfg.cls += ' table-responsive';
6144 cfg.cls+= ' ' +this.cls;
6147 // this lot should be simplifed...
6160 ].forEach(function(k) {
6168 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6171 if(this.store || this.cm){
6172 if(this.headerShow){
6173 cfg.cn.push(this.renderHeader());
6176 cfg.cn.push(this.renderBody());
6178 if(this.footerShow){
6179 cfg.cn.push(this.renderFooter());
6181 // where does this come from?
6182 //cfg.cls+= ' TableGrid';
6185 return { cn : [ cfg ] };
6188 initEvents : function()
6190 if(!this.store || !this.cm){
6193 if (this.selModel) {
6194 this.selModel.initEvents();
6198 //Roo.log('initEvents with ds!!!!');
6200 this.mainBody = this.el.select('tbody', true).first();
6201 this.mainHead = this.el.select('thead', true).first();
6202 this.mainFoot = this.el.select('tfoot', true).first();
6208 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6209 e.on('click', _this.sort, _this);
6212 this.mainBody.on("click", this.onClick, this);
6213 this.mainBody.on("dblclick", this.onDblClick, this);
6215 // why is this done????? = it breaks dialogs??
6216 //this.parent().el.setStyle('position', 'relative');
6220 this.footer.parentId = this.id;
6221 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6224 this.el.select('tfoot tr td').first().addClass('hide');
6229 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6232 this.store.on('load', this.onLoad, this);
6233 this.store.on('beforeload', this.onBeforeLoad, this);
6234 this.store.on('update', this.onUpdate, this);
6235 this.store.on('add', this.onAdd, this);
6236 this.store.on("clear", this.clear, this);
6238 this.el.on("contextmenu", this.onContextMenu, this);
6240 this.mainBody.on('scroll', this.onBodyScroll, this);
6242 this.cm.on("headerchange", this.onHeaderChange, this);
6244 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6248 onContextMenu : function(e, t)
6250 this.processEvent("contextmenu", e);
6253 processEvent : function(name, e)
6255 if (name != 'touchstart' ) {
6256 this.fireEvent(name, e);
6259 var t = e.getTarget();
6261 var cell = Roo.get(t);
6267 if(cell.findParent('tfoot', false, true)){
6271 if(cell.findParent('thead', false, true)){
6273 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6274 cell = Roo.get(t).findParent('th', false, true);
6276 Roo.log("failed to find th in thead?");
6277 Roo.log(e.getTarget());
6282 var cellIndex = cell.dom.cellIndex;
6284 var ename = name == 'touchstart' ? 'click' : name;
6285 this.fireEvent("header" + ename, this, cellIndex, e);
6290 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291 cell = Roo.get(t).findParent('td', false, true);
6293 Roo.log("failed to find th in tbody?");
6294 Roo.log(e.getTarget());
6299 var row = cell.findParent('tr', false, true);
6300 var cellIndex = cell.dom.cellIndex;
6301 var rowIndex = row.dom.rowIndex - 1;
6305 this.fireEvent("row" + name, this, rowIndex, e);
6309 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6315 onMouseover : function(e, el)
6317 var cell = Roo.get(el);
6323 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6324 cell = cell.findParent('td', false, true);
6327 var row = cell.findParent('tr', false, true);
6328 var cellIndex = cell.dom.cellIndex;
6329 var rowIndex = row.dom.rowIndex - 1; // start from 0
6331 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6335 onMouseout : function(e, el)
6337 var cell = Roo.get(el);
6343 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6344 cell = cell.findParent('td', false, true);
6347 var row = cell.findParent('tr', false, true);
6348 var cellIndex = cell.dom.cellIndex;
6349 var rowIndex = row.dom.rowIndex - 1; // start from 0
6351 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6355 onClick : function(e, el)
6357 var cell = Roo.get(el);
6359 if(!cell || (!this.cellSelection && !this.rowSelection)){
6363 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6364 cell = cell.findParent('td', false, true);
6367 if(!cell || typeof(cell) == 'undefined'){
6371 var row = cell.findParent('tr', false, true);
6373 if(!row || typeof(row) == 'undefined'){
6377 var cellIndex = cell.dom.cellIndex;
6378 var rowIndex = this.getRowIndex(row);
6380 // why??? - should these not be based on SelectionModel?
6381 if(this.cellSelection){
6382 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6385 if(this.rowSelection){
6386 this.fireEvent('rowclick', this, row, rowIndex, e);
6392 onDblClick : function(e,el)
6394 var cell = Roo.get(el);
6396 if(!cell || (!this.cellSelection && !this.rowSelection)){
6400 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6401 cell = cell.findParent('td', false, true);
6404 if(!cell || typeof(cell) == 'undefined'){
6408 var row = cell.findParent('tr', false, true);
6410 if(!row || typeof(row) == 'undefined'){
6414 var cellIndex = cell.dom.cellIndex;
6415 var rowIndex = this.getRowIndex(row);
6417 if(this.cellSelection){
6418 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6421 if(this.rowSelection){
6422 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6426 sort : function(e,el)
6428 var col = Roo.get(el);
6430 if(!col.hasClass('sortable')){
6434 var sort = col.attr('sort');
6437 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6441 this.store.sortInfo = {field : sort, direction : dir};
6444 Roo.log("calling footer first");
6445 this.footer.onClick('first');
6448 this.store.load({ params : { start : 0 } });
6452 renderHeader : function()
6460 this.totalWidth = 0;
6462 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6464 var config = cm.config[i];
6468 cls : 'x-hcol-' + i,
6470 html: cm.getColumnHeader(i)
6475 if(typeof(config.sortable) != 'undefined' && config.sortable){
6477 c.html = '<i class="glyphicon"></i>' + c.html;
6480 if(typeof(config.lgHeader) != 'undefined'){
6481 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6484 if(typeof(config.mdHeader) != 'undefined'){
6485 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6488 if(typeof(config.smHeader) != 'undefined'){
6489 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6492 if(typeof(config.xsHeader) != 'undefined'){
6493 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6500 if(typeof(config.tooltip) != 'undefined'){
6501 c.tooltip = config.tooltip;
6504 if(typeof(config.colspan) != 'undefined'){
6505 c.colspan = config.colspan;
6508 if(typeof(config.hidden) != 'undefined' && config.hidden){
6509 c.style += ' display:none;';
6512 if(typeof(config.dataIndex) != 'undefined'){
6513 c.sort = config.dataIndex;
6518 if(typeof(config.align) != 'undefined' && config.align.length){
6519 c.style += ' text-align:' + config.align + ';';
6522 if(typeof(config.width) != 'undefined'){
6523 c.style += ' width:' + config.width + 'px;';
6524 this.totalWidth += config.width;
6526 this.totalWidth += 100; // assume minimum of 100 per column?
6529 if(typeof(config.cls) != 'undefined'){
6530 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6533 ['xs','sm','md','lg'].map(function(size){
6535 if(typeof(config[size]) == 'undefined'){
6539 if (!config[size]) { // 0 = hidden
6540 c.cls += ' hidden-' + size;
6544 c.cls += ' col-' + size + '-' + config[size];
6554 renderBody : function()
6564 colspan : this.cm.getColumnCount()
6574 renderFooter : function()
6584 colspan : this.cm.getColumnCount()
6598 // Roo.log('ds onload');
6603 var ds = this.store;
6605 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6606 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6607 if (_this.store.sortInfo) {
6609 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6610 e.select('i', true).addClass(['glyphicon-arrow-up']);
6613 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6614 e.select('i', true).addClass(['glyphicon-arrow-down']);
6619 var tbody = this.mainBody;
6621 if(ds.getCount() > 0){
6622 ds.data.each(function(d,rowIndex){
6623 var row = this.renderRow(cm, ds, rowIndex);
6625 tbody.createChild(row);
6629 if(row.cellObjects.length){
6630 Roo.each(row.cellObjects, function(r){
6631 _this.renderCellObject(r);
6638 var tfoot = this.el.select('tfoot', true).first();
6640 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6642 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6644 var total = this.ds.getTotalCount();
6646 if(this.footer.pageSize < total){
6647 this.mainFoot.show();
6651 Roo.each(this.el.select('tbody td', true).elements, function(e){
6652 e.on('mouseover', _this.onMouseover, _this);
6655 Roo.each(this.el.select('tbody td', true).elements, function(e){
6656 e.on('mouseout', _this.onMouseout, _this);
6658 this.fireEvent('rowsrendered', this);
6664 onUpdate : function(ds,record)
6666 this.refreshRow(record);
6670 onRemove : function(ds, record, index, isUpdate){
6671 if(isUpdate !== true){
6672 this.fireEvent("beforerowremoved", this, index, record);
6674 var bt = this.mainBody.dom;
6676 var rows = this.el.select('tbody > tr', true).elements;
6678 if(typeof(rows[index]) != 'undefined'){
6679 bt.removeChild(rows[index].dom);
6682 // if(bt.rows[index]){
6683 // bt.removeChild(bt.rows[index]);
6686 if(isUpdate !== true){
6687 //this.stripeRows(index);
6688 //this.syncRowHeights(index, index);
6690 this.fireEvent("rowremoved", this, index, record);
6694 onAdd : function(ds, records, rowIndex)
6696 //Roo.log('on Add called');
6697 // - note this does not handle multiple adding very well..
6698 var bt = this.mainBody.dom;
6699 for (var i =0 ; i < records.length;i++) {
6700 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6701 //Roo.log(records[i]);
6702 //Roo.log(this.store.getAt(rowIndex+i));
6703 this.insertRow(this.store, rowIndex + i, false);
6710 refreshRow : function(record){
6711 var ds = this.store, index;
6712 if(typeof record == 'number'){
6714 record = ds.getAt(index);
6716 index = ds.indexOf(record);
6718 this.insertRow(ds, index, true);
6720 this.onRemove(ds, record, index+1, true);
6722 //this.syncRowHeights(index, index);
6724 this.fireEvent("rowupdated", this, index, record);
6727 insertRow : function(dm, rowIndex, isUpdate){
6730 this.fireEvent("beforerowsinserted", this, rowIndex);
6732 //var s = this.getScrollState();
6733 var row = this.renderRow(this.cm, this.store, rowIndex);
6734 // insert before rowIndex..
6735 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6739 if(row.cellObjects.length){
6740 Roo.each(row.cellObjects, function(r){
6741 _this.renderCellObject(r);
6746 this.fireEvent("rowsinserted", this, rowIndex);
6747 //this.syncRowHeights(firstRow, lastRow);
6748 //this.stripeRows(firstRow);
6755 getRowDom : function(rowIndex)
6757 var rows = this.el.select('tbody > tr', true).elements;
6759 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6762 // returns the object tree for a tr..
6765 renderRow : function(cm, ds, rowIndex)
6767 var d = ds.getAt(rowIndex);
6771 cls : 'x-row-' + rowIndex,
6775 var cellObjects = [];
6777 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6778 var config = cm.config[i];
6780 var renderer = cm.getRenderer(i);
6784 if(typeof(renderer) !== 'undefined'){
6785 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6787 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6788 // and are rendered into the cells after the row is rendered - using the id for the element.
6790 if(typeof(value) === 'object'){
6800 rowIndex : rowIndex,
6805 this.fireEvent('rowclass', this, rowcfg);
6809 cls : rowcfg.rowClass + ' x-col-' + i,
6811 html: (typeof(value) === 'object') ? '' : value
6818 if(typeof(config.colspan) != 'undefined'){
6819 td.colspan = config.colspan;
6822 if(typeof(config.hidden) != 'undefined' && config.hidden){
6823 td.style += ' display:none;';
6826 if(typeof(config.align) != 'undefined' && config.align.length){
6827 td.style += ' text-align:' + config.align + ';';
6829 if(typeof(config.valign) != 'undefined' && config.valign.length){
6830 td.style += ' vertical-align:' + config.valign + ';';
6833 if(typeof(config.width) != 'undefined'){
6834 td.style += ' width:' + config.width + 'px;';
6837 if(typeof(config.cursor) != 'undefined'){
6838 td.style += ' cursor:' + config.cursor + ';';
6841 if(typeof(config.cls) != 'undefined'){
6842 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6845 ['xs','sm','md','lg'].map(function(size){
6847 if(typeof(config[size]) == 'undefined'){
6851 if (!config[size]) { // 0 = hidden
6852 td.cls += ' hidden-' + size;
6856 td.cls += ' col-' + size + '-' + config[size];
6864 row.cellObjects = cellObjects;
6872 onBeforeLoad : function()
6881 this.el.select('tbody', true).first().dom.innerHTML = '';
6884 * Show or hide a row.
6885 * @param {Number} rowIndex to show or hide
6886 * @param {Boolean} state hide
6888 setRowVisibility : function(rowIndex, state)
6890 var bt = this.mainBody.dom;
6892 var rows = this.el.select('tbody > tr', true).elements;
6894 if(typeof(rows[rowIndex]) == 'undefined'){
6897 rows[rowIndex].dom.style.display = state ? '' : 'none';
6901 getSelectionModel : function(){
6903 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6905 return this.selModel;
6908 * Render the Roo.bootstrap object from renderder
6910 renderCellObject : function(r)
6914 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6916 var t = r.cfg.render(r.container);
6919 Roo.each(r.cfg.cn, function(c){
6921 container: t.getChildContainer(),
6924 _this.renderCellObject(child);
6929 getRowIndex : function(row)
6933 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6944 * Returns the grid's underlying element = used by panel.Grid
6945 * @return {Element} The element
6947 getGridEl : function(){
6951 * Forces a resize - used by panel.Grid
6952 * @return {Element} The element
6954 autoSize : function()
6956 //var ctr = Roo.get(this.container.dom.parentElement);
6957 var ctr = Roo.get(this.el.dom);
6959 var thd = this.getGridEl().select('thead',true).first();
6960 var tbd = this.getGridEl().select('tbody', true).first();
6961 var tfd = this.getGridEl().select('tfoot', true).first();
6963 var cw = ctr.getWidth();
6967 tbd.setSize(ctr.getWidth(),
6968 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6970 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6973 cw = Math.max(cw, this.totalWidth);
6974 this.getGridEl().select('tr',true).setWidth(cw);
6975 // resize 'expandable coloumn?
6977 return; // we doe not have a view in this design..
6980 onBodyScroll: function()
6982 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6984 this.mainHead.setStyle({
6985 'position' : 'relative',
6986 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6992 var scrollHeight = this.mainBody.dom.scrollHeight;
6994 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6996 var height = this.mainBody.getHeight();
6998 if(scrollHeight - height == scrollTop) {
7000 var total = this.ds.getTotalCount();
7002 if(this.footer.cursor + this.footer.pageSize < total){
7004 this.footer.ds.load({
7006 start : this.footer.cursor + this.footer.pageSize,
7007 limit : this.footer.pageSize
7017 onHeaderChange : function()
7019 var header = this.renderHeader();
7020 var table = this.el.select('table', true).first();
7022 this.mainHead.remove();
7023 this.mainHead = table.createChild(header, this.mainBody, false);
7026 onHiddenChange : function(colModel, colIndex, hidden)
7028 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7029 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7031 this.CSS.updateRule(thSelector, "display", "");
7032 this.CSS.updateRule(tdSelector, "display", "");
7035 this.CSS.updateRule(thSelector, "display", "none");
7036 this.CSS.updateRule(tdSelector, "display", "none");
7039 this.onHeaderChange();
7056 * @class Roo.bootstrap.TableCell
7057 * @extends Roo.bootstrap.Component
7058 * Bootstrap TableCell class
7059 * @cfg {String} html cell contain text
7060 * @cfg {String} cls cell class
7061 * @cfg {String} tag cell tag (td|th) default td
7062 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7063 * @cfg {String} align Aligns the content in a cell
7064 * @cfg {String} axis Categorizes cells
7065 * @cfg {String} bgcolor Specifies the background color of a cell
7066 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7067 * @cfg {Number} colspan Specifies the number of columns a cell should span
7068 * @cfg {String} headers Specifies one or more header cells a cell is related to
7069 * @cfg {Number} height Sets the height of a cell
7070 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7071 * @cfg {Number} rowspan Sets the number of rows a cell should span
7072 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7073 * @cfg {String} valign Vertical aligns the content in a cell
7074 * @cfg {Number} width Specifies the width of a cell
7077 * Create a new TableCell
7078 * @param {Object} config The config object
7081 Roo.bootstrap.TableCell = function(config){
7082 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7085 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7105 getAutoCreate : function(){
7106 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7126 cfg.align=this.align
7132 cfg.bgcolor=this.bgcolor
7135 cfg.charoff=this.charoff
7138 cfg.colspan=this.colspan
7141 cfg.headers=this.headers
7144 cfg.height=this.height
7147 cfg.nowrap=this.nowrap
7150 cfg.rowspan=this.rowspan
7153 cfg.scope=this.scope
7156 cfg.valign=this.valign
7159 cfg.width=this.width
7178 * @class Roo.bootstrap.TableRow
7179 * @extends Roo.bootstrap.Component
7180 * Bootstrap TableRow class
7181 * @cfg {String} cls row class
7182 * @cfg {String} align Aligns the content in a table row
7183 * @cfg {String} bgcolor Specifies a background color for a table row
7184 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7185 * @cfg {String} valign Vertical aligns the content in a table row
7188 * Create a new TableRow
7189 * @param {Object} config The config object
7192 Roo.bootstrap.TableRow = function(config){
7193 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7196 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7204 getAutoCreate : function(){
7205 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7215 cfg.align = this.align;
7218 cfg.bgcolor = this.bgcolor;
7221 cfg.charoff = this.charoff;
7224 cfg.valign = this.valign;
7242 * @class Roo.bootstrap.TableBody
7243 * @extends Roo.bootstrap.Component
7244 * Bootstrap TableBody class
7245 * @cfg {String} cls element class
7246 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7247 * @cfg {String} align Aligns the content inside the element
7248 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7249 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7252 * Create a new TableBody
7253 * @param {Object} config The config object
7256 Roo.bootstrap.TableBody = function(config){
7257 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7260 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7268 getAutoCreate : function(){
7269 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7283 cfg.align = this.align;
7286 cfg.charoff = this.charoff;
7289 cfg.valign = this.valign;
7296 // initEvents : function()
7303 // this.store = Roo.factory(this.store, Roo.data);
7304 // this.store.on('load', this.onLoad, this);
7306 // this.store.load();
7310 // onLoad: function ()
7312 // this.fireEvent('load', this);
7322 * Ext JS Library 1.1.1
7323 * Copyright(c) 2006-2007, Ext JS, LLC.
7325 * Originally Released Under LGPL - original licence link has changed is not relivant.
7328 * <script type="text/javascript">
7331 // as we use this in bootstrap.
7332 Roo.namespace('Roo.form');
7334 * @class Roo.form.Action
7335 * Internal Class used to handle form actions
7337 * @param {Roo.form.BasicForm} el The form element or its id
7338 * @param {Object} config Configuration options
7343 // define the action interface
7344 Roo.form.Action = function(form, options){
7346 this.options = options || {};
7349 * Client Validation Failed
7352 Roo.form.Action.CLIENT_INVALID = 'client';
7354 * Server Validation Failed
7357 Roo.form.Action.SERVER_INVALID = 'server';
7359 * Connect to Server Failed
7362 Roo.form.Action.CONNECT_FAILURE = 'connect';
7364 * Reading Data from Server Failed
7367 Roo.form.Action.LOAD_FAILURE = 'load';
7369 Roo.form.Action.prototype = {
7371 failureType : undefined,
7372 response : undefined,
7376 run : function(options){
7381 success : function(response){
7386 handleResponse : function(response){
7390 // default connection failure
7391 failure : function(response){
7393 this.response = response;
7394 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7395 this.form.afterAction(this, false);
7398 processResponse : function(response){
7399 this.response = response;
7400 if(!response.responseText){
7403 this.result = this.handleResponse(response);
7407 // utility functions used internally
7408 getUrl : function(appendParams){
7409 var url = this.options.url || this.form.url || this.form.el.dom.action;
7411 var p = this.getParams();
7413 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7419 getMethod : function(){
7420 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7423 getParams : function(){
7424 var bp = this.form.baseParams;
7425 var p = this.options.params;
7427 if(typeof p == "object"){
7428 p = Roo.urlEncode(Roo.applyIf(p, bp));
7429 }else if(typeof p == 'string' && bp){
7430 p += '&' + Roo.urlEncode(bp);
7433 p = Roo.urlEncode(bp);
7438 createCallback : function(){
7440 success: this.success,
7441 failure: this.failure,
7443 timeout: (this.form.timeout*1000),
7444 upload: this.form.fileUpload ? this.success : undefined
7449 Roo.form.Action.Submit = function(form, options){
7450 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7453 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7456 haveProgress : false,
7457 uploadComplete : false,
7459 // uploadProgress indicator.
7460 uploadProgress : function()
7462 if (!this.form.progressUrl) {
7466 if (!this.haveProgress) {
7467 Roo.MessageBox.progress("Uploading", "Uploading");
7469 if (this.uploadComplete) {
7470 Roo.MessageBox.hide();
7474 this.haveProgress = true;
7476 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7478 var c = new Roo.data.Connection();
7480 url : this.form.progressUrl,
7485 success : function(req){
7486 //console.log(data);
7490 rdata = Roo.decode(req.responseText)
7492 Roo.log("Invalid data from server..");
7496 if (!rdata || !rdata.success) {
7498 Roo.MessageBox.alert(Roo.encode(rdata));
7501 var data = rdata.data;
7503 if (this.uploadComplete) {
7504 Roo.MessageBox.hide();
7509 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7510 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7513 this.uploadProgress.defer(2000,this);
7516 failure: function(data) {
7517 Roo.log('progress url failed ');
7528 // run get Values on the form, so it syncs any secondary forms.
7529 this.form.getValues();
7531 var o = this.options;
7532 var method = this.getMethod();
7533 var isPost = method == 'POST';
7534 if(o.clientValidation === false || this.form.isValid()){
7536 if (this.form.progressUrl) {
7537 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7538 (new Date() * 1) + '' + Math.random());
7543 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7544 form:this.form.el.dom,
7545 url:this.getUrl(!isPost),
7547 params:isPost ? this.getParams() : null,
7548 isUpload: this.form.fileUpload
7551 this.uploadProgress();
7553 }else if (o.clientValidation !== false){ // client validation failed
7554 this.failureType = Roo.form.Action.CLIENT_INVALID;
7555 this.form.afterAction(this, false);
7559 success : function(response)
7561 this.uploadComplete= true;
7562 if (this.haveProgress) {
7563 Roo.MessageBox.hide();
7567 var result = this.processResponse(response);
7568 if(result === true || result.success){
7569 this.form.afterAction(this, true);
7573 this.form.markInvalid(result.errors);
7574 this.failureType = Roo.form.Action.SERVER_INVALID;
7576 this.form.afterAction(this, false);
7578 failure : function(response)
7580 this.uploadComplete= true;
7581 if (this.haveProgress) {
7582 Roo.MessageBox.hide();
7585 this.response = response;
7586 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7587 this.form.afterAction(this, false);
7590 handleResponse : function(response){
7591 if(this.form.errorReader){
7592 var rs = this.form.errorReader.read(response);
7595 for(var i = 0, len = rs.records.length; i < len; i++) {
7596 var r = rs.records[i];
7600 if(errors.length < 1){
7604 success : rs.success,
7610 ret = Roo.decode(response.responseText);
7614 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7624 Roo.form.Action.Load = function(form, options){
7625 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7626 this.reader = this.form.reader;
7629 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7634 Roo.Ajax.request(Roo.apply(
7635 this.createCallback(), {
7636 method:this.getMethod(),
7637 url:this.getUrl(false),
7638 params:this.getParams()
7642 success : function(response){
7644 var result = this.processResponse(response);
7645 if(result === true || !result.success || !result.data){
7646 this.failureType = Roo.form.Action.LOAD_FAILURE;
7647 this.form.afterAction(this, false);
7650 this.form.clearInvalid();
7651 this.form.setValues(result.data);
7652 this.form.afterAction(this, true);
7655 handleResponse : function(response){
7656 if(this.form.reader){
7657 var rs = this.form.reader.read(response);
7658 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7660 success : rs.success,
7664 return Roo.decode(response.responseText);
7668 Roo.form.Action.ACTION_TYPES = {
7669 'load' : Roo.form.Action.Load,
7670 'submit' : Roo.form.Action.Submit
7679 * @class Roo.bootstrap.Form
7680 * @extends Roo.bootstrap.Component
7681 * Bootstrap Form class
7682 * @cfg {String} method GET | POST (default POST)
7683 * @cfg {String} labelAlign top | left (default top)
7684 * @cfg {String} align left | right - for navbars
7685 * @cfg {Boolean} loadMask load mask when submit (default true)
7690 * @param {Object} config The config object
7694 Roo.bootstrap.Form = function(config){
7696 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7698 Roo.bootstrap.Form.popover.apply();
7702 * @event clientvalidation
7703 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7704 * @param {Form} this
7705 * @param {Boolean} valid true if the form has passed client-side validation
7707 clientvalidation: true,
7709 * @event beforeaction
7710 * Fires before any action is performed. Return false to cancel the action.
7711 * @param {Form} this
7712 * @param {Action} action The action to be performed
7716 * @event actionfailed
7717 * Fires when an action fails.
7718 * @param {Form} this
7719 * @param {Action} action The action that failed
7721 actionfailed : true,
7723 * @event actioncomplete
7724 * Fires when an action is completed.
7725 * @param {Form} this
7726 * @param {Action} action The action that completed
7728 actioncomplete : true
7732 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7735 * @cfg {String} method
7736 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7741 * The URL to use for form actions if one isn't supplied in the action options.
7744 * @cfg {Boolean} fileUpload
7745 * Set to true if this form is a file upload.
7749 * @cfg {Object} baseParams
7750 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7754 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7758 * @cfg {Sting} align (left|right) for navbar forms
7763 activeAction : null,
7766 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7767 * element by passing it or its id or mask the form itself by passing in true.
7770 waitMsgTarget : false,
7775 * @cfg {Boolean} errorMask (true|false) default false
7780 * @cfg {Number} maskOffset Default 100
7785 * @cfg {Boolean} maskBody
7789 getAutoCreate : function(){
7793 method : this.method || 'POST',
7794 id : this.id || Roo.id(),
7797 if (this.parent().xtype.match(/^Nav/)) {
7798 cfg.cls = 'navbar-form navbar-' + this.align;
7802 if (this.labelAlign == 'left' ) {
7803 cfg.cls += ' form-horizontal';
7809 initEvents : function()
7811 this.el.on('submit', this.onSubmit, this);
7812 // this was added as random key presses on the form where triggering form submit.
7813 this.el.on('keypress', function(e) {
7814 if (e.getCharCode() != 13) {
7817 // we might need to allow it for textareas.. and some other items.
7818 // check e.getTarget().
7820 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7824 Roo.log("keypress blocked");
7832 onSubmit : function(e){
7837 * Returns true if client-side validation on the form is successful.
7840 isValid : function(){
7841 var items = this.getItems();
7845 items.each(function(f){
7851 Roo.log('invalid field: ' + f.name);
7855 if(!target && f.el.isVisible(true)){
7861 if(this.errorMask && !valid){
7862 Roo.bootstrap.Form.popover.mask(this, target);
7869 * Returns true if any fields in this form have changed since their original load.
7872 isDirty : function(){
7874 var items = this.getItems();
7875 items.each(function(f){
7885 * Performs a predefined action (submit or load) or custom actions you define on this form.
7886 * @param {String} actionName The name of the action type
7887 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7888 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7889 * accept other config options):
7891 Property Type Description
7892 ---------------- --------------- ----------------------------------------------------------------------------------
7893 url String The url for the action (defaults to the form's url)
7894 method String The form method to use (defaults to the form's method, or POST if not defined)
7895 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7896 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7897 validate the form on the client (defaults to false)
7899 * @return {BasicForm} this
7901 doAction : function(action, options){
7902 if(typeof action == 'string'){
7903 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7905 if(this.fireEvent('beforeaction', this, action) !== false){
7906 this.beforeAction(action);
7907 action.run.defer(100, action);
7913 beforeAction : function(action){
7914 var o = action.options;
7919 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7921 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7924 // not really supported yet.. ??
7926 //if(this.waitMsgTarget === true){
7927 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7928 //}else if(this.waitMsgTarget){
7929 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7930 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7932 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7938 afterAction : function(action, success){
7939 this.activeAction = null;
7940 var o = action.options;
7945 Roo.get(document.body).unmask();
7951 //if(this.waitMsgTarget === true){
7952 // this.el.unmask();
7953 //}else if(this.waitMsgTarget){
7954 // this.waitMsgTarget.unmask();
7956 // Roo.MessageBox.updateProgress(1);
7957 // Roo.MessageBox.hide();
7964 Roo.callback(o.success, o.scope, [this, action]);
7965 this.fireEvent('actioncomplete', this, action);
7969 // failure condition..
7970 // we have a scenario where updates need confirming.
7971 // eg. if a locking scenario exists..
7972 // we look for { errors : { needs_confirm : true }} in the response.
7974 (typeof(action.result) != 'undefined') &&
7975 (typeof(action.result.errors) != 'undefined') &&
7976 (typeof(action.result.errors.needs_confirm) != 'undefined')
7979 Roo.log("not supported yet");
7982 Roo.MessageBox.confirm(
7983 "Change requires confirmation",
7984 action.result.errorMsg,
7989 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7999 Roo.callback(o.failure, o.scope, [this, action]);
8000 // show an error message if no failed handler is set..
8001 if (!this.hasListener('actionfailed')) {
8002 Roo.log("need to add dialog support");
8004 Roo.MessageBox.alert("Error",
8005 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8006 action.result.errorMsg :
8007 "Saving Failed, please check your entries or try again"
8012 this.fireEvent('actionfailed', this, action);
8017 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8018 * @param {String} id The value to search for
8021 findField : function(id){
8022 var items = this.getItems();
8023 var field = items.get(id);
8025 items.each(function(f){
8026 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8033 return field || null;
8036 * Mark fields in this form invalid in bulk.
8037 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8038 * @return {BasicForm} this
8040 markInvalid : function(errors){
8041 if(errors instanceof Array){
8042 for(var i = 0, len = errors.length; i < len; i++){
8043 var fieldError = errors[i];
8044 var f = this.findField(fieldError.id);
8046 f.markInvalid(fieldError.msg);
8052 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8053 field.markInvalid(errors[id]);
8057 //Roo.each(this.childForms || [], function (f) {
8058 // f.markInvalid(errors);
8065 * Set values for fields in this form in bulk.
8066 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8067 * @return {BasicForm} this
8069 setValues : function(values){
8070 if(values instanceof Array){ // array of objects
8071 for(var i = 0, len = values.length; i < len; i++){
8073 var f = this.findField(v.id);
8075 f.setValue(v.value);
8076 if(this.trackResetOnLoad){
8077 f.originalValue = f.getValue();
8081 }else{ // object hash
8084 if(typeof values[id] != 'function' && (field = this.findField(id))){
8086 if (field.setFromData &&
8088 field.displayField &&
8089 // combos' with local stores can
8090 // be queried via setValue()
8091 // to set their value..
8092 (field.store && !field.store.isLocal)
8096 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8097 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8098 field.setFromData(sd);
8100 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8102 field.setFromData(values);
8105 field.setValue(values[id]);
8109 if(this.trackResetOnLoad){
8110 field.originalValue = field.getValue();
8116 //Roo.each(this.childForms || [], function (f) {
8117 // f.setValues(values);
8124 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8125 * they are returned as an array.
8126 * @param {Boolean} asString
8129 getValues : function(asString){
8130 //if (this.childForms) {
8131 // copy values from the child forms
8132 // Roo.each(this.childForms, function (f) {
8133 // this.setValues(f.getValues());
8139 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8140 if(asString === true){
8143 return Roo.urlDecode(fs);
8147 * Returns the fields in this form as an object with key/value pairs.
8148 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8151 getFieldValues : function(with_hidden)
8153 var items = this.getItems();
8155 items.each(function(f){
8161 var v = f.getValue();
8163 if (f.inputType =='radio') {
8164 if (typeof(ret[f.getName()]) == 'undefined') {
8165 ret[f.getName()] = ''; // empty..
8168 if (!f.el.dom.checked) {
8176 if(f.xtype == 'MoneyField'){
8177 ret[f.currencyName] = f.getCurrency();
8180 // not sure if this supported any more..
8181 if ((typeof(v) == 'object') && f.getRawValue) {
8182 v = f.getRawValue() ; // dates..
8184 // combo boxes where name != hiddenName...
8185 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8186 ret[f.name] = f.getRawValue();
8188 ret[f.getName()] = v;
8195 * Clears all invalid messages in this form.
8196 * @return {BasicForm} this
8198 clearInvalid : function(){
8199 var items = this.getItems();
8201 items.each(function(f){
8210 * @return {BasicForm} this
8213 var items = this.getItems();
8214 items.each(function(f){
8218 Roo.each(this.childForms || [], function (f) {
8226 getItems : function()
8228 var r=new Roo.util.MixedCollection(false, function(o){
8229 return o.id || (o.id = Roo.id());
8231 var iter = function(el) {
8238 Roo.each(el.items,function(e) {
8247 hideFields : function(items)
8249 Roo.each(items, function(i){
8251 var f = this.findField(i);
8257 if(f.xtype == 'DateField'){
8258 f.setVisible(false);
8267 showFields : function(items)
8269 Roo.each(items, function(i){
8271 var f = this.findField(i);
8277 if(f.xtype == 'DateField'){
8289 Roo.apply(Roo.bootstrap.Form, {
8316 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8317 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8318 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8319 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8322 this.maskEl.top.enableDisplayMode("block");
8323 this.maskEl.left.enableDisplayMode("block");
8324 this.maskEl.bottom.enableDisplayMode("block");
8325 this.maskEl.right.enableDisplayMode("block");
8327 this.toolTip = new Roo.bootstrap.Tooltip({
8328 cls : 'roo-form-error-popover',
8330 'left' : ['r-l', [-2,0], 'right'],
8331 'right' : ['l-r', [2,0], 'left'],
8332 'bottom' : ['tl-bl', [0,2], 'top'],
8333 'top' : [ 'bl-tl', [0,-2], 'bottom']
8337 this.toolTip.render(Roo.get(document.body));
8339 this.toolTip.el.enableDisplayMode("block");
8341 Roo.get(document.body).on('click', function(){
8345 Roo.get(document.body).on('touchstart', function(){
8349 this.isApplied = true
8352 mask : function(form, target)
8356 this.target = target;
8358 if(!this.form.errorMask || !target.el){
8362 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8364 Roo.log(scrollable);
8366 var ot = this.target.el.calcOffsetsTo(scrollable);
8368 var scrollTo = ot[1] - this.form.maskOffset;
8370 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8372 scrollable.scrollTo('top', scrollTo);
8374 var box = this.target.el.getBox();
8376 var zIndex = Roo.bootstrap.Modal.zIndex++;
8379 this.maskEl.top.setStyle('position', 'absolute');
8380 this.maskEl.top.setStyle('z-index', zIndex);
8381 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8382 this.maskEl.top.setLeft(0);
8383 this.maskEl.top.setTop(0);
8384 this.maskEl.top.show();
8386 this.maskEl.left.setStyle('position', 'absolute');
8387 this.maskEl.left.setStyle('z-index', zIndex);
8388 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8389 this.maskEl.left.setLeft(0);
8390 this.maskEl.left.setTop(box.y - this.padding);
8391 this.maskEl.left.show();
8393 this.maskEl.bottom.setStyle('position', 'absolute');
8394 this.maskEl.bottom.setStyle('z-index', zIndex);
8395 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8396 this.maskEl.bottom.setLeft(0);
8397 this.maskEl.bottom.setTop(box.bottom + this.padding);
8398 this.maskEl.bottom.show();
8400 this.maskEl.right.setStyle('position', 'absolute');
8401 this.maskEl.right.setStyle('z-index', zIndex);
8402 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8403 this.maskEl.right.setLeft(box.right + this.padding);
8404 this.maskEl.right.setTop(box.y - this.padding);
8405 this.maskEl.right.show();
8407 this.toolTip.bindEl = this.target.el;
8409 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8411 var tip = this.target.blankText;
8413 if(this.target.getValue() !== '' ) {
8415 if (this.target.invalidText.length) {
8416 tip = this.target.invalidText;
8417 } else if (this.target.regexText.length){
8418 tip = this.target.regexText;
8422 this.toolTip.show(tip);
8424 this.intervalID = window.setInterval(function() {
8425 Roo.bootstrap.Form.popover.unmask();
8428 window.onwheel = function(){ return false;};
8430 (function(){ this.isMasked = true; }).defer(500, this);
8436 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8440 this.maskEl.top.setStyle('position', 'absolute');
8441 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8442 this.maskEl.top.hide();
8444 this.maskEl.left.setStyle('position', 'absolute');
8445 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8446 this.maskEl.left.hide();
8448 this.maskEl.bottom.setStyle('position', 'absolute');
8449 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8450 this.maskEl.bottom.hide();
8452 this.maskEl.right.setStyle('position', 'absolute');
8453 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8454 this.maskEl.right.hide();
8456 this.toolTip.hide();
8458 this.toolTip.el.hide();
8460 window.onwheel = function(){ return true;};
8462 if(this.intervalID){
8463 window.clearInterval(this.intervalID);
8464 this.intervalID = false;
8467 this.isMasked = false;
8477 * Ext JS Library 1.1.1
8478 * Copyright(c) 2006-2007, Ext JS, LLC.
8480 * Originally Released Under LGPL - original licence link has changed is not relivant.
8483 * <script type="text/javascript">
8486 * @class Roo.form.VTypes
8487 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8490 Roo.form.VTypes = function(){
8491 // closure these in so they are only created once.
8492 var alpha = /^[a-zA-Z_]+$/;
8493 var alphanum = /^[a-zA-Z0-9_]+$/;
8494 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8495 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8497 // All these messages and functions are configurable
8500 * The function used to validate email addresses
8501 * @param {String} value The email address
8503 'email' : function(v){
8504 return email.test(v);
8507 * The error text to display when the email validation function returns false
8510 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8512 * The keystroke filter mask to be applied on email input
8515 'emailMask' : /[a-z0-9_\.\-@]/i,
8518 * The function used to validate URLs
8519 * @param {String} value The URL
8521 'url' : function(v){
8525 * The error text to display when the url validation function returns false
8528 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8531 * The function used to validate alpha values
8532 * @param {String} value The value
8534 'alpha' : function(v){
8535 return alpha.test(v);
8538 * The error text to display when the alpha validation function returns false
8541 'alphaText' : 'This field should only contain letters and _',
8543 * The keystroke filter mask to be applied on alpha input
8546 'alphaMask' : /[a-z_]/i,
8549 * The function used to validate alphanumeric values
8550 * @param {String} value The value
8552 'alphanum' : function(v){
8553 return alphanum.test(v);
8556 * The error text to display when the alphanumeric validation function returns false
8559 'alphanumText' : 'This field should only contain letters, numbers and _',
8561 * The keystroke filter mask to be applied on alphanumeric input
8564 'alphanumMask' : /[a-z0-9_]/i
8574 * @class Roo.bootstrap.Input
8575 * @extends Roo.bootstrap.Component
8576 * Bootstrap Input class
8577 * @cfg {Boolean} disabled is it disabled
8578 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8579 * @cfg {String} name name of the input
8580 * @cfg {string} fieldLabel - the label associated
8581 * @cfg {string} placeholder - placeholder to put in text.
8582 * @cfg {string} before - input group add on before
8583 * @cfg {string} after - input group add on after
8584 * @cfg {string} size - (lg|sm) or leave empty..
8585 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8586 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8587 * @cfg {Number} md colspan out of 12 for computer-sized screens
8588 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8589 * @cfg {string} value default value of the input
8590 * @cfg {Number} labelWidth set the width of label
8591 * @cfg {Number} labellg set the width of label (1-12)
8592 * @cfg {Number} labelmd set the width of label (1-12)
8593 * @cfg {Number} labelsm set the width of label (1-12)
8594 * @cfg {Number} labelxs set the width of label (1-12)
8595 * @cfg {String} labelAlign (top|left)
8596 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8597 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8598 * @cfg {String} indicatorpos (left|right) default left
8599 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8600 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8602 * @cfg {String} align (left|center|right) Default left
8603 * @cfg {Boolean} forceFeedback (true|false) Default false
8606 * Create a new Input
8607 * @param {Object} config The config object
8610 Roo.bootstrap.Input = function(config){
8612 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8617 * Fires when this field receives input focus.
8618 * @param {Roo.form.Field} this
8623 * Fires when this field loses input focus.
8624 * @param {Roo.form.Field} this
8629 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8630 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8631 * @param {Roo.form.Field} this
8632 * @param {Roo.EventObject} e The event object
8637 * Fires just before the field blurs if the field value has changed.
8638 * @param {Roo.form.Field} this
8639 * @param {Mixed} newValue The new value
8640 * @param {Mixed} oldValue The original value
8645 * Fires after the field has been marked as invalid.
8646 * @param {Roo.form.Field} this
8647 * @param {String} msg The validation message
8652 * Fires after the field has been validated with no errors.
8653 * @param {Roo.form.Field} this
8658 * Fires after the key up
8659 * @param {Roo.form.Field} this
8660 * @param {Roo.EventObject} e The event Object
8666 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8668 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8669 automatic validation (defaults to "keyup").
8671 validationEvent : "keyup",
8673 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8675 validateOnBlur : true,
8677 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8679 validationDelay : 250,
8681 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8683 focusClass : "x-form-focus", // not needed???
8687 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8689 invalidClass : "has-warning",
8692 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8694 validClass : "has-success",
8697 * @cfg {Boolean} hasFeedback (true|false) default true
8702 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8704 invalidFeedbackClass : "glyphicon-warning-sign",
8707 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8709 validFeedbackClass : "glyphicon-ok",
8712 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8714 selectOnFocus : false,
8717 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8721 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8726 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8728 disableKeyFilter : false,
8731 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8735 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8739 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8741 blankText : "Please complete this mandatory field",
8744 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8748 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8750 maxLength : Number.MAX_VALUE,
8752 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8754 minLengthText : "The minimum length for this field is {0}",
8756 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8758 maxLengthText : "The maximum length for this field is {0}",
8762 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8763 * If available, this function will be called only after the basic validators all return true, and will be passed the
8764 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8768 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8769 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8770 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8774 * @cfg {String} regexText -- Depricated - use Invalid Text
8779 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8785 autocomplete: false,
8804 formatedValue : false,
8805 forceFeedback : false,
8807 indicatorpos : 'left',
8817 parentLabelAlign : function()
8820 while (parent.parent()) {
8821 parent = parent.parent();
8822 if (typeof(parent.labelAlign) !='undefined') {
8823 return parent.labelAlign;
8830 getAutoCreate : function()
8832 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8838 if(this.inputType != 'hidden'){
8839 cfg.cls = 'form-group' //input-group
8845 type : this.inputType,
8847 cls : 'form-control',
8848 placeholder : this.placeholder || '',
8849 autocomplete : this.autocomplete || 'new-password'
8852 if(this.capture.length){
8853 input.capture = this.capture;
8856 if(this.accept.length){
8857 input.accept = this.accept + "/*";
8861 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8864 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8865 input.maxLength = this.maxLength;
8868 if (this.disabled) {
8869 input.disabled=true;
8872 if (this.readOnly) {
8873 input.readonly=true;
8877 input.name = this.name;
8881 input.cls += ' input-' + this.size;
8885 ['xs','sm','md','lg'].map(function(size){
8886 if (settings[size]) {
8887 cfg.cls += ' col-' + size + '-' + settings[size];
8891 var inputblock = input;
8895 cls: 'glyphicon form-control-feedback'
8898 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8901 cls : 'has-feedback',
8909 if (this.before || this.after) {
8912 cls : 'input-group',
8916 if (this.before && typeof(this.before) == 'string') {
8918 inputblock.cn.push({
8920 cls : 'roo-input-before input-group-addon',
8924 if (this.before && typeof(this.before) == 'object') {
8925 this.before = Roo.factory(this.before);
8927 inputblock.cn.push({
8929 cls : 'roo-input-before input-group-' +
8930 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8934 inputblock.cn.push(input);
8936 if (this.after && typeof(this.after) == 'string') {
8937 inputblock.cn.push({
8939 cls : 'roo-input-after input-group-addon',
8943 if (this.after && typeof(this.after) == 'object') {
8944 this.after = Roo.factory(this.after);
8946 inputblock.cn.push({
8948 cls : 'roo-input-after input-group-' +
8949 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8953 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8954 inputblock.cls += ' has-feedback';
8955 inputblock.cn.push(feedback);
8959 if (align ==='left' && this.fieldLabel.length) {
8961 cfg.cls += ' roo-form-group-label-left';
8966 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8967 tooltip : 'This field is required'
8972 cls : 'control-label',
8973 html : this.fieldLabel
8984 var labelCfg = cfg.cn[1];
8985 var contentCfg = cfg.cn[2];
8987 if(this.indicatorpos == 'right'){
8992 cls : 'control-label',
8996 html : this.fieldLabel
9000 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9001 tooltip : 'This field is required'
9014 labelCfg = cfg.cn[0];
9015 contentCfg = cfg.cn[1];
9019 if(this.labelWidth > 12){
9020 labelCfg.style = "width: " + this.labelWidth + 'px';
9023 if(this.labelWidth < 13 && this.labelmd == 0){
9024 this.labelmd = this.labelWidth;
9027 if(this.labellg > 0){
9028 labelCfg.cls += ' col-lg-' + this.labellg;
9029 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9032 if(this.labelmd > 0){
9033 labelCfg.cls += ' col-md-' + this.labelmd;
9034 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9037 if(this.labelsm > 0){
9038 labelCfg.cls += ' col-sm-' + this.labelsm;
9039 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9042 if(this.labelxs > 0){
9043 labelCfg.cls += ' col-xs-' + this.labelxs;
9044 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9048 } else if ( this.fieldLabel.length) {
9053 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9054 tooltip : 'This field is required'
9058 //cls : 'input-group-addon',
9059 html : this.fieldLabel
9067 if(this.indicatorpos == 'right'){
9072 //cls : 'input-group-addon',
9073 html : this.fieldLabel
9078 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9079 tooltip : 'This field is required'
9099 if (this.parentType === 'Navbar' && this.parent().bar) {
9100 cfg.cls += ' navbar-form';
9103 if (this.parentType === 'NavGroup') {
9104 cfg.cls += ' navbar-form';
9112 * return the real input element.
9114 inputEl: function ()
9116 return this.el.select('input.form-control',true).first();
9119 tooltipEl : function()
9121 return this.inputEl();
9124 indicatorEl : function()
9126 var indicator = this.el.select('i.roo-required-indicator',true).first();
9136 setDisabled : function(v)
9138 var i = this.inputEl().dom;
9140 i.removeAttribute('disabled');
9144 i.setAttribute('disabled','true');
9146 initEvents : function()
9149 this.inputEl().on("keydown" , this.fireKey, this);
9150 this.inputEl().on("focus", this.onFocus, this);
9151 this.inputEl().on("blur", this.onBlur, this);
9153 this.inputEl().relayEvent('keyup', this);
9155 this.indicator = this.indicatorEl();
9158 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9161 // reference to original value for reset
9162 this.originalValue = this.getValue();
9163 //Roo.form.TextField.superclass.initEvents.call(this);
9164 if(this.validationEvent == 'keyup'){
9165 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9166 this.inputEl().on('keyup', this.filterValidation, this);
9168 else if(this.validationEvent !== false){
9169 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9172 if(this.selectOnFocus){
9173 this.on("focus", this.preFocus, this);
9176 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9177 this.inputEl().on("keypress", this.filterKeys, this);
9179 this.inputEl().relayEvent('keypress', this);
9182 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9183 this.el.on("click", this.autoSize, this);
9186 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9187 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9190 if (typeof(this.before) == 'object') {
9191 this.before.render(this.el.select('.roo-input-before',true).first());
9193 if (typeof(this.after) == 'object') {
9194 this.after.render(this.el.select('.roo-input-after',true).first());
9197 this.inputEl().on('change', this.onChange, this);
9200 filterValidation : function(e){
9201 if(!e.isNavKeyPress()){
9202 this.validationTask.delay(this.validationDelay);
9206 * Validates the field value
9207 * @return {Boolean} True if the value is valid, else false
9209 validate : function(){
9210 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9211 if(this.disabled || this.validateValue(this.getRawValue())){
9222 * Validates a value according to the field's validation rules and marks the field as invalid
9223 * if the validation fails
9224 * @param {Mixed} value The value to validate
9225 * @return {Boolean} True if the value is valid, else false
9227 validateValue : function(value)
9229 if(this.getVisibilityEl().hasClass('hidden')){
9233 if(value.length < 1) { // if it's blank
9234 if(this.allowBlank){
9240 if(value.length < this.minLength){
9243 if(value.length > this.maxLength){
9247 var vt = Roo.form.VTypes;
9248 if(!vt[this.vtype](value, this)){
9252 if(typeof this.validator == "function"){
9253 var msg = this.validator(value);
9257 if (typeof(msg) == 'string') {
9258 this.invalidText = msg;
9262 if(this.regex && !this.regex.test(value)){
9270 fireKey : function(e){
9271 //Roo.log('field ' + e.getKey());
9272 if(e.isNavKeyPress()){
9273 this.fireEvent("specialkey", this, e);
9276 focus : function (selectText){
9278 this.inputEl().focus();
9279 if(selectText === true){
9280 this.inputEl().dom.select();
9286 onFocus : function(){
9287 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9288 // this.el.addClass(this.focusClass);
9291 this.hasFocus = true;
9292 this.startValue = this.getValue();
9293 this.fireEvent("focus", this);
9297 beforeBlur : Roo.emptyFn,
9301 onBlur : function(){
9303 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9304 //this.el.removeClass(this.focusClass);
9306 this.hasFocus = false;
9307 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9310 var v = this.getValue();
9311 if(String(v) !== String(this.startValue)){
9312 this.fireEvent('change', this, v, this.startValue);
9314 this.fireEvent("blur", this);
9317 onChange : function(e)
9319 var v = this.getValue();
9320 if(String(v) !== String(this.startValue)){
9321 this.fireEvent('change', this, v, this.startValue);
9327 * Resets the current field value to the originally loaded value and clears any validation messages
9330 this.setValue(this.originalValue);
9334 * Returns the name of the field
9335 * @return {Mixed} name The name field
9337 getName: function(){
9341 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9342 * @return {Mixed} value The field value
9344 getValue : function(){
9346 var v = this.inputEl().getValue();
9351 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9352 * @return {Mixed} value The field value
9354 getRawValue : function(){
9355 var v = this.inputEl().getValue();
9361 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9362 * @param {Mixed} value The value to set
9364 setRawValue : function(v){
9365 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9368 selectText : function(start, end){
9369 var v = this.getRawValue();
9371 start = start === undefined ? 0 : start;
9372 end = end === undefined ? v.length : end;
9373 var d = this.inputEl().dom;
9374 if(d.setSelectionRange){
9375 d.setSelectionRange(start, end);
9376 }else if(d.createTextRange){
9377 var range = d.createTextRange();
9378 range.moveStart("character", start);
9379 range.moveEnd("character", v.length-end);
9386 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9387 * @param {Mixed} value The value to set
9389 setValue : function(v){
9392 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9398 processValue : function(value){
9399 if(this.stripCharsRe){
9400 var newValue = value.replace(this.stripCharsRe, '');
9401 if(newValue !== value){
9402 this.setRawValue(newValue);
9409 preFocus : function(){
9411 if(this.selectOnFocus){
9412 this.inputEl().dom.select();
9415 filterKeys : function(e){
9417 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9420 var c = e.getCharCode(), cc = String.fromCharCode(c);
9421 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9424 if(!this.maskRe.test(cc)){
9429 * Clear any invalid styles/messages for this field
9431 clearInvalid : function(){
9433 if(!this.el || this.preventMark){ // not rendered
9438 this.el.removeClass(this.invalidClass);
9440 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9442 var feedback = this.el.select('.form-control-feedback', true).first();
9445 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9451 this.indicator.removeClass('visible');
9452 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9455 this.fireEvent('valid', this);
9459 * Mark this field as valid
9461 markValid : function()
9463 if(!this.el || this.preventMark){ // not rendered...
9467 this.el.removeClass([this.invalidClass, this.validClass]);
9469 var feedback = this.el.select('.form-control-feedback', true).first();
9472 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9476 this.indicator.removeClass('visible');
9477 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9484 if(this.allowBlank && !this.getRawValue().length){
9488 this.el.addClass(this.validClass);
9490 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9492 var feedback = this.el.select('.form-control-feedback', true).first();
9495 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9496 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9501 this.fireEvent('valid', this);
9505 * Mark this field as invalid
9506 * @param {String} msg The validation message
9508 markInvalid : function(msg)
9510 if(!this.el || this.preventMark){ // not rendered
9514 this.el.removeClass([this.invalidClass, this.validClass]);
9516 var feedback = this.el.select('.form-control-feedback', true).first();
9519 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9526 if(this.allowBlank && !this.getRawValue().length){
9531 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9532 this.indicator.addClass('visible');
9535 this.el.addClass(this.invalidClass);
9537 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9539 var feedback = this.el.select('.form-control-feedback', true).first();
9542 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9544 if(this.getValue().length || this.forceFeedback){
9545 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9552 this.fireEvent('invalid', this, msg);
9555 SafariOnKeyDown : function(event)
9557 // this is a workaround for a password hang bug on chrome/ webkit.
9558 if (this.inputEl().dom.type != 'password') {
9562 var isSelectAll = false;
9564 if(this.inputEl().dom.selectionEnd > 0){
9565 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9567 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9568 event.preventDefault();
9573 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9575 event.preventDefault();
9576 // this is very hacky as keydown always get's upper case.
9578 var cc = String.fromCharCode(event.getCharCode());
9579 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9583 adjustWidth : function(tag, w){
9584 tag = tag.toLowerCase();
9585 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9586 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9590 if(tag == 'textarea'){
9593 }else if(Roo.isOpera){
9597 if(tag == 'textarea'){
9605 setFieldLabel : function(v)
9612 var ar = this.el.select('label > span',true);
9614 if (ar.elements.length) {
9615 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9616 this.fieldLabel = v;
9620 var br = this.el.select('label',true);
9622 if(br.elements.length) {
9623 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9624 this.fieldLabel = v;
9628 Roo.log('Cannot Found any of label > span || label in input');
9632 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9633 this.fieldLabel = v;
9648 * @class Roo.bootstrap.TextArea
9649 * @extends Roo.bootstrap.Input
9650 * Bootstrap TextArea class
9651 * @cfg {Number} cols Specifies the visible width of a text area
9652 * @cfg {Number} rows Specifies the visible number of lines in a text area
9653 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9654 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9655 * @cfg {string} html text
9658 * Create a new TextArea
9659 * @param {Object} config The config object
9662 Roo.bootstrap.TextArea = function(config){
9663 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9667 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9677 getAutoCreate : function(){
9679 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9685 if(this.inputType != 'hidden'){
9686 cfg.cls = 'form-group' //input-group
9694 value : this.value || '',
9695 html: this.html || '',
9696 cls : 'form-control',
9697 placeholder : this.placeholder || ''
9701 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9702 input.maxLength = this.maxLength;
9706 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9710 input.cols = this.cols;
9713 if (this.readOnly) {
9714 input.readonly = true;
9718 input.name = this.name;
9722 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9726 ['xs','sm','md','lg'].map(function(size){
9727 if (settings[size]) {
9728 cfg.cls += ' col-' + size + '-' + settings[size];
9732 var inputblock = input;
9734 if(this.hasFeedback && !this.allowBlank){
9738 cls: 'glyphicon form-control-feedback'
9742 cls : 'has-feedback',
9751 if (this.before || this.after) {
9754 cls : 'input-group',
9758 inputblock.cn.push({
9760 cls : 'input-group-addon',
9765 inputblock.cn.push(input);
9767 if(this.hasFeedback && !this.allowBlank){
9768 inputblock.cls += ' has-feedback';
9769 inputblock.cn.push(feedback);
9773 inputblock.cn.push({
9775 cls : 'input-group-addon',
9782 if (align ==='left' && this.fieldLabel.length) {
9787 cls : 'control-label',
9788 html : this.fieldLabel
9799 if(this.labelWidth > 12){
9800 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9803 if(this.labelWidth < 13 && this.labelmd == 0){
9804 this.labelmd = this.labelWidth;
9807 if(this.labellg > 0){
9808 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9809 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9812 if(this.labelmd > 0){
9813 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9814 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9817 if(this.labelsm > 0){
9818 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9819 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9822 if(this.labelxs > 0){
9823 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9824 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9827 } else if ( this.fieldLabel.length) {
9832 //cls : 'input-group-addon',
9833 html : this.fieldLabel
9851 if (this.disabled) {
9852 input.disabled=true;
9859 * return the real textarea element.
9861 inputEl: function ()
9863 return this.el.select('textarea.form-control',true).first();
9867 * Clear any invalid styles/messages for this field
9869 clearInvalid : function()
9872 if(!this.el || this.preventMark){ // not rendered
9876 var label = this.el.select('label', true).first();
9877 var icon = this.el.select('i.fa-star', true).first();
9883 this.el.removeClass(this.invalidClass);
9885 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9887 var feedback = this.el.select('.form-control-feedback', true).first();
9890 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9895 this.fireEvent('valid', this);
9899 * Mark this field as valid
9901 markValid : function()
9903 if(!this.el || this.preventMark){ // not rendered
9907 this.el.removeClass([this.invalidClass, this.validClass]);
9909 var feedback = this.el.select('.form-control-feedback', true).first();
9912 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9915 if(this.disabled || this.allowBlank){
9919 var label = this.el.select('label', true).first();
9920 var icon = this.el.select('i.fa-star', true).first();
9926 this.el.addClass(this.validClass);
9928 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9930 var feedback = this.el.select('.form-control-feedback', true).first();
9933 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9934 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9939 this.fireEvent('valid', this);
9943 * Mark this field as invalid
9944 * @param {String} msg The validation message
9946 markInvalid : function(msg)
9948 if(!this.el || this.preventMark){ // not rendered
9952 this.el.removeClass([this.invalidClass, this.validClass]);
9954 var feedback = this.el.select('.form-control-feedback', true).first();
9957 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9960 if(this.disabled || this.allowBlank){
9964 var label = this.el.select('label', true).first();
9965 var icon = this.el.select('i.fa-star', true).first();
9967 if(!this.getValue().length && label && !icon){
9968 this.el.createChild({
9970 cls : 'text-danger fa fa-lg fa-star',
9971 tooltip : 'This field is required',
9972 style : 'margin-right:5px;'
9976 this.el.addClass(this.invalidClass);
9978 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9980 var feedback = this.el.select('.form-control-feedback', true).first();
9983 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9985 if(this.getValue().length || this.forceFeedback){
9986 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9993 this.fireEvent('invalid', this, msg);
10001 * trigger field - base class for combo..
10006 * @class Roo.bootstrap.TriggerField
10007 * @extends Roo.bootstrap.Input
10008 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10009 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10010 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10011 * for which you can provide a custom implementation. For example:
10013 var trigger = new Roo.bootstrap.TriggerField();
10014 trigger.onTriggerClick = myTriggerFn;
10015 trigger.applyTo('my-field');
10018 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10019 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10020 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10021 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10022 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10025 * Create a new TriggerField.
10026 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10027 * to the base TextField)
10029 Roo.bootstrap.TriggerField = function(config){
10030 this.mimicing = false;
10031 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10034 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10036 * @cfg {String} triggerClass A CSS class to apply to the trigger
10039 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10044 * @cfg {Boolean} removable (true|false) special filter default false
10048 /** @cfg {Boolean} grow @hide */
10049 /** @cfg {Number} growMin @hide */
10050 /** @cfg {Number} growMax @hide */
10056 autoSize: Roo.emptyFn,
10060 deferHeight : true,
10063 actionMode : 'wrap',
10068 getAutoCreate : function(){
10070 var align = this.labelAlign || this.parentLabelAlign();
10075 cls: 'form-group' //input-group
10082 type : this.inputType,
10083 cls : 'form-control',
10084 autocomplete: 'new-password',
10085 placeholder : this.placeholder || ''
10089 input.name = this.name;
10092 input.cls += ' input-' + this.size;
10095 if (this.disabled) {
10096 input.disabled=true;
10099 var inputblock = input;
10101 if(this.hasFeedback && !this.allowBlank){
10105 cls: 'glyphicon form-control-feedback'
10108 if(this.removable && !this.editable && !this.tickable){
10110 cls : 'has-feedback',
10116 cls : 'roo-combo-removable-btn close'
10123 cls : 'has-feedback',
10132 if(this.removable && !this.editable && !this.tickable){
10134 cls : 'roo-removable',
10140 cls : 'roo-combo-removable-btn close'
10147 if (this.before || this.after) {
10150 cls : 'input-group',
10154 inputblock.cn.push({
10156 cls : 'input-group-addon',
10161 inputblock.cn.push(input);
10163 if(this.hasFeedback && !this.allowBlank){
10164 inputblock.cls += ' has-feedback';
10165 inputblock.cn.push(feedback);
10169 inputblock.cn.push({
10171 cls : 'input-group-addon',
10184 cls: 'form-hidden-field'
10198 cls: 'form-hidden-field'
10202 cls: 'roo-select2-choices',
10206 cls: 'roo-select2-search-field',
10219 cls: 'roo-select2-container input-group',
10224 // cls: 'typeahead typeahead-long dropdown-menu',
10225 // style: 'display:none'
10230 if(!this.multiple && this.showToggleBtn){
10236 if (this.caret != false) {
10239 cls: 'fa fa-' + this.caret
10246 cls : 'input-group-addon btn dropdown-toggle',
10251 cls: 'combobox-clear',
10265 combobox.cls += ' roo-select2-container-multi';
10268 if (align ==='left' && this.fieldLabel.length) {
10270 cfg.cls += ' roo-form-group-label-left';
10275 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10276 tooltip : 'This field is required'
10281 cls : 'control-label',
10282 html : this.fieldLabel
10294 var labelCfg = cfg.cn[1];
10295 var contentCfg = cfg.cn[2];
10297 if(this.indicatorpos == 'right'){
10302 cls : 'control-label',
10306 html : this.fieldLabel
10310 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10311 tooltip : 'This field is required'
10324 labelCfg = cfg.cn[0];
10325 contentCfg = cfg.cn[1];
10328 if(this.labelWidth > 12){
10329 labelCfg.style = "width: " + this.labelWidth + 'px';
10332 if(this.labelWidth < 13 && this.labelmd == 0){
10333 this.labelmd = this.labelWidth;
10336 if(this.labellg > 0){
10337 labelCfg.cls += ' col-lg-' + this.labellg;
10338 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10341 if(this.labelmd > 0){
10342 labelCfg.cls += ' col-md-' + this.labelmd;
10343 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10346 if(this.labelsm > 0){
10347 labelCfg.cls += ' col-sm-' + this.labelsm;
10348 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10351 if(this.labelxs > 0){
10352 labelCfg.cls += ' col-xs-' + this.labelxs;
10353 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10356 } else if ( this.fieldLabel.length) {
10357 // Roo.log(" label");
10361 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10362 tooltip : 'This field is required'
10366 //cls : 'input-group-addon',
10367 html : this.fieldLabel
10375 if(this.indicatorpos == 'right'){
10383 html : this.fieldLabel
10387 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10388 tooltip : 'This field is required'
10401 // Roo.log(" no label && no align");
10408 ['xs','sm','md','lg'].map(function(size){
10409 if (settings[size]) {
10410 cfg.cls += ' col-' + size + '-' + settings[size];
10421 onResize : function(w, h){
10422 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10423 // if(typeof w == 'number'){
10424 // var x = w - this.trigger.getWidth();
10425 // this.inputEl().setWidth(this.adjustWidth('input', x));
10426 // this.trigger.setStyle('left', x+'px');
10431 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10434 getResizeEl : function(){
10435 return this.inputEl();
10439 getPositionEl : function(){
10440 return this.inputEl();
10444 alignErrorIcon : function(){
10445 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10449 initEvents : function(){
10453 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10454 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10455 if(!this.multiple && this.showToggleBtn){
10456 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10457 if(this.hideTrigger){
10458 this.trigger.setDisplayed(false);
10460 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10464 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10467 if(this.removable && !this.editable && !this.tickable){
10468 var close = this.closeTriggerEl();
10471 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10472 close.on('click', this.removeBtnClick, this, close);
10476 //this.trigger.addClassOnOver('x-form-trigger-over');
10477 //this.trigger.addClassOnClick('x-form-trigger-click');
10480 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10484 closeTriggerEl : function()
10486 var close = this.el.select('.roo-combo-removable-btn', true).first();
10487 return close ? close : false;
10490 removeBtnClick : function(e, h, el)
10492 e.preventDefault();
10494 if(this.fireEvent("remove", this) !== false){
10496 this.fireEvent("afterremove", this)
10500 createList : function()
10502 this.list = Roo.get(document.body).createChild({
10504 cls: 'typeahead typeahead-long dropdown-menu',
10505 style: 'display:none'
10508 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10513 initTrigger : function(){
10518 onDestroy : function(){
10520 this.trigger.removeAllListeners();
10521 // this.trigger.remove();
10524 // this.wrap.remove();
10526 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10530 onFocus : function(){
10531 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10533 if(!this.mimicing){
10534 this.wrap.addClass('x-trigger-wrap-focus');
10535 this.mimicing = true;
10536 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10537 if(this.monitorTab){
10538 this.el.on("keydown", this.checkTab, this);
10545 checkTab : function(e){
10546 if(e.getKey() == e.TAB){
10547 this.triggerBlur();
10552 onBlur : function(){
10557 mimicBlur : function(e, t){
10559 if(!this.wrap.contains(t) && this.validateBlur()){
10560 this.triggerBlur();
10566 triggerBlur : function(){
10567 this.mimicing = false;
10568 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10569 if(this.monitorTab){
10570 this.el.un("keydown", this.checkTab, this);
10572 //this.wrap.removeClass('x-trigger-wrap-focus');
10573 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10577 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10578 validateBlur : function(e, t){
10583 onDisable : function(){
10584 this.inputEl().dom.disabled = true;
10585 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10587 // this.wrap.addClass('x-item-disabled');
10592 onEnable : function(){
10593 this.inputEl().dom.disabled = false;
10594 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10596 // this.el.removeClass('x-item-disabled');
10601 onShow : function(){
10602 var ae = this.getActionEl();
10605 ae.dom.style.display = '';
10606 ae.dom.style.visibility = 'visible';
10612 onHide : function(){
10613 var ae = this.getActionEl();
10614 ae.dom.style.display = 'none';
10618 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10619 * by an implementing function.
10621 * @param {EventObject} e
10623 onTriggerClick : Roo.emptyFn
10627 * Ext JS Library 1.1.1
10628 * Copyright(c) 2006-2007, Ext JS, LLC.
10630 * Originally Released Under LGPL - original licence link has changed is not relivant.
10633 * <script type="text/javascript">
10638 * @class Roo.data.SortTypes
10640 * Defines the default sorting (casting?) comparison functions used when sorting data.
10642 Roo.data.SortTypes = {
10644 * Default sort that does nothing
10645 * @param {Mixed} s The value being converted
10646 * @return {Mixed} The comparison value
10648 none : function(s){
10653 * The regular expression used to strip tags
10657 stripTagsRE : /<\/?[^>]+>/gi,
10660 * Strips all HTML tags to sort on text only
10661 * @param {Mixed} s The value being converted
10662 * @return {String} The comparison value
10664 asText : function(s){
10665 return String(s).replace(this.stripTagsRE, "");
10669 * Strips all HTML tags to sort on text only - Case insensitive
10670 * @param {Mixed} s The value being converted
10671 * @return {String} The comparison value
10673 asUCText : function(s){
10674 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10678 * Case insensitive string
10679 * @param {Mixed} s The value being converted
10680 * @return {String} The comparison value
10682 asUCString : function(s) {
10683 return String(s).toUpperCase();
10688 * @param {Mixed} s The value being converted
10689 * @return {Number} The comparison value
10691 asDate : function(s) {
10695 if(s instanceof Date){
10696 return s.getTime();
10698 return Date.parse(String(s));
10703 * @param {Mixed} s The value being converted
10704 * @return {Float} The comparison value
10706 asFloat : function(s) {
10707 var val = parseFloat(String(s).replace(/,/g, ""));
10716 * @param {Mixed} s The value being converted
10717 * @return {Number} The comparison value
10719 asInt : function(s) {
10720 var val = parseInt(String(s).replace(/,/g, ""));
10728 * Ext JS Library 1.1.1
10729 * Copyright(c) 2006-2007, Ext JS, LLC.
10731 * Originally Released Under LGPL - original licence link has changed is not relivant.
10734 * <script type="text/javascript">
10738 * @class Roo.data.Record
10739 * Instances of this class encapsulate both record <em>definition</em> information, and record
10740 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10741 * to access Records cached in an {@link Roo.data.Store} object.<br>
10743 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10744 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10747 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10749 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10750 * {@link #create}. The parameters are the same.
10751 * @param {Array} data An associative Array of data values keyed by the field name.
10752 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10753 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10754 * not specified an integer id is generated.
10756 Roo.data.Record = function(data, id){
10757 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10762 * Generate a constructor for a specific record layout.
10763 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10764 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10765 * Each field definition object may contain the following properties: <ul>
10766 * <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,
10767 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10768 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10769 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10770 * is being used, then this is a string containing the javascript expression to reference the data relative to
10771 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10772 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10773 * this may be omitted.</p></li>
10774 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10775 * <ul><li>auto (Default, implies no conversion)</li>
10780 * <li>date</li></ul></p></li>
10781 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10782 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10783 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10784 * by the Reader into an object that will be stored in the Record. It is passed the
10785 * following parameters:<ul>
10786 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10788 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10790 * <br>usage:<br><pre><code>
10791 var TopicRecord = Roo.data.Record.create(
10792 {name: 'title', mapping: 'topic_title'},
10793 {name: 'author', mapping: 'username'},
10794 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10795 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10796 {name: 'lastPoster', mapping: 'user2'},
10797 {name: 'excerpt', mapping: 'post_text'}
10800 var myNewRecord = new TopicRecord({
10801 title: 'Do my job please',
10804 lastPost: new Date(),
10805 lastPoster: 'Animal',
10806 excerpt: 'No way dude!'
10808 myStore.add(myNewRecord);
10813 Roo.data.Record.create = function(o){
10814 var f = function(){
10815 f.superclass.constructor.apply(this, arguments);
10817 Roo.extend(f, Roo.data.Record);
10818 var p = f.prototype;
10819 p.fields = new Roo.util.MixedCollection(false, function(field){
10822 for(var i = 0, len = o.length; i < len; i++){
10823 p.fields.add(new Roo.data.Field(o[i]));
10825 f.getField = function(name){
10826 return p.fields.get(name);
10831 Roo.data.Record.AUTO_ID = 1000;
10832 Roo.data.Record.EDIT = 'edit';
10833 Roo.data.Record.REJECT = 'reject';
10834 Roo.data.Record.COMMIT = 'commit';
10836 Roo.data.Record.prototype = {
10838 * Readonly flag - true if this record has been modified.
10847 join : function(store){
10848 this.store = store;
10852 * Set the named field to the specified value.
10853 * @param {String} name The name of the field to set.
10854 * @param {Object} value The value to set the field to.
10856 set : function(name, value){
10857 if(this.data[name] == value){
10861 if(!this.modified){
10862 this.modified = {};
10864 if(typeof this.modified[name] == 'undefined'){
10865 this.modified[name] = this.data[name];
10867 this.data[name] = value;
10868 if(!this.editing && this.store){
10869 this.store.afterEdit(this);
10874 * Get the value of the named field.
10875 * @param {String} name The name of the field to get the value of.
10876 * @return {Object} The value of the field.
10878 get : function(name){
10879 return this.data[name];
10883 beginEdit : function(){
10884 this.editing = true;
10885 this.modified = {};
10889 cancelEdit : function(){
10890 this.editing = false;
10891 delete this.modified;
10895 endEdit : function(){
10896 this.editing = false;
10897 if(this.dirty && this.store){
10898 this.store.afterEdit(this);
10903 * Usually called by the {@link Roo.data.Store} which owns the Record.
10904 * Rejects all changes made to the Record since either creation, or the last commit operation.
10905 * Modified fields are reverted to their original values.
10907 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10908 * of reject operations.
10910 reject : function(){
10911 var m = this.modified;
10913 if(typeof m[n] != "function"){
10914 this.data[n] = m[n];
10917 this.dirty = false;
10918 delete this.modified;
10919 this.editing = false;
10921 this.store.afterReject(this);
10926 * Usually called by the {@link Roo.data.Store} which owns the Record.
10927 * Commits all changes made to the Record since either creation, or the last commit operation.
10929 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10930 * of commit operations.
10932 commit : function(){
10933 this.dirty = false;
10934 delete this.modified;
10935 this.editing = false;
10937 this.store.afterCommit(this);
10942 hasError : function(){
10943 return this.error != null;
10947 clearError : function(){
10952 * Creates a copy of this record.
10953 * @param {String} id (optional) A new record id if you don't want to use this record's id
10956 copy : function(newId) {
10957 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10961 * Ext JS Library 1.1.1
10962 * Copyright(c) 2006-2007, Ext JS, LLC.
10964 * Originally Released Under LGPL - original licence link has changed is not relivant.
10967 * <script type="text/javascript">
10973 * @class Roo.data.Store
10974 * @extends Roo.util.Observable
10975 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10976 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10978 * 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
10979 * has no knowledge of the format of the data returned by the Proxy.<br>
10981 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10982 * instances from the data object. These records are cached and made available through accessor functions.
10984 * Creates a new Store.
10985 * @param {Object} config A config object containing the objects needed for the Store to access data,
10986 * and read the data into Records.
10988 Roo.data.Store = function(config){
10989 this.data = new Roo.util.MixedCollection(false);
10990 this.data.getKey = function(o){
10993 this.baseParams = {};
10995 this.paramNames = {
11000 "multisort" : "_multisort"
11003 if(config && config.data){
11004 this.inlineData = config.data;
11005 delete config.data;
11008 Roo.apply(this, config);
11010 if(this.reader){ // reader passed
11011 this.reader = Roo.factory(this.reader, Roo.data);
11012 this.reader.xmodule = this.xmodule || false;
11013 if(!this.recordType){
11014 this.recordType = this.reader.recordType;
11016 if(this.reader.onMetaChange){
11017 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11021 if(this.recordType){
11022 this.fields = this.recordType.prototype.fields;
11024 this.modified = [];
11028 * @event datachanged
11029 * Fires when the data cache has changed, and a widget which is using this Store
11030 * as a Record cache should refresh its view.
11031 * @param {Store} this
11033 datachanged : true,
11035 * @event metachange
11036 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11037 * @param {Store} this
11038 * @param {Object} meta The JSON metadata
11043 * Fires when Records have been added to the Store
11044 * @param {Store} this
11045 * @param {Roo.data.Record[]} records The array of Records added
11046 * @param {Number} index The index at which the record(s) were added
11051 * Fires when a Record has been removed from the Store
11052 * @param {Store} this
11053 * @param {Roo.data.Record} record The Record that was removed
11054 * @param {Number} index The index at which the record was removed
11059 * Fires when a Record has been updated
11060 * @param {Store} this
11061 * @param {Roo.data.Record} record The Record that was updated
11062 * @param {String} operation The update operation being performed. Value may be one of:
11064 Roo.data.Record.EDIT
11065 Roo.data.Record.REJECT
11066 Roo.data.Record.COMMIT
11072 * Fires when the data cache has been cleared.
11073 * @param {Store} this
11077 * @event beforeload
11078 * Fires before a request is made for a new data object. If the beforeload handler returns false
11079 * the load action will be canceled.
11080 * @param {Store} this
11081 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11085 * @event beforeloadadd
11086 * Fires after a new set of Records has been loaded.
11087 * @param {Store} this
11088 * @param {Roo.data.Record[]} records The Records that were loaded
11089 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11091 beforeloadadd : true,
11094 * Fires after a new set of Records has been loaded, before they are added to the store.
11095 * @param {Store} this
11096 * @param {Roo.data.Record[]} records The Records that were loaded
11097 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11098 * @params {Object} return from reader
11102 * @event loadexception
11103 * Fires if an exception occurs in the Proxy during loading.
11104 * Called with the signature of the Proxy's "loadexception" event.
11105 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11108 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11109 * @param {Object} load options
11110 * @param {Object} jsonData from your request (normally this contains the Exception)
11112 loadexception : true
11116 this.proxy = Roo.factory(this.proxy, Roo.data);
11117 this.proxy.xmodule = this.xmodule || false;
11118 this.relayEvents(this.proxy, ["loadexception"]);
11120 this.sortToggle = {};
11121 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11123 Roo.data.Store.superclass.constructor.call(this);
11125 if(this.inlineData){
11126 this.loadData(this.inlineData);
11127 delete this.inlineData;
11131 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11133 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11134 * without a remote query - used by combo/forms at present.
11138 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11141 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11144 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11145 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11148 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11149 * on any HTTP request
11152 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11155 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11159 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11160 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11162 remoteSort : false,
11165 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11166 * loaded or when a record is removed. (defaults to false).
11168 pruneModifiedRecords : false,
11171 lastOptions : null,
11174 * Add Records to the Store and fires the add event.
11175 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11177 add : function(records){
11178 records = [].concat(records);
11179 for(var i = 0, len = records.length; i < len; i++){
11180 records[i].join(this);
11182 var index = this.data.length;
11183 this.data.addAll(records);
11184 this.fireEvent("add", this, records, index);
11188 * Remove a Record from the Store and fires the remove event.
11189 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11191 remove : function(record){
11192 var index = this.data.indexOf(record);
11193 this.data.removeAt(index);
11195 if(this.pruneModifiedRecords){
11196 this.modified.remove(record);
11198 this.fireEvent("remove", this, record, index);
11202 * Remove all Records from the Store and fires the clear event.
11204 removeAll : function(){
11206 if(this.pruneModifiedRecords){
11207 this.modified = [];
11209 this.fireEvent("clear", this);
11213 * Inserts Records to the Store at the given index and fires the add event.
11214 * @param {Number} index The start index at which to insert the passed Records.
11215 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11217 insert : function(index, records){
11218 records = [].concat(records);
11219 for(var i = 0, len = records.length; i < len; i++){
11220 this.data.insert(index, records[i]);
11221 records[i].join(this);
11223 this.fireEvent("add", this, records, index);
11227 * Get the index within the cache of the passed Record.
11228 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11229 * @return {Number} The index of the passed Record. Returns -1 if not found.
11231 indexOf : function(record){
11232 return this.data.indexOf(record);
11236 * Get the index within the cache of the Record with the passed id.
11237 * @param {String} id The id of the Record to find.
11238 * @return {Number} The index of the Record. Returns -1 if not found.
11240 indexOfId : function(id){
11241 return this.data.indexOfKey(id);
11245 * Get the Record with the specified id.
11246 * @param {String} id The id of the Record to find.
11247 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11249 getById : function(id){
11250 return this.data.key(id);
11254 * Get the Record at the specified index.
11255 * @param {Number} index The index of the Record to find.
11256 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11258 getAt : function(index){
11259 return this.data.itemAt(index);
11263 * Returns a range of Records between specified indices.
11264 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11265 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11266 * @return {Roo.data.Record[]} An array of Records
11268 getRange : function(start, end){
11269 return this.data.getRange(start, end);
11273 storeOptions : function(o){
11274 o = Roo.apply({}, o);
11277 this.lastOptions = o;
11281 * Loads the Record cache from the configured Proxy using the configured Reader.
11283 * If using remote paging, then the first load call must specify the <em>start</em>
11284 * and <em>limit</em> properties in the options.params property to establish the initial
11285 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11287 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11288 * and this call will return before the new data has been loaded. Perform any post-processing
11289 * in a callback function, or in a "load" event handler.</strong>
11291 * @param {Object} options An object containing properties which control loading options:<ul>
11292 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11293 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11294 * passed the following arguments:<ul>
11295 * <li>r : Roo.data.Record[]</li>
11296 * <li>options: Options object from the load call</li>
11297 * <li>success: Boolean success indicator</li></ul></li>
11298 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11299 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11302 load : function(options){
11303 options = options || {};
11304 if(this.fireEvent("beforeload", this, options) !== false){
11305 this.storeOptions(options);
11306 var p = Roo.apply(options.params || {}, this.baseParams);
11307 // if meta was not loaded from remote source.. try requesting it.
11308 if (!this.reader.metaFromRemote) {
11309 p._requestMeta = 1;
11311 if(this.sortInfo && this.remoteSort){
11312 var pn = this.paramNames;
11313 p[pn["sort"]] = this.sortInfo.field;
11314 p[pn["dir"]] = this.sortInfo.direction;
11316 if (this.multiSort) {
11317 var pn = this.paramNames;
11318 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11321 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11326 * Reloads the Record cache from the configured Proxy using the configured Reader and
11327 * the options from the last load operation performed.
11328 * @param {Object} options (optional) An object containing properties which may override the options
11329 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11330 * the most recently used options are reused).
11332 reload : function(options){
11333 this.load(Roo.applyIf(options||{}, this.lastOptions));
11337 // Called as a callback by the Reader during a load operation.
11338 loadRecords : function(o, options, success){
11339 if(!o || success === false){
11340 if(success !== false){
11341 this.fireEvent("load", this, [], options, o);
11343 if(options.callback){
11344 options.callback.call(options.scope || this, [], options, false);
11348 // if data returned failure - throw an exception.
11349 if (o.success === false) {
11350 // show a message if no listener is registered.
11351 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11352 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11354 // loadmask wil be hooked into this..
11355 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11358 var r = o.records, t = o.totalRecords || r.length;
11360 this.fireEvent("beforeloadadd", this, r, options, o);
11362 if(!options || options.add !== true){
11363 if(this.pruneModifiedRecords){
11364 this.modified = [];
11366 for(var i = 0, len = r.length; i < len; i++){
11370 this.data = this.snapshot;
11371 delete this.snapshot;
11374 this.data.addAll(r);
11375 this.totalLength = t;
11377 this.fireEvent("datachanged", this);
11379 this.totalLength = Math.max(t, this.data.length+r.length);
11383 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11385 var e = new Roo.data.Record({});
11387 e.set(this.parent.displayField, this.parent.emptyTitle);
11388 e.set(this.parent.valueField, '');
11393 this.fireEvent("load", this, r, options, o);
11394 if(options.callback){
11395 options.callback.call(options.scope || this, r, options, true);
11401 * Loads data from a passed data block. A Reader which understands the format of the data
11402 * must have been configured in the constructor.
11403 * @param {Object} data The data block from which to read the Records. The format of the data expected
11404 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11405 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11407 loadData : function(o, append){
11408 var r = this.reader.readRecords(o);
11409 this.loadRecords(r, {add: append}, true);
11413 * Gets the number of cached records.
11415 * <em>If using paging, this may not be the total size of the dataset. If the data object
11416 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11417 * the data set size</em>
11419 getCount : function(){
11420 return this.data.length || 0;
11424 * Gets the total number of records in the dataset as returned by the server.
11426 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11427 * the dataset size</em>
11429 getTotalCount : function(){
11430 return this.totalLength || 0;
11434 * Returns the sort state of the Store as an object with two properties:
11436 field {String} The name of the field by which the Records are sorted
11437 direction {String} The sort order, "ASC" or "DESC"
11440 getSortState : function(){
11441 return this.sortInfo;
11445 applySort : function(){
11446 if(this.sortInfo && !this.remoteSort){
11447 var s = this.sortInfo, f = s.field;
11448 var st = this.fields.get(f).sortType;
11449 var fn = function(r1, r2){
11450 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11451 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11453 this.data.sort(s.direction, fn);
11454 if(this.snapshot && this.snapshot != this.data){
11455 this.snapshot.sort(s.direction, fn);
11461 * Sets the default sort column and order to be used by the next load operation.
11462 * @param {String} fieldName The name of the field to sort by.
11463 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11465 setDefaultSort : function(field, dir){
11466 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11470 * Sort the Records.
11471 * If remote sorting is used, the sort is performed on the server, and the cache is
11472 * reloaded. If local sorting is used, the cache is sorted internally.
11473 * @param {String} fieldName The name of the field to sort by.
11474 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11476 sort : function(fieldName, dir){
11477 var f = this.fields.get(fieldName);
11479 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11481 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11482 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11487 this.sortToggle[f.name] = dir;
11488 this.sortInfo = {field: f.name, direction: dir};
11489 if(!this.remoteSort){
11491 this.fireEvent("datachanged", this);
11493 this.load(this.lastOptions);
11498 * Calls the specified function for each of the Records in the cache.
11499 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11500 * Returning <em>false</em> aborts and exits the iteration.
11501 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11503 each : function(fn, scope){
11504 this.data.each(fn, scope);
11508 * Gets all records modified since the last commit. Modified records are persisted across load operations
11509 * (e.g., during paging).
11510 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11512 getModifiedRecords : function(){
11513 return this.modified;
11517 createFilterFn : function(property, value, anyMatch){
11518 if(!value.exec){ // not a regex
11519 value = String(value);
11520 if(value.length == 0){
11523 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11525 return function(r){
11526 return value.test(r.data[property]);
11531 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11532 * @param {String} property A field on your records
11533 * @param {Number} start The record index to start at (defaults to 0)
11534 * @param {Number} end The last record index to include (defaults to length - 1)
11535 * @return {Number} The sum
11537 sum : function(property, start, end){
11538 var rs = this.data.items, v = 0;
11539 start = start || 0;
11540 end = (end || end === 0) ? end : rs.length-1;
11542 for(var i = start; i <= end; i++){
11543 v += (rs[i].data[property] || 0);
11549 * Filter the records by a specified property.
11550 * @param {String} field A field on your records
11551 * @param {String/RegExp} value Either a string that the field
11552 * should start with or a RegExp to test against the field
11553 * @param {Boolean} anyMatch True to match any part not just the beginning
11555 filter : function(property, value, anyMatch){
11556 var fn = this.createFilterFn(property, value, anyMatch);
11557 return fn ? this.filterBy(fn) : this.clearFilter();
11561 * Filter by a function. The specified function will be called with each
11562 * record in this data source. If the function returns true the record is included,
11563 * otherwise it is filtered.
11564 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11565 * @param {Object} scope (optional) The scope of the function (defaults to this)
11567 filterBy : function(fn, scope){
11568 this.snapshot = this.snapshot || this.data;
11569 this.data = this.queryBy(fn, scope||this);
11570 this.fireEvent("datachanged", this);
11574 * Query the records by a specified property.
11575 * @param {String} field A field on your records
11576 * @param {String/RegExp} value Either a string that the field
11577 * should start with or a RegExp to test against the field
11578 * @param {Boolean} anyMatch True to match any part not just the beginning
11579 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11581 query : function(property, value, anyMatch){
11582 var fn = this.createFilterFn(property, value, anyMatch);
11583 return fn ? this.queryBy(fn) : this.data.clone();
11587 * Query by a function. The specified function will be called with each
11588 * record in this data source. If the function returns true the record is included
11590 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11591 * @param {Object} scope (optional) The scope of the function (defaults to this)
11592 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11594 queryBy : function(fn, scope){
11595 var data = this.snapshot || this.data;
11596 return data.filterBy(fn, scope||this);
11600 * Collects unique values for a particular dataIndex from this store.
11601 * @param {String} dataIndex The property to collect
11602 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11603 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11604 * @return {Array} An array of the unique values
11606 collect : function(dataIndex, allowNull, bypassFilter){
11607 var d = (bypassFilter === true && this.snapshot) ?
11608 this.snapshot.items : this.data.items;
11609 var v, sv, r = [], l = {};
11610 for(var i = 0, len = d.length; i < len; i++){
11611 v = d[i].data[dataIndex];
11613 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11622 * Revert to a view of the Record cache with no filtering applied.
11623 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11625 clearFilter : function(suppressEvent){
11626 if(this.snapshot && this.snapshot != this.data){
11627 this.data = this.snapshot;
11628 delete this.snapshot;
11629 if(suppressEvent !== true){
11630 this.fireEvent("datachanged", this);
11636 afterEdit : function(record){
11637 if(this.modified.indexOf(record) == -1){
11638 this.modified.push(record);
11640 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11644 afterReject : function(record){
11645 this.modified.remove(record);
11646 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11650 afterCommit : function(record){
11651 this.modified.remove(record);
11652 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11656 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11657 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11659 commitChanges : function(){
11660 var m = this.modified.slice(0);
11661 this.modified = [];
11662 for(var i = 0, len = m.length; i < len; i++){
11668 * Cancel outstanding changes on all changed records.
11670 rejectChanges : function(){
11671 var m = this.modified.slice(0);
11672 this.modified = [];
11673 for(var i = 0, len = m.length; i < len; i++){
11678 onMetaChange : function(meta, rtype, o){
11679 this.recordType = rtype;
11680 this.fields = rtype.prototype.fields;
11681 delete this.snapshot;
11682 this.sortInfo = meta.sortInfo || this.sortInfo;
11683 this.modified = [];
11684 this.fireEvent('metachange', this, this.reader.meta);
11687 moveIndex : function(data, type)
11689 var index = this.indexOf(data);
11691 var newIndex = index + type;
11695 this.insert(newIndex, data);
11700 * Ext JS Library 1.1.1
11701 * Copyright(c) 2006-2007, Ext JS, LLC.
11703 * Originally Released Under LGPL - original licence link has changed is not relivant.
11706 * <script type="text/javascript">
11710 * @class Roo.data.SimpleStore
11711 * @extends Roo.data.Store
11712 * Small helper class to make creating Stores from Array data easier.
11713 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11714 * @cfg {Array} fields An array of field definition objects, or field name strings.
11715 * @cfg {Array} data The multi-dimensional array of data
11717 * @param {Object} config
11719 Roo.data.SimpleStore = function(config){
11720 Roo.data.SimpleStore.superclass.constructor.call(this, {
11722 reader: new Roo.data.ArrayReader({
11725 Roo.data.Record.create(config.fields)
11727 proxy : new Roo.data.MemoryProxy(config.data)
11731 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11733 * Ext JS Library 1.1.1
11734 * Copyright(c) 2006-2007, Ext JS, LLC.
11736 * Originally Released Under LGPL - original licence link has changed is not relivant.
11739 * <script type="text/javascript">
11744 * @extends Roo.data.Store
11745 * @class Roo.data.JsonStore
11746 * Small helper class to make creating Stores for JSON data easier. <br/>
11748 var store = new Roo.data.JsonStore({
11749 url: 'get-images.php',
11751 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11754 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11755 * JsonReader and HttpProxy (unless inline data is provided).</b>
11756 * @cfg {Array} fields An array of field definition objects, or field name strings.
11758 * @param {Object} config
11760 Roo.data.JsonStore = function(c){
11761 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11762 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11763 reader: new Roo.data.JsonReader(c, c.fields)
11766 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11768 * Ext JS Library 1.1.1
11769 * Copyright(c) 2006-2007, Ext JS, LLC.
11771 * Originally Released Under LGPL - original licence link has changed is not relivant.
11774 * <script type="text/javascript">
11778 Roo.data.Field = function(config){
11779 if(typeof config == "string"){
11780 config = {name: config};
11782 Roo.apply(this, config);
11785 this.type = "auto";
11788 var st = Roo.data.SortTypes;
11789 // named sortTypes are supported, here we look them up
11790 if(typeof this.sortType == "string"){
11791 this.sortType = st[this.sortType];
11794 // set default sortType for strings and dates
11795 if(!this.sortType){
11798 this.sortType = st.asUCString;
11801 this.sortType = st.asDate;
11804 this.sortType = st.none;
11809 var stripRe = /[\$,%]/g;
11811 // prebuilt conversion function for this field, instead of
11812 // switching every time we're reading a value
11814 var cv, dateFormat = this.dateFormat;
11819 cv = function(v){ return v; };
11822 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11826 return v !== undefined && v !== null && v !== '' ?
11827 parseInt(String(v).replace(stripRe, ""), 10) : '';
11832 return v !== undefined && v !== null && v !== '' ?
11833 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11838 cv = function(v){ return v === true || v === "true" || v == 1; };
11845 if(v instanceof Date){
11849 if(dateFormat == "timestamp"){
11850 return new Date(v*1000);
11852 return Date.parseDate(v, dateFormat);
11854 var parsed = Date.parse(v);
11855 return parsed ? new Date(parsed) : null;
11864 Roo.data.Field.prototype = {
11872 * Ext JS Library 1.1.1
11873 * Copyright(c) 2006-2007, Ext JS, LLC.
11875 * Originally Released Under LGPL - original licence link has changed is not relivant.
11878 * <script type="text/javascript">
11881 // Base class for reading structured data from a data source. This class is intended to be
11882 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11885 * @class Roo.data.DataReader
11886 * Base class for reading structured data from a data source. This class is intended to be
11887 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11890 Roo.data.DataReader = function(meta, recordType){
11894 this.recordType = recordType instanceof Array ?
11895 Roo.data.Record.create(recordType) : recordType;
11898 Roo.data.DataReader.prototype = {
11900 * Create an empty record
11901 * @param {Object} data (optional) - overlay some values
11902 * @return {Roo.data.Record} record created.
11904 newRow : function(d) {
11906 this.recordType.prototype.fields.each(function(c) {
11908 case 'int' : da[c.name] = 0; break;
11909 case 'date' : da[c.name] = new Date(); break;
11910 case 'float' : da[c.name] = 0.0; break;
11911 case 'boolean' : da[c.name] = false; break;
11912 default : da[c.name] = ""; break;
11916 return new this.recordType(Roo.apply(da, d));
11921 * Ext JS Library 1.1.1
11922 * Copyright(c) 2006-2007, Ext JS, LLC.
11924 * Originally Released Under LGPL - original licence link has changed is not relivant.
11927 * <script type="text/javascript">
11931 * @class Roo.data.DataProxy
11932 * @extends Roo.data.Observable
11933 * This class is an abstract base class for implementations which provide retrieval of
11934 * unformatted data objects.<br>
11936 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11937 * (of the appropriate type which knows how to parse the data object) to provide a block of
11938 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11940 * Custom implementations must implement the load method as described in
11941 * {@link Roo.data.HttpProxy#load}.
11943 Roo.data.DataProxy = function(){
11946 * @event beforeload
11947 * Fires before a network request is made to retrieve a data object.
11948 * @param {Object} This DataProxy object.
11949 * @param {Object} params The params parameter to the load function.
11954 * Fires before the load method's callback is called.
11955 * @param {Object} This DataProxy object.
11956 * @param {Object} o The data object.
11957 * @param {Object} arg The callback argument object passed to the load function.
11961 * @event loadexception
11962 * Fires if an Exception occurs during data retrieval.
11963 * @param {Object} This DataProxy object.
11964 * @param {Object} o The data object.
11965 * @param {Object} arg The callback argument object passed to the load function.
11966 * @param {Object} e The Exception.
11968 loadexception : true
11970 Roo.data.DataProxy.superclass.constructor.call(this);
11973 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11976 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11980 * Ext JS Library 1.1.1
11981 * Copyright(c) 2006-2007, Ext JS, LLC.
11983 * Originally Released Under LGPL - original licence link has changed is not relivant.
11986 * <script type="text/javascript">
11989 * @class Roo.data.MemoryProxy
11990 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11991 * to the Reader when its load method is called.
11993 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11995 Roo.data.MemoryProxy = function(data){
11999 Roo.data.MemoryProxy.superclass.constructor.call(this);
12003 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12006 * Load data from the requested source (in this case an in-memory
12007 * data object passed to the constructor), read the data object into
12008 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12009 * process that block using the passed callback.
12010 * @param {Object} params This parameter is not used by the MemoryProxy class.
12011 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12012 * object into a block of Roo.data.Records.
12013 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12014 * The function must be passed <ul>
12015 * <li>The Record block object</li>
12016 * <li>The "arg" argument from the load function</li>
12017 * <li>A boolean success indicator</li>
12019 * @param {Object} scope The scope in which to call the callback
12020 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12022 load : function(params, reader, callback, scope, arg){
12023 params = params || {};
12026 result = reader.readRecords(this.data);
12028 this.fireEvent("loadexception", this, arg, null, e);
12029 callback.call(scope, null, arg, false);
12032 callback.call(scope, result, arg, true);
12036 update : function(params, records){
12041 * Ext JS Library 1.1.1
12042 * Copyright(c) 2006-2007, Ext JS, LLC.
12044 * Originally Released Under LGPL - original licence link has changed is not relivant.
12047 * <script type="text/javascript">
12050 * @class Roo.data.HttpProxy
12051 * @extends Roo.data.DataProxy
12052 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12053 * configured to reference a certain URL.<br><br>
12055 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12056 * from which the running page was served.<br><br>
12058 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12060 * Be aware that to enable the browser to parse an XML document, the server must set
12061 * the Content-Type header in the HTTP response to "text/xml".
12063 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12064 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12065 * will be used to make the request.
12067 Roo.data.HttpProxy = function(conn){
12068 Roo.data.HttpProxy.superclass.constructor.call(this);
12069 // is conn a conn config or a real conn?
12071 this.useAjax = !conn || !conn.events;
12075 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12076 // thse are take from connection...
12079 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12082 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12083 * extra parameters to each request made by this object. (defaults to undefined)
12086 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12087 * to each request made by this object. (defaults to undefined)
12090 * @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)
12093 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12096 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12102 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12106 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12107 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12108 * a finer-grained basis than the DataProxy events.
12110 getConnection : function(){
12111 return this.useAjax ? Roo.Ajax : this.conn;
12115 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12116 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12117 * process that block using the passed callback.
12118 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12119 * for the request to the remote server.
12120 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12121 * object into a block of Roo.data.Records.
12122 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12123 * The function must be passed <ul>
12124 * <li>The Record block object</li>
12125 * <li>The "arg" argument from the load function</li>
12126 * <li>A boolean success indicator</li>
12128 * @param {Object} scope The scope in which to call the callback
12129 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12131 load : function(params, reader, callback, scope, arg){
12132 if(this.fireEvent("beforeload", this, params) !== false){
12134 params : params || {},
12136 callback : callback,
12141 callback : this.loadResponse,
12145 Roo.applyIf(o, this.conn);
12146 if(this.activeRequest){
12147 Roo.Ajax.abort(this.activeRequest);
12149 this.activeRequest = Roo.Ajax.request(o);
12151 this.conn.request(o);
12154 callback.call(scope||this, null, arg, false);
12159 loadResponse : function(o, success, response){
12160 delete this.activeRequest;
12162 this.fireEvent("loadexception", this, o, response);
12163 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12168 result = o.reader.read(response);
12170 this.fireEvent("loadexception", this, o, response, e);
12171 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12175 this.fireEvent("load", this, o, o.request.arg);
12176 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12180 update : function(dataSet){
12185 updateResponse : function(dataSet){
12190 * Ext JS Library 1.1.1
12191 * Copyright(c) 2006-2007, Ext JS, LLC.
12193 * Originally Released Under LGPL - original licence link has changed is not relivant.
12196 * <script type="text/javascript">
12200 * @class Roo.data.ScriptTagProxy
12201 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12202 * other than the originating domain of the running page.<br><br>
12204 * <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
12205 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12207 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12208 * source code that is used as the source inside a <script> tag.<br><br>
12210 * In order for the browser to process the returned data, the server must wrap the data object
12211 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12212 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12213 * depending on whether the callback name was passed:
12216 boolean scriptTag = false;
12217 String cb = request.getParameter("callback");
12220 response.setContentType("text/javascript");
12222 response.setContentType("application/x-json");
12224 Writer out = response.getWriter();
12226 out.write(cb + "(");
12228 out.print(dataBlock.toJsonString());
12235 * @param {Object} config A configuration object.
12237 Roo.data.ScriptTagProxy = function(config){
12238 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12239 Roo.apply(this, config);
12240 this.head = document.getElementsByTagName("head")[0];
12243 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12245 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12247 * @cfg {String} url The URL from which to request the data object.
12250 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12254 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12255 * the server the name of the callback function set up by the load call to process the returned data object.
12256 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12257 * javascript output which calls this named function passing the data object as its only parameter.
12259 callbackParam : "callback",
12261 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12262 * name to the request.
12267 * Load data from the configured URL, read the data object into
12268 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12269 * process that block using the passed callback.
12270 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12271 * for the request to the remote server.
12272 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12273 * object into a block of Roo.data.Records.
12274 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12275 * The function must be passed <ul>
12276 * <li>The Record block object</li>
12277 * <li>The "arg" argument from the load function</li>
12278 * <li>A boolean success indicator</li>
12280 * @param {Object} scope The scope in which to call the callback
12281 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12283 load : function(params, reader, callback, scope, arg){
12284 if(this.fireEvent("beforeload", this, params) !== false){
12286 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12288 var url = this.url;
12289 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12291 url += "&_dc=" + (new Date().getTime());
12293 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12296 cb : "stcCallback"+transId,
12297 scriptId : "stcScript"+transId,
12301 callback : callback,
12307 window[trans.cb] = function(o){
12308 conn.handleResponse(o, trans);
12311 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12313 if(this.autoAbort !== false){
12317 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12319 var script = document.createElement("script");
12320 script.setAttribute("src", url);
12321 script.setAttribute("type", "text/javascript");
12322 script.setAttribute("id", trans.scriptId);
12323 this.head.appendChild(script);
12325 this.trans = trans;
12327 callback.call(scope||this, null, arg, false);
12332 isLoading : function(){
12333 return this.trans ? true : false;
12337 * Abort the current server request.
12339 abort : function(){
12340 if(this.isLoading()){
12341 this.destroyTrans(this.trans);
12346 destroyTrans : function(trans, isLoaded){
12347 this.head.removeChild(document.getElementById(trans.scriptId));
12348 clearTimeout(trans.timeoutId);
12350 window[trans.cb] = undefined;
12352 delete window[trans.cb];
12355 // if hasn't been loaded, wait for load to remove it to prevent script error
12356 window[trans.cb] = function(){
12357 window[trans.cb] = undefined;
12359 delete window[trans.cb];
12366 handleResponse : function(o, trans){
12367 this.trans = false;
12368 this.destroyTrans(trans, true);
12371 result = trans.reader.readRecords(o);
12373 this.fireEvent("loadexception", this, o, trans.arg, e);
12374 trans.callback.call(trans.scope||window, null, trans.arg, false);
12377 this.fireEvent("load", this, o, trans.arg);
12378 trans.callback.call(trans.scope||window, result, trans.arg, true);
12382 handleFailure : function(trans){
12383 this.trans = false;
12384 this.destroyTrans(trans, false);
12385 this.fireEvent("loadexception", this, null, trans.arg);
12386 trans.callback.call(trans.scope||window, null, trans.arg, false);
12390 * Ext JS Library 1.1.1
12391 * Copyright(c) 2006-2007, Ext JS, LLC.
12393 * Originally Released Under LGPL - original licence link has changed is not relivant.
12396 * <script type="text/javascript">
12400 * @class Roo.data.JsonReader
12401 * @extends Roo.data.DataReader
12402 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12403 * based on mappings in a provided Roo.data.Record constructor.
12405 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12406 * in the reply previously.
12411 var RecordDef = Roo.data.Record.create([
12412 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12413 {name: 'occupation'} // This field will use "occupation" as the mapping.
12415 var myReader = new Roo.data.JsonReader({
12416 totalProperty: "results", // The property which contains the total dataset size (optional)
12417 root: "rows", // The property which contains an Array of row objects
12418 id: "id" // The property within each row object that provides an ID for the record (optional)
12422 * This would consume a JSON file like this:
12424 { 'results': 2, 'rows': [
12425 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12426 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12429 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12430 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12431 * paged from the remote server.
12432 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12433 * @cfg {String} root name of the property which contains the Array of row objects.
12434 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12435 * @cfg {Array} fields Array of field definition objects
12437 * Create a new JsonReader
12438 * @param {Object} meta Metadata configuration options
12439 * @param {Object} recordType Either an Array of field definition objects,
12440 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12442 Roo.data.JsonReader = function(meta, recordType){
12445 // set some defaults:
12446 Roo.applyIf(meta, {
12447 totalProperty: 'total',
12448 successProperty : 'success',
12453 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12455 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12458 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12459 * Used by Store query builder to append _requestMeta to params.
12462 metaFromRemote : false,
12464 * This method is only used by a DataProxy which has retrieved data from a remote server.
12465 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12466 * @return {Object} data A data block which is used by an Roo.data.Store object as
12467 * a cache of Roo.data.Records.
12469 read : function(response){
12470 var json = response.responseText;
12472 var o = /* eval:var:o */ eval("("+json+")");
12474 throw {message: "JsonReader.read: Json object not found"};
12480 this.metaFromRemote = true;
12481 this.meta = o.metaData;
12482 this.recordType = Roo.data.Record.create(o.metaData.fields);
12483 this.onMetaChange(this.meta, this.recordType, o);
12485 return this.readRecords(o);
12488 // private function a store will implement
12489 onMetaChange : function(meta, recordType, o){
12496 simpleAccess: function(obj, subsc) {
12503 getJsonAccessor: function(){
12505 return function(expr) {
12507 return(re.test(expr))
12508 ? new Function("obj", "return obj." + expr)
12513 return Roo.emptyFn;
12518 * Create a data block containing Roo.data.Records from an XML document.
12519 * @param {Object} o An object which contains an Array of row objects in the property specified
12520 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12521 * which contains the total size of the dataset.
12522 * @return {Object} data A data block which is used by an Roo.data.Store object as
12523 * a cache of Roo.data.Records.
12525 readRecords : function(o){
12527 * After any data loads, the raw JSON data is available for further custom processing.
12531 var s = this.meta, Record = this.recordType,
12532 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12534 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12536 if(s.totalProperty) {
12537 this.getTotal = this.getJsonAccessor(s.totalProperty);
12539 if(s.successProperty) {
12540 this.getSuccess = this.getJsonAccessor(s.successProperty);
12542 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12544 var g = this.getJsonAccessor(s.id);
12545 this.getId = function(rec) {
12547 return (r === undefined || r === "") ? null : r;
12550 this.getId = function(){return null;};
12553 for(var jj = 0; jj < fl; jj++){
12555 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12556 this.ef[jj] = this.getJsonAccessor(map);
12560 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12561 if(s.totalProperty){
12562 var vt = parseInt(this.getTotal(o), 10);
12567 if(s.successProperty){
12568 var vs = this.getSuccess(o);
12569 if(vs === false || vs === 'false'){
12574 for(var i = 0; i < c; i++){
12577 var id = this.getId(n);
12578 for(var j = 0; j < fl; j++){
12580 var v = this.ef[j](n);
12582 Roo.log('missing convert for ' + f.name);
12586 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12588 var record = new Record(values, id);
12590 records[i] = record;
12596 totalRecords : totalRecords
12601 * Ext JS Library 1.1.1
12602 * Copyright(c) 2006-2007, Ext JS, LLC.
12604 * Originally Released Under LGPL - original licence link has changed is not relivant.
12607 * <script type="text/javascript">
12611 * @class Roo.data.ArrayReader
12612 * @extends Roo.data.DataReader
12613 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12614 * Each element of that Array represents a row of data fields. The
12615 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12616 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12620 var RecordDef = Roo.data.Record.create([
12621 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12622 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12624 var myReader = new Roo.data.ArrayReader({
12625 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12629 * This would consume an Array like this:
12631 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12633 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12635 * Create a new JsonReader
12636 * @param {Object} meta Metadata configuration options.
12637 * @param {Object} recordType Either an Array of field definition objects
12638 * as specified to {@link Roo.data.Record#create},
12639 * or an {@link Roo.data.Record} object
12640 * created using {@link Roo.data.Record#create}.
12642 Roo.data.ArrayReader = function(meta, recordType){
12643 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12646 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12648 * Create a data block containing Roo.data.Records from an XML document.
12649 * @param {Object} o An Array of row objects which represents the dataset.
12650 * @return {Object} data A data block which is used by an Roo.data.Store object as
12651 * a cache of Roo.data.Records.
12653 readRecords : function(o){
12654 var sid = this.meta ? this.meta.id : null;
12655 var recordType = this.recordType, fields = recordType.prototype.fields;
12658 for(var i = 0; i < root.length; i++){
12661 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12662 for(var j = 0, jlen = fields.length; j < jlen; j++){
12663 var f = fields.items[j];
12664 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12665 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12667 values[f.name] = v;
12669 var record = new recordType(values, id);
12671 records[records.length] = record;
12675 totalRecords : records.length
12684 * @class Roo.bootstrap.ComboBox
12685 * @extends Roo.bootstrap.TriggerField
12686 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12687 * @cfg {Boolean} append (true|false) default false
12688 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12689 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12690 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12691 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12692 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12693 * @cfg {Boolean} animate default true
12694 * @cfg {Boolean} emptyResultText only for touch device
12695 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12696 * @cfg {String} emptyTitle default ''
12698 * Create a new ComboBox.
12699 * @param {Object} config Configuration options
12701 Roo.bootstrap.ComboBox = function(config){
12702 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12706 * Fires when the dropdown list is expanded
12707 * @param {Roo.bootstrap.ComboBox} combo This combo box
12712 * Fires when the dropdown list is collapsed
12713 * @param {Roo.bootstrap.ComboBox} combo This combo box
12717 * @event beforeselect
12718 * Fires before a list item is selected. Return false to cancel the selection.
12719 * @param {Roo.bootstrap.ComboBox} combo This combo box
12720 * @param {Roo.data.Record} record The data record returned from the underlying store
12721 * @param {Number} index The index of the selected item in the dropdown list
12723 'beforeselect' : true,
12726 * Fires when a list item is selected
12727 * @param {Roo.bootstrap.ComboBox} combo This combo box
12728 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12729 * @param {Number} index The index of the selected item in the dropdown list
12733 * @event beforequery
12734 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12735 * The event object passed has these properties:
12736 * @param {Roo.bootstrap.ComboBox} combo This combo box
12737 * @param {String} query The query
12738 * @param {Boolean} forceAll true to force "all" query
12739 * @param {Boolean} cancel true to cancel the query
12740 * @param {Object} e The query event object
12742 'beforequery': true,
12745 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12746 * @param {Roo.bootstrap.ComboBox} combo This combo box
12751 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12752 * @param {Roo.bootstrap.ComboBox} combo This combo box
12753 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12758 * Fires when the remove value from the combobox array
12759 * @param {Roo.bootstrap.ComboBox} combo This combo box
12763 * @event afterremove
12764 * Fires when the remove value from the combobox array
12765 * @param {Roo.bootstrap.ComboBox} combo This combo box
12767 'afterremove' : true,
12769 * @event specialfilter
12770 * Fires when specialfilter
12771 * @param {Roo.bootstrap.ComboBox} combo This combo box
12773 'specialfilter' : true,
12776 * Fires when tick the element
12777 * @param {Roo.bootstrap.ComboBox} combo This combo box
12781 * @event touchviewdisplay
12782 * Fires when touch view require special display (default is using displayField)
12783 * @param {Roo.bootstrap.ComboBox} combo This combo box
12784 * @param {Object} cfg set html .
12786 'touchviewdisplay' : true
12791 this.tickItems = [];
12793 this.selectedIndex = -1;
12794 if(this.mode == 'local'){
12795 if(config.queryDelay === undefined){
12796 this.queryDelay = 10;
12798 if(config.minChars === undefined){
12804 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12807 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12808 * rendering into an Roo.Editor, defaults to false)
12811 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12812 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12815 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12818 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12819 * the dropdown list (defaults to undefined, with no header element)
12823 * @cfg {String/Roo.Template} tpl The template to use to render the output
12827 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12829 listWidth: undefined,
12831 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12832 * mode = 'remote' or 'text' if mode = 'local')
12834 displayField: undefined,
12837 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12838 * mode = 'remote' or 'value' if mode = 'local').
12839 * Note: use of a valueField requires the user make a selection
12840 * in order for a value to be mapped.
12842 valueField: undefined,
12844 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12849 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12850 * field's data value (defaults to the underlying DOM element's name)
12852 hiddenName: undefined,
12854 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12858 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12860 selectedClass: 'active',
12863 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12867 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12868 * anchor positions (defaults to 'tl-bl')
12870 listAlign: 'tl-bl?',
12872 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12876 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12877 * query specified by the allQuery config option (defaults to 'query')
12879 triggerAction: 'query',
12881 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12882 * (defaults to 4, does not apply if editable = false)
12886 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12887 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12891 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12892 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12896 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12897 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12901 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12902 * when editable = true (defaults to false)
12904 selectOnFocus:false,
12906 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12908 queryParam: 'query',
12910 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12911 * when mode = 'remote' (defaults to 'Loading...')
12913 loadingText: 'Loading...',
12915 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12919 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12923 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12924 * traditional select (defaults to true)
12928 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12932 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12936 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12937 * listWidth has a higher value)
12941 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12942 * allow the user to set arbitrary text into the field (defaults to false)
12944 forceSelection:false,
12946 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12947 * if typeAhead = true (defaults to 250)
12949 typeAheadDelay : 250,
12951 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12952 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12954 valueNotFoundText : undefined,
12956 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12958 blockFocus : false,
12961 * @cfg {Boolean} disableClear Disable showing of clear button.
12963 disableClear : false,
12965 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12967 alwaysQuery : false,
12970 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12975 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12977 invalidClass : "has-warning",
12980 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12982 validClass : "has-success",
12985 * @cfg {Boolean} specialFilter (true|false) special filter default false
12987 specialFilter : false,
12990 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12992 mobileTouchView : true,
12995 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12997 useNativeIOS : false,
12999 ios_options : false,
13011 btnPosition : 'right',
13012 triggerList : true,
13013 showToggleBtn : true,
13015 emptyResultText: 'Empty',
13016 triggerText : 'Select',
13019 // element that contains real text value.. (when hidden is used..)
13021 getAutoCreate : function()
13026 * Render classic select for iso
13029 if(Roo.isIOS && this.useNativeIOS){
13030 cfg = this.getAutoCreateNativeIOS();
13038 if(Roo.isTouch && this.mobileTouchView){
13039 cfg = this.getAutoCreateTouchView();
13046 if(!this.tickable){
13047 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13052 * ComboBox with tickable selections
13055 var align = this.labelAlign || this.parentLabelAlign();
13058 cls : 'form-group roo-combobox-tickable' //input-group
13061 var btn_text_select = '';
13062 var btn_text_done = '';
13063 var btn_text_cancel = '';
13065 if (this.btn_text_show) {
13066 btn_text_select = 'Select';
13067 btn_text_done = 'Done';
13068 btn_text_cancel = 'Cancel';
13073 cls : 'tickable-buttons',
13078 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13079 //html : this.triggerText
13080 html: btn_text_select
13086 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13088 html: btn_text_done
13094 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13096 html: btn_text_cancel
13102 buttons.cn.unshift({
13104 cls: 'roo-select2-search-field-input'
13110 Roo.each(buttons.cn, function(c){
13112 c.cls += ' btn-' + _this.size;
13115 if (_this.disabled) {
13126 cls: 'form-hidden-field'
13130 cls: 'roo-select2-choices',
13134 cls: 'roo-select2-search-field',
13145 cls: 'roo-select2-container input-group roo-select2-container-multi',
13150 // cls: 'typeahead typeahead-long dropdown-menu',
13151 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13156 if(this.hasFeedback && !this.allowBlank){
13160 cls: 'glyphicon form-control-feedback'
13163 combobox.cn.push(feedback);
13167 if (align ==='left' && this.fieldLabel.length) {
13169 cfg.cls += ' roo-form-group-label-left';
13174 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13175 tooltip : 'This field is required'
13180 cls : 'control-label',
13181 html : this.fieldLabel
13193 var labelCfg = cfg.cn[1];
13194 var contentCfg = cfg.cn[2];
13197 if(this.indicatorpos == 'right'){
13203 cls : 'control-label',
13207 html : this.fieldLabel
13211 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13212 tooltip : 'This field is required'
13227 labelCfg = cfg.cn[0];
13228 contentCfg = cfg.cn[1];
13232 if(this.labelWidth > 12){
13233 labelCfg.style = "width: " + this.labelWidth + 'px';
13236 if(this.labelWidth < 13 && this.labelmd == 0){
13237 this.labelmd = this.labelWidth;
13240 if(this.labellg > 0){
13241 labelCfg.cls += ' col-lg-' + this.labellg;
13242 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13245 if(this.labelmd > 0){
13246 labelCfg.cls += ' col-md-' + this.labelmd;
13247 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13250 if(this.labelsm > 0){
13251 labelCfg.cls += ' col-sm-' + this.labelsm;
13252 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13255 if(this.labelxs > 0){
13256 labelCfg.cls += ' col-xs-' + this.labelxs;
13257 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13261 } else if ( this.fieldLabel.length) {
13262 // Roo.log(" label");
13266 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13267 tooltip : 'This field is required'
13271 //cls : 'input-group-addon',
13272 html : this.fieldLabel
13277 if(this.indicatorpos == 'right'){
13281 //cls : 'input-group-addon',
13282 html : this.fieldLabel
13286 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13287 tooltip : 'This field is required'
13296 // Roo.log(" no label && no align");
13303 ['xs','sm','md','lg'].map(function(size){
13304 if (settings[size]) {
13305 cfg.cls += ' col-' + size + '-' + settings[size];
13313 _initEventsCalled : false,
13316 initEvents: function()
13318 if (this._initEventsCalled) { // as we call render... prevent looping...
13321 this._initEventsCalled = true;
13324 throw "can not find store for combo";
13327 this.indicator = this.indicatorEl();
13329 this.store = Roo.factory(this.store, Roo.data);
13330 this.store.parent = this;
13332 // if we are building from html. then this element is so complex, that we can not really
13333 // use the rendered HTML.
13334 // so we have to trash and replace the previous code.
13335 if (Roo.XComponent.build_from_html) {
13336 // remove this element....
13337 var e = this.el.dom, k=0;
13338 while (e ) { e = e.previousSibling; ++k;}
13343 this.rendered = false;
13345 this.render(this.parent().getChildContainer(true), k);
13348 if(Roo.isIOS && this.useNativeIOS){
13349 this.initIOSView();
13357 if(Roo.isTouch && this.mobileTouchView){
13358 this.initTouchView();
13363 this.initTickableEvents();
13367 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13369 if(this.hiddenName){
13371 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13373 this.hiddenField.dom.value =
13374 this.hiddenValue !== undefined ? this.hiddenValue :
13375 this.value !== undefined ? this.value : '';
13377 // prevent input submission
13378 this.el.dom.removeAttribute('name');
13379 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13384 // this.el.dom.setAttribute('autocomplete', 'off');
13387 var cls = 'x-combo-list';
13389 //this.list = new Roo.Layer({
13390 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13396 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13397 _this.list.setWidth(lw);
13400 this.list.on('mouseover', this.onViewOver, this);
13401 this.list.on('mousemove', this.onViewMove, this);
13402 this.list.on('scroll', this.onViewScroll, this);
13405 this.list.swallowEvent('mousewheel');
13406 this.assetHeight = 0;
13409 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13410 this.assetHeight += this.header.getHeight();
13413 this.innerList = this.list.createChild({cls:cls+'-inner'});
13414 this.innerList.on('mouseover', this.onViewOver, this);
13415 this.innerList.on('mousemove', this.onViewMove, this);
13416 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13418 if(this.allowBlank && !this.pageSize && !this.disableClear){
13419 this.footer = this.list.createChild({cls:cls+'-ft'});
13420 this.pageTb = new Roo.Toolbar(this.footer);
13424 this.footer = this.list.createChild({cls:cls+'-ft'});
13425 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13426 {pageSize: this.pageSize});
13430 if (this.pageTb && this.allowBlank && !this.disableClear) {
13432 this.pageTb.add(new Roo.Toolbar.Fill(), {
13433 cls: 'x-btn-icon x-btn-clear',
13435 handler: function()
13438 _this.clearValue();
13439 _this.onSelect(false, -1);
13444 this.assetHeight += this.footer.getHeight();
13449 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13452 this.view = new Roo.View(this.list, this.tpl, {
13453 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13455 //this.view.wrapEl.setDisplayed(false);
13456 this.view.on('click', this.onViewClick, this);
13459 this.store.on('beforeload', this.onBeforeLoad, this);
13460 this.store.on('load', this.onLoad, this);
13461 this.store.on('loadexception', this.onLoadException, this);
13463 if(this.resizable){
13464 this.resizer = new Roo.Resizable(this.list, {
13465 pinned:true, handles:'se'
13467 this.resizer.on('resize', function(r, w, h){
13468 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13469 this.listWidth = w;
13470 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13471 this.restrictHeight();
13473 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13476 if(!this.editable){
13477 this.editable = true;
13478 this.setEditable(false);
13483 if (typeof(this.events.add.listeners) != 'undefined') {
13485 this.addicon = this.wrap.createChild(
13486 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13488 this.addicon.on('click', function(e) {
13489 this.fireEvent('add', this);
13492 if (typeof(this.events.edit.listeners) != 'undefined') {
13494 this.editicon = this.wrap.createChild(
13495 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13496 if (this.addicon) {
13497 this.editicon.setStyle('margin-left', '40px');
13499 this.editicon.on('click', function(e) {
13501 // we fire even if inothing is selected..
13502 this.fireEvent('edit', this, this.lastData );
13508 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13509 "up" : function(e){
13510 this.inKeyMode = true;
13514 "down" : function(e){
13515 if(!this.isExpanded()){
13516 this.onTriggerClick();
13518 this.inKeyMode = true;
13523 "enter" : function(e){
13524 // this.onViewClick();
13528 if(this.fireEvent("specialkey", this, e)){
13529 this.onViewClick(false);
13535 "esc" : function(e){
13539 "tab" : function(e){
13542 if(this.fireEvent("specialkey", this, e)){
13543 this.onViewClick(false);
13551 doRelay : function(foo, bar, hname){
13552 if(hname == 'down' || this.scope.isExpanded()){
13553 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13562 this.queryDelay = Math.max(this.queryDelay || 10,
13563 this.mode == 'local' ? 10 : 250);
13566 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13568 if(this.typeAhead){
13569 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13571 if(this.editable !== false){
13572 this.inputEl().on("keyup", this.onKeyUp, this);
13574 if(this.forceSelection){
13575 this.inputEl().on('blur', this.doForce, this);
13579 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13580 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13584 initTickableEvents: function()
13588 if(this.hiddenName){
13590 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13592 this.hiddenField.dom.value =
13593 this.hiddenValue !== undefined ? this.hiddenValue :
13594 this.value !== undefined ? this.value : '';
13596 // prevent input submission
13597 this.el.dom.removeAttribute('name');
13598 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13603 // this.list = this.el.select('ul.dropdown-menu',true).first();
13605 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13606 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13607 if(this.triggerList){
13608 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13611 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13612 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13614 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13615 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13617 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13618 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13620 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13621 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13622 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13625 this.cancelBtn.hide();
13630 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13631 _this.list.setWidth(lw);
13634 this.list.on('mouseover', this.onViewOver, this);
13635 this.list.on('mousemove', this.onViewMove, this);
13637 this.list.on('scroll', this.onViewScroll, this);
13640 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13641 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13644 this.view = new Roo.View(this.list, this.tpl, {
13649 selectedClass: this.selectedClass
13652 //this.view.wrapEl.setDisplayed(false);
13653 this.view.on('click', this.onViewClick, this);
13657 this.store.on('beforeload', this.onBeforeLoad, this);
13658 this.store.on('load', this.onLoad, this);
13659 this.store.on('loadexception', this.onLoadException, this);
13662 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13663 "up" : function(e){
13664 this.inKeyMode = true;
13668 "down" : function(e){
13669 this.inKeyMode = true;
13673 "enter" : function(e){
13674 if(this.fireEvent("specialkey", this, e)){
13675 this.onViewClick(false);
13681 "esc" : function(e){
13682 this.onTickableFooterButtonClick(e, false, false);
13685 "tab" : function(e){
13686 this.fireEvent("specialkey", this, e);
13688 this.onTickableFooterButtonClick(e, false, false);
13695 doRelay : function(e, fn, key){
13696 if(this.scope.isExpanded()){
13697 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13706 this.queryDelay = Math.max(this.queryDelay || 10,
13707 this.mode == 'local' ? 10 : 250);
13710 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13712 if(this.typeAhead){
13713 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13716 if(this.editable !== false){
13717 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13720 this.indicator = this.indicatorEl();
13722 if(this.indicator){
13723 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13724 this.indicator.hide();
13729 onDestroy : function(){
13731 this.view.setStore(null);
13732 this.view.el.removeAllListeners();
13733 this.view.el.remove();
13734 this.view.purgeListeners();
13737 this.list.dom.innerHTML = '';
13741 this.store.un('beforeload', this.onBeforeLoad, this);
13742 this.store.un('load', this.onLoad, this);
13743 this.store.un('loadexception', this.onLoadException, this);
13745 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13749 fireKey : function(e){
13750 if(e.isNavKeyPress() && !this.list.isVisible()){
13751 this.fireEvent("specialkey", this, e);
13756 onResize: function(w, h){
13757 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13759 // if(typeof w != 'number'){
13760 // // we do not handle it!?!?
13763 // var tw = this.trigger.getWidth();
13764 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13765 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13767 // this.inputEl().setWidth( this.adjustWidth('input', x));
13769 // //this.trigger.setStyle('left', x+'px');
13771 // if(this.list && this.listWidth === undefined){
13772 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13773 // this.list.setWidth(lw);
13774 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13782 * Allow or prevent the user from directly editing the field text. If false is passed,
13783 * the user will only be able to select from the items defined in the dropdown list. This method
13784 * is the runtime equivalent of setting the 'editable' config option at config time.
13785 * @param {Boolean} value True to allow the user to directly edit the field text
13787 setEditable : function(value){
13788 if(value == this.editable){
13791 this.editable = value;
13793 this.inputEl().dom.setAttribute('readOnly', true);
13794 this.inputEl().on('mousedown', this.onTriggerClick, this);
13795 this.inputEl().addClass('x-combo-noedit');
13797 this.inputEl().dom.setAttribute('readOnly', false);
13798 this.inputEl().un('mousedown', this.onTriggerClick, this);
13799 this.inputEl().removeClass('x-combo-noedit');
13805 onBeforeLoad : function(combo,opts){
13806 if(!this.hasFocus){
13810 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13812 this.restrictHeight();
13813 this.selectedIndex = -1;
13817 onLoad : function(){
13819 this.hasQuery = false;
13821 if(!this.hasFocus){
13825 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13826 this.loading.hide();
13829 if(this.store.getCount() > 0){
13832 this.restrictHeight();
13833 if(this.lastQuery == this.allQuery){
13834 if(this.editable && !this.tickable){
13835 this.inputEl().dom.select();
13839 !this.selectByValue(this.value, true) &&
13842 !this.store.lastOptions ||
13843 typeof(this.store.lastOptions.add) == 'undefined' ||
13844 this.store.lastOptions.add != true
13847 this.select(0, true);
13850 if(this.autoFocus){
13853 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13854 this.taTask.delay(this.typeAheadDelay);
13858 this.onEmptyResults();
13864 onLoadException : function()
13866 this.hasQuery = false;
13868 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13869 this.loading.hide();
13872 if(this.tickable && this.editable){
13877 // only causes errors at present
13878 //Roo.log(this.store.reader.jsonData);
13879 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13881 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13887 onTypeAhead : function(){
13888 if(this.store.getCount() > 0){
13889 var r = this.store.getAt(0);
13890 var newValue = r.data[this.displayField];
13891 var len = newValue.length;
13892 var selStart = this.getRawValue().length;
13894 if(selStart != len){
13895 this.setRawValue(newValue);
13896 this.selectText(selStart, newValue.length);
13902 onSelect : function(record, index){
13904 if(this.fireEvent('beforeselect', this, record, index) !== false){
13906 this.setFromData(index > -1 ? record.data : false);
13909 this.fireEvent('select', this, record, index);
13914 * Returns the currently selected field value or empty string if no value is set.
13915 * @return {String} value The selected value
13917 getValue : function()
13919 if(Roo.isIOS && this.useNativeIOS){
13920 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13924 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13927 if(this.valueField){
13928 return typeof this.value != 'undefined' ? this.value : '';
13930 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13934 getRawValue : function()
13936 if(Roo.isIOS && this.useNativeIOS){
13937 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13940 var v = this.inputEl().getValue();
13946 * Clears any text/value currently set in the field
13948 clearValue : function(){
13950 if(this.hiddenField){
13951 this.hiddenField.dom.value = '';
13954 this.setRawValue('');
13955 this.lastSelectionText = '';
13956 this.lastData = false;
13958 var close = this.closeTriggerEl();
13969 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13970 * will be displayed in the field. If the value does not match the data value of an existing item,
13971 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13972 * Otherwise the field will be blank (although the value will still be set).
13973 * @param {String} value The value to match
13975 setValue : function(v)
13977 if(Roo.isIOS && this.useNativeIOS){
13978 this.setIOSValue(v);
13988 if(this.valueField){
13989 var r = this.findRecord(this.valueField, v);
13991 text = r.data[this.displayField];
13992 }else if(this.valueNotFoundText !== undefined){
13993 text = this.valueNotFoundText;
13996 this.lastSelectionText = text;
13997 if(this.hiddenField){
13998 this.hiddenField.dom.value = v;
14000 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14003 var close = this.closeTriggerEl();
14006 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14012 * @property {Object} the last set data for the element
14017 * Sets the value of the field based on a object which is related to the record format for the store.
14018 * @param {Object} value the value to set as. or false on reset?
14020 setFromData : function(o){
14027 var dv = ''; // display value
14028 var vv = ''; // value value..
14030 if (this.displayField) {
14031 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14033 // this is an error condition!!!
14034 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14037 if(this.valueField){
14038 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14041 var close = this.closeTriggerEl();
14044 if(dv.length || vv * 1 > 0){
14046 this.blockFocus=true;
14052 if(this.hiddenField){
14053 this.hiddenField.dom.value = vv;
14055 this.lastSelectionText = dv;
14056 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14060 // no hidden field.. - we store the value in 'value', but still display
14061 // display field!!!!
14062 this.lastSelectionText = dv;
14063 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14070 reset : function(){
14071 // overridden so that last data is reset..
14078 this.setValue(this.originalValue);
14079 //this.clearInvalid();
14080 this.lastData = false;
14082 this.view.clearSelections();
14088 findRecord : function(prop, value){
14090 if(this.store.getCount() > 0){
14091 this.store.each(function(r){
14092 if(r.data[prop] == value){
14102 getName: function()
14104 // returns hidden if it's set..
14105 if (!this.rendered) {return ''};
14106 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14110 onViewMove : function(e, t){
14111 this.inKeyMode = false;
14115 onViewOver : function(e, t){
14116 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14119 var item = this.view.findItemFromChild(t);
14122 var index = this.view.indexOf(item);
14123 this.select(index, false);
14128 onViewClick : function(view, doFocus, el, e)
14130 var index = this.view.getSelectedIndexes()[0];
14132 var r = this.store.getAt(index);
14136 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14143 Roo.each(this.tickItems, function(v,k){
14145 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14147 _this.tickItems.splice(k, 1);
14149 if(typeof(e) == 'undefined' && view == false){
14150 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14162 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14163 this.tickItems.push(r.data);
14166 if(typeof(e) == 'undefined' && view == false){
14167 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14174 this.onSelect(r, index);
14176 if(doFocus !== false && !this.blockFocus){
14177 this.inputEl().focus();
14182 restrictHeight : function(){
14183 //this.innerList.dom.style.height = '';
14184 //var inner = this.innerList.dom;
14185 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14186 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14187 //this.list.beginUpdate();
14188 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14189 this.list.alignTo(this.inputEl(), this.listAlign);
14190 this.list.alignTo(this.inputEl(), this.listAlign);
14191 //this.list.endUpdate();
14195 onEmptyResults : function(){
14197 if(this.tickable && this.editable){
14198 this.hasFocus = false;
14199 this.restrictHeight();
14207 * Returns true if the dropdown list is expanded, else false.
14209 isExpanded : function(){
14210 return this.list.isVisible();
14214 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14215 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14216 * @param {String} value The data value of the item to select
14217 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14218 * selected item if it is not currently in view (defaults to true)
14219 * @return {Boolean} True if the value matched an item in the list, else false
14221 selectByValue : function(v, scrollIntoView){
14222 if(v !== undefined && v !== null){
14223 var r = this.findRecord(this.valueField || this.displayField, v);
14225 this.select(this.store.indexOf(r), scrollIntoView);
14233 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14234 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14235 * @param {Number} index The zero-based index of the list item to select
14236 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14237 * selected item if it is not currently in view (defaults to true)
14239 select : function(index, scrollIntoView){
14240 this.selectedIndex = index;
14241 this.view.select(index);
14242 if(scrollIntoView !== false){
14243 var el = this.view.getNode(index);
14245 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14248 this.list.scrollChildIntoView(el, false);
14254 selectNext : function(){
14255 var ct = this.store.getCount();
14257 if(this.selectedIndex == -1){
14259 }else if(this.selectedIndex < ct-1){
14260 this.select(this.selectedIndex+1);
14266 selectPrev : function(){
14267 var ct = this.store.getCount();
14269 if(this.selectedIndex == -1){
14271 }else if(this.selectedIndex != 0){
14272 this.select(this.selectedIndex-1);
14278 onKeyUp : function(e){
14279 if(this.editable !== false && !e.isSpecialKey()){
14280 this.lastKey = e.getKey();
14281 this.dqTask.delay(this.queryDelay);
14286 validateBlur : function(){
14287 return !this.list || !this.list.isVisible();
14291 initQuery : function(){
14293 var v = this.getRawValue();
14295 if(this.tickable && this.editable){
14296 v = this.tickableInputEl().getValue();
14303 doForce : function(){
14304 if(this.inputEl().dom.value.length > 0){
14305 this.inputEl().dom.value =
14306 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14312 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14313 * query allowing the query action to be canceled if needed.
14314 * @param {String} query The SQL query to execute
14315 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14316 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14317 * saved in the current store (defaults to false)
14319 doQuery : function(q, forceAll){
14321 if(q === undefined || q === null){
14326 forceAll: forceAll,
14330 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14335 forceAll = qe.forceAll;
14336 if(forceAll === true || (q.length >= this.minChars)){
14338 this.hasQuery = true;
14340 if(this.lastQuery != q || this.alwaysQuery){
14341 this.lastQuery = q;
14342 if(this.mode == 'local'){
14343 this.selectedIndex = -1;
14345 this.store.clearFilter();
14348 if(this.specialFilter){
14349 this.fireEvent('specialfilter', this);
14354 this.store.filter(this.displayField, q);
14357 this.store.fireEvent("datachanged", this.store);
14364 this.store.baseParams[this.queryParam] = q;
14366 var options = {params : this.getParams(q)};
14369 options.add = true;
14370 options.params.start = this.page * this.pageSize;
14373 this.store.load(options);
14376 * this code will make the page width larger, at the beginning, the list not align correctly,
14377 * we should expand the list on onLoad
14378 * so command out it
14383 this.selectedIndex = -1;
14388 this.loadNext = false;
14392 getParams : function(q){
14394 //p[this.queryParam] = q;
14398 p.limit = this.pageSize;
14404 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14406 collapse : function(){
14407 if(!this.isExpanded()){
14413 this.hasFocus = false;
14417 this.cancelBtn.hide();
14418 this.trigger.show();
14421 this.tickableInputEl().dom.value = '';
14422 this.tickableInputEl().blur();
14427 Roo.get(document).un('mousedown', this.collapseIf, this);
14428 Roo.get(document).un('mousewheel', this.collapseIf, this);
14429 if (!this.editable) {
14430 Roo.get(document).un('keydown', this.listKeyPress, this);
14432 this.fireEvent('collapse', this);
14438 collapseIf : function(e){
14439 var in_combo = e.within(this.el);
14440 var in_list = e.within(this.list);
14441 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14443 if (in_combo || in_list || is_list) {
14444 //e.stopPropagation();
14449 this.onTickableFooterButtonClick(e, false, false);
14457 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14459 expand : function(){
14461 if(this.isExpanded() || !this.hasFocus){
14465 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14466 this.list.setWidth(lw);
14472 this.restrictHeight();
14476 this.tickItems = Roo.apply([], this.item);
14479 this.cancelBtn.show();
14480 this.trigger.hide();
14483 this.tickableInputEl().focus();
14488 Roo.get(document).on('mousedown', this.collapseIf, this);
14489 Roo.get(document).on('mousewheel', this.collapseIf, this);
14490 if (!this.editable) {
14491 Roo.get(document).on('keydown', this.listKeyPress, this);
14494 this.fireEvent('expand', this);
14498 // Implements the default empty TriggerField.onTriggerClick function
14499 onTriggerClick : function(e)
14501 Roo.log('trigger click');
14503 if(this.disabled || !this.triggerList){
14508 this.loadNext = false;
14510 if(this.isExpanded()){
14512 if (!this.blockFocus) {
14513 this.inputEl().focus();
14517 this.hasFocus = true;
14518 if(this.triggerAction == 'all') {
14519 this.doQuery(this.allQuery, true);
14521 this.doQuery(this.getRawValue());
14523 if (!this.blockFocus) {
14524 this.inputEl().focus();
14529 onTickableTriggerClick : function(e)
14536 this.loadNext = false;
14537 this.hasFocus = true;
14539 if(this.triggerAction == 'all') {
14540 this.doQuery(this.allQuery, true);
14542 this.doQuery(this.getRawValue());
14546 onSearchFieldClick : function(e)
14548 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14549 this.onTickableFooterButtonClick(e, false, false);
14553 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14558 this.loadNext = false;
14559 this.hasFocus = true;
14561 if(this.triggerAction == 'all') {
14562 this.doQuery(this.allQuery, true);
14564 this.doQuery(this.getRawValue());
14568 listKeyPress : function(e)
14570 //Roo.log('listkeypress');
14571 // scroll to first matching element based on key pres..
14572 if (e.isSpecialKey()) {
14575 var k = String.fromCharCode(e.getKey()).toUpperCase();
14578 var csel = this.view.getSelectedNodes();
14579 var cselitem = false;
14581 var ix = this.view.indexOf(csel[0]);
14582 cselitem = this.store.getAt(ix);
14583 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14589 this.store.each(function(v) {
14591 // start at existing selection.
14592 if (cselitem.id == v.id) {
14598 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14599 match = this.store.indexOf(v);
14605 if (match === false) {
14606 return true; // no more action?
14609 this.view.select(match);
14610 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14611 sn.scrollIntoView(sn.dom.parentNode, false);
14614 onViewScroll : function(e, t){
14616 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){
14620 this.hasQuery = true;
14622 this.loading = this.list.select('.loading', true).first();
14624 if(this.loading === null){
14625 this.list.createChild({
14627 cls: 'loading roo-select2-more-results roo-select2-active',
14628 html: 'Loading more results...'
14631 this.loading = this.list.select('.loading', true).first();
14633 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14635 this.loading.hide();
14638 this.loading.show();
14643 this.loadNext = true;
14645 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14650 addItem : function(o)
14652 var dv = ''; // display value
14654 if (this.displayField) {
14655 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14657 // this is an error condition!!!
14658 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14665 var choice = this.choices.createChild({
14667 cls: 'roo-select2-search-choice',
14676 cls: 'roo-select2-search-choice-close fa fa-times',
14681 }, this.searchField);
14683 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14685 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14693 this.inputEl().dom.value = '';
14698 onRemoveItem : function(e, _self, o)
14700 e.preventDefault();
14702 this.lastItem = Roo.apply([], this.item);
14704 var index = this.item.indexOf(o.data) * 1;
14707 Roo.log('not this item?!');
14711 this.item.splice(index, 1);
14716 this.fireEvent('remove', this, e);
14722 syncValue : function()
14724 if(!this.item.length){
14731 Roo.each(this.item, function(i){
14732 if(_this.valueField){
14733 value.push(i[_this.valueField]);
14740 this.value = value.join(',');
14742 if(this.hiddenField){
14743 this.hiddenField.dom.value = this.value;
14746 this.store.fireEvent("datachanged", this.store);
14751 clearItem : function()
14753 if(!this.multiple){
14759 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14767 if(this.tickable && !Roo.isTouch){
14768 this.view.refresh();
14772 inputEl: function ()
14774 if(Roo.isIOS && this.useNativeIOS){
14775 return this.el.select('select.roo-ios-select', true).first();
14778 if(Roo.isTouch && this.mobileTouchView){
14779 return this.el.select('input.form-control',true).first();
14783 return this.searchField;
14786 return this.el.select('input.form-control',true).first();
14789 onTickableFooterButtonClick : function(e, btn, el)
14791 e.preventDefault();
14793 this.lastItem = Roo.apply([], this.item);
14795 if(btn && btn.name == 'cancel'){
14796 this.tickItems = Roo.apply([], this.item);
14805 Roo.each(this.tickItems, function(o){
14813 validate : function()
14815 if(this.getVisibilityEl().hasClass('hidden')){
14819 var v = this.getRawValue();
14822 v = this.getValue();
14825 if(this.disabled || this.allowBlank || v.length){
14830 this.markInvalid();
14834 tickableInputEl : function()
14836 if(!this.tickable || !this.editable){
14837 return this.inputEl();
14840 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14844 getAutoCreateTouchView : function()
14849 cls: 'form-group' //input-group
14855 type : this.inputType,
14856 cls : 'form-control x-combo-noedit',
14857 autocomplete: 'new-password',
14858 placeholder : this.placeholder || '',
14863 input.name = this.name;
14867 input.cls += ' input-' + this.size;
14870 if (this.disabled) {
14871 input.disabled = true;
14882 inputblock.cls += ' input-group';
14884 inputblock.cn.unshift({
14886 cls : 'input-group-addon',
14891 if(this.removable && !this.multiple){
14892 inputblock.cls += ' roo-removable';
14894 inputblock.cn.push({
14897 cls : 'roo-combo-removable-btn close'
14901 if(this.hasFeedback && !this.allowBlank){
14903 inputblock.cls += ' has-feedback';
14905 inputblock.cn.push({
14907 cls: 'glyphicon form-control-feedback'
14914 inputblock.cls += (this.before) ? '' : ' input-group';
14916 inputblock.cn.push({
14918 cls : 'input-group-addon',
14929 cls: 'form-hidden-field'
14943 cls: 'form-hidden-field'
14947 cls: 'roo-select2-choices',
14951 cls: 'roo-select2-search-field',
14964 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14970 if(!this.multiple && this.showToggleBtn){
14977 if (this.caret != false) {
14980 cls: 'fa fa-' + this.caret
14987 cls : 'input-group-addon btn dropdown-toggle',
14992 cls: 'combobox-clear',
15006 combobox.cls += ' roo-select2-container-multi';
15009 var align = this.labelAlign || this.parentLabelAlign();
15011 if (align ==='left' && this.fieldLabel.length) {
15016 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15017 tooltip : 'This field is required'
15021 cls : 'control-label',
15022 html : this.fieldLabel
15033 var labelCfg = cfg.cn[1];
15034 var contentCfg = cfg.cn[2];
15037 if(this.indicatorpos == 'right'){
15042 cls : 'control-label',
15046 html : this.fieldLabel
15050 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15051 tooltip : 'This field is required'
15064 labelCfg = cfg.cn[0];
15065 contentCfg = cfg.cn[1];
15070 if(this.labelWidth > 12){
15071 labelCfg.style = "width: " + this.labelWidth + 'px';
15074 if(this.labelWidth < 13 && this.labelmd == 0){
15075 this.labelmd = this.labelWidth;
15078 if(this.labellg > 0){
15079 labelCfg.cls += ' col-lg-' + this.labellg;
15080 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15083 if(this.labelmd > 0){
15084 labelCfg.cls += ' col-md-' + this.labelmd;
15085 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15088 if(this.labelsm > 0){
15089 labelCfg.cls += ' col-sm-' + this.labelsm;
15090 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15093 if(this.labelxs > 0){
15094 labelCfg.cls += ' col-xs-' + this.labelxs;
15095 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15099 } else if ( this.fieldLabel.length) {
15103 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15104 tooltip : 'This field is required'
15108 cls : 'control-label',
15109 html : this.fieldLabel
15120 if(this.indicatorpos == 'right'){
15124 cls : 'control-label',
15125 html : this.fieldLabel,
15129 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15130 tooltip : 'This field is required'
15147 var settings = this;
15149 ['xs','sm','md','lg'].map(function(size){
15150 if (settings[size]) {
15151 cfg.cls += ' col-' + size + '-' + settings[size];
15158 initTouchView : function()
15160 this.renderTouchView();
15162 this.touchViewEl.on('scroll', function(){
15163 this.el.dom.scrollTop = 0;
15166 this.originalValue = this.getValue();
15168 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15170 this.inputEl().on("click", this.showTouchView, this);
15171 if (this.triggerEl) {
15172 this.triggerEl.on("click", this.showTouchView, this);
15176 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15177 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15179 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15181 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15182 this.store.on('load', this.onTouchViewLoad, this);
15183 this.store.on('loadexception', this.onTouchViewLoadException, this);
15185 if(this.hiddenName){
15187 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15189 this.hiddenField.dom.value =
15190 this.hiddenValue !== undefined ? this.hiddenValue :
15191 this.value !== undefined ? this.value : '';
15193 this.el.dom.removeAttribute('name');
15194 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15198 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15199 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15202 if(this.removable && !this.multiple){
15203 var close = this.closeTriggerEl();
15205 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15206 close.on('click', this.removeBtnClick, this, close);
15210 * fix the bug in Safari iOS8
15212 this.inputEl().on("focus", function(e){
15213 document.activeElement.blur();
15221 renderTouchView : function()
15223 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15224 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15226 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15227 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15229 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15230 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15231 this.touchViewBodyEl.setStyle('overflow', 'auto');
15233 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15234 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15236 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15237 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15241 showTouchView : function()
15247 this.touchViewHeaderEl.hide();
15249 if(this.modalTitle.length){
15250 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15251 this.touchViewHeaderEl.show();
15254 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15255 this.touchViewEl.show();
15257 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15259 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15260 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15262 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15264 if(this.modalTitle.length){
15265 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15268 this.touchViewBodyEl.setHeight(bodyHeight);
15272 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15274 this.touchViewEl.addClass('in');
15277 this.doTouchViewQuery();
15281 hideTouchView : function()
15283 this.touchViewEl.removeClass('in');
15287 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15289 this.touchViewEl.setStyle('display', 'none');
15294 setTouchViewValue : function()
15301 Roo.each(this.tickItems, function(o){
15306 this.hideTouchView();
15309 doTouchViewQuery : function()
15318 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15322 if(!this.alwaysQuery || this.mode == 'local'){
15323 this.onTouchViewLoad();
15330 onTouchViewBeforeLoad : function(combo,opts)
15336 onTouchViewLoad : function()
15338 if(this.store.getCount() < 1){
15339 this.onTouchViewEmptyResults();
15343 this.clearTouchView();
15345 var rawValue = this.getRawValue();
15347 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15349 this.tickItems = [];
15351 this.store.data.each(function(d, rowIndex){
15352 var row = this.touchViewListGroup.createChild(template);
15354 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15355 row.addClass(d.data.cls);
15358 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15361 html : d.data[this.displayField]
15364 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15365 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15368 row.removeClass('selected');
15369 if(!this.multiple && this.valueField &&
15370 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15373 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15374 row.addClass('selected');
15377 if(this.multiple && this.valueField &&
15378 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15382 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15383 this.tickItems.push(d.data);
15386 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15390 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15392 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15394 if(this.modalTitle.length){
15395 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15398 var listHeight = this.touchViewListGroup.getHeight();
15402 if(firstChecked && listHeight > bodyHeight){
15403 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15408 onTouchViewLoadException : function()
15410 this.hideTouchView();
15413 onTouchViewEmptyResults : function()
15415 this.clearTouchView();
15417 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15419 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15423 clearTouchView : function()
15425 this.touchViewListGroup.dom.innerHTML = '';
15428 onTouchViewClick : function(e, el, o)
15430 e.preventDefault();
15433 var rowIndex = o.rowIndex;
15435 var r = this.store.getAt(rowIndex);
15437 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15439 if(!this.multiple){
15440 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15441 c.dom.removeAttribute('checked');
15444 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15446 this.setFromData(r.data);
15448 var close = this.closeTriggerEl();
15454 this.hideTouchView();
15456 this.fireEvent('select', this, r, rowIndex);
15461 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15462 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15463 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15467 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15468 this.addItem(r.data);
15469 this.tickItems.push(r.data);
15473 getAutoCreateNativeIOS : function()
15476 cls: 'form-group' //input-group,
15481 cls : 'roo-ios-select'
15485 combobox.name = this.name;
15488 if (this.disabled) {
15489 combobox.disabled = true;
15492 var settings = this;
15494 ['xs','sm','md','lg'].map(function(size){
15495 if (settings[size]) {
15496 cfg.cls += ' col-' + size + '-' + settings[size];
15506 initIOSView : function()
15508 this.store.on('load', this.onIOSViewLoad, this);
15513 onIOSViewLoad : function()
15515 if(this.store.getCount() < 1){
15519 this.clearIOSView();
15521 if(this.allowBlank) {
15523 var default_text = '-- SELECT --';
15525 if(this.placeholder.length){
15526 default_text = this.placeholder;
15529 if(this.emptyTitle.length){
15530 default_text += ' - ' + this.emptyTitle + ' -';
15533 var opt = this.inputEl().createChild({
15536 html : default_text
15540 o[this.valueField] = 0;
15541 o[this.displayField] = default_text;
15543 this.ios_options.push({
15550 this.store.data.each(function(d, rowIndex){
15554 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15555 html = d.data[this.displayField];
15560 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15561 value = d.data[this.valueField];
15570 if(this.value == d.data[this.valueField]){
15571 option['selected'] = true;
15574 var opt = this.inputEl().createChild(option);
15576 this.ios_options.push({
15583 this.inputEl().on('change', function(){
15584 this.fireEvent('select', this);
15589 clearIOSView: function()
15591 this.inputEl().dom.innerHTML = '';
15593 this.ios_options = [];
15596 setIOSValue: function(v)
15600 if(!this.ios_options){
15604 Roo.each(this.ios_options, function(opts){
15606 opts.el.dom.removeAttribute('selected');
15608 if(opts.data[this.valueField] != v){
15612 opts.el.dom.setAttribute('selected', true);
15618 * @cfg {Boolean} grow
15622 * @cfg {Number} growMin
15626 * @cfg {Number} growMax
15635 Roo.apply(Roo.bootstrap.ComboBox, {
15639 cls: 'modal-header',
15661 cls: 'list-group-item',
15665 cls: 'roo-combobox-list-group-item-value'
15669 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15683 listItemCheckbox : {
15685 cls: 'list-group-item',
15689 cls: 'roo-combobox-list-group-item-value'
15693 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15709 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15714 cls: 'modal-footer',
15722 cls: 'col-xs-6 text-left',
15725 cls: 'btn btn-danger roo-touch-view-cancel',
15731 cls: 'col-xs-6 text-right',
15734 cls: 'btn btn-success roo-touch-view-ok',
15745 Roo.apply(Roo.bootstrap.ComboBox, {
15747 touchViewTemplate : {
15749 cls: 'modal fade roo-combobox-touch-view',
15753 cls: 'modal-dialog',
15754 style : 'position:fixed', // we have to fix position....
15758 cls: 'modal-content',
15760 Roo.bootstrap.ComboBox.header,
15761 Roo.bootstrap.ComboBox.body,
15762 Roo.bootstrap.ComboBox.footer
15771 * Ext JS Library 1.1.1
15772 * Copyright(c) 2006-2007, Ext JS, LLC.
15774 * Originally Released Under LGPL - original licence link has changed is not relivant.
15777 * <script type="text/javascript">
15782 * @extends Roo.util.Observable
15783 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15784 * This class also supports single and multi selection modes. <br>
15785 * Create a data model bound view:
15787 var store = new Roo.data.Store(...);
15789 var view = new Roo.View({
15791 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15793 singleSelect: true,
15794 selectedClass: "ydataview-selected",
15798 // listen for node click?
15799 view.on("click", function(vw, index, node, e){
15800 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15804 dataModel.load("foobar.xml");
15806 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15808 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15809 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15811 * Note: old style constructor is still suported (container, template, config)
15814 * Create a new View
15815 * @param {Object} config The config object
15818 Roo.View = function(config, depreciated_tpl, depreciated_config){
15820 this.parent = false;
15822 if (typeof(depreciated_tpl) == 'undefined') {
15823 // new way.. - universal constructor.
15824 Roo.apply(this, config);
15825 this.el = Roo.get(this.el);
15828 this.el = Roo.get(config);
15829 this.tpl = depreciated_tpl;
15830 Roo.apply(this, depreciated_config);
15832 this.wrapEl = this.el.wrap().wrap();
15833 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15836 if(typeof(this.tpl) == "string"){
15837 this.tpl = new Roo.Template(this.tpl);
15839 // support xtype ctors..
15840 this.tpl = new Roo.factory(this.tpl, Roo);
15844 this.tpl.compile();
15849 * @event beforeclick
15850 * Fires before a click is processed. Returns false to cancel the default action.
15851 * @param {Roo.View} this
15852 * @param {Number} index The index of the target node
15853 * @param {HTMLElement} node The target node
15854 * @param {Roo.EventObject} e The raw event object
15856 "beforeclick" : true,
15859 * Fires when a template node is clicked.
15860 * @param {Roo.View} this
15861 * @param {Number} index The index of the target node
15862 * @param {HTMLElement} node The target node
15863 * @param {Roo.EventObject} e The raw event object
15868 * Fires when a template node is double clicked.
15869 * @param {Roo.View} this
15870 * @param {Number} index The index of the target node
15871 * @param {HTMLElement} node The target node
15872 * @param {Roo.EventObject} e The raw event object
15876 * @event contextmenu
15877 * Fires when a template node is right clicked.
15878 * @param {Roo.View} this
15879 * @param {Number} index The index of the target node
15880 * @param {HTMLElement} node The target node
15881 * @param {Roo.EventObject} e The raw event object
15883 "contextmenu" : true,
15885 * @event selectionchange
15886 * Fires when the selected nodes change.
15887 * @param {Roo.View} this
15888 * @param {Array} selections Array of the selected nodes
15890 "selectionchange" : true,
15893 * @event beforeselect
15894 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15895 * @param {Roo.View} this
15896 * @param {HTMLElement} node The node to be selected
15897 * @param {Array} selections Array of currently selected nodes
15899 "beforeselect" : true,
15901 * @event preparedata
15902 * Fires on every row to render, to allow you to change the data.
15903 * @param {Roo.View} this
15904 * @param {Object} data to be rendered (change this)
15906 "preparedata" : true
15914 "click": this.onClick,
15915 "dblclick": this.onDblClick,
15916 "contextmenu": this.onContextMenu,
15920 this.selections = [];
15922 this.cmp = new Roo.CompositeElementLite([]);
15924 this.store = Roo.factory(this.store, Roo.data);
15925 this.setStore(this.store, true);
15928 if ( this.footer && this.footer.xtype) {
15930 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15932 this.footer.dataSource = this.store;
15933 this.footer.container = fctr;
15934 this.footer = Roo.factory(this.footer, Roo);
15935 fctr.insertFirst(this.el);
15937 // this is a bit insane - as the paging toolbar seems to detach the el..
15938 // dom.parentNode.parentNode.parentNode
15939 // they get detached?
15943 Roo.View.superclass.constructor.call(this);
15948 Roo.extend(Roo.View, Roo.util.Observable, {
15951 * @cfg {Roo.data.Store} store Data store to load data from.
15956 * @cfg {String|Roo.Element} el The container element.
15961 * @cfg {String|Roo.Template} tpl The template used by this View
15965 * @cfg {String} dataName the named area of the template to use as the data area
15966 * Works with domtemplates roo-name="name"
15970 * @cfg {String} selectedClass The css class to add to selected nodes
15972 selectedClass : "x-view-selected",
15974 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15979 * @cfg {String} text to display on mask (default Loading)
15983 * @cfg {Boolean} multiSelect Allow multiple selection
15985 multiSelect : false,
15987 * @cfg {Boolean} singleSelect Allow single selection
15989 singleSelect: false,
15992 * @cfg {Boolean} toggleSelect - selecting
15994 toggleSelect : false,
15997 * @cfg {Boolean} tickable - selecting
16002 * Returns the element this view is bound to.
16003 * @return {Roo.Element}
16005 getEl : function(){
16006 return this.wrapEl;
16012 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16014 refresh : function(){
16015 //Roo.log('refresh');
16018 // if we are using something like 'domtemplate', then
16019 // the what gets used is:
16020 // t.applySubtemplate(NAME, data, wrapping data..)
16021 // the outer template then get' applied with
16022 // the store 'extra data'
16023 // and the body get's added to the
16024 // roo-name="data" node?
16025 // <span class='roo-tpl-{name}'></span> ?????
16029 this.clearSelections();
16030 this.el.update("");
16032 var records = this.store.getRange();
16033 if(records.length < 1) {
16035 // is this valid?? = should it render a template??
16037 this.el.update(this.emptyText);
16041 if (this.dataName) {
16042 this.el.update(t.apply(this.store.meta)); //????
16043 el = this.el.child('.roo-tpl-' + this.dataName);
16046 for(var i = 0, len = records.length; i < len; i++){
16047 var data = this.prepareData(records[i].data, i, records[i]);
16048 this.fireEvent("preparedata", this, data, i, records[i]);
16050 var d = Roo.apply({}, data);
16053 Roo.apply(d, {'roo-id' : Roo.id()});
16057 Roo.each(this.parent.item, function(item){
16058 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16061 Roo.apply(d, {'roo-data-checked' : 'checked'});
16065 html[html.length] = Roo.util.Format.trim(
16067 t.applySubtemplate(this.dataName, d, this.store.meta) :
16074 el.update(html.join(""));
16075 this.nodes = el.dom.childNodes;
16076 this.updateIndexes(0);
16081 * Function to override to reformat the data that is sent to
16082 * the template for each node.
16083 * DEPRICATED - use the preparedata event handler.
16084 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16085 * a JSON object for an UpdateManager bound view).
16087 prepareData : function(data, index, record)
16089 this.fireEvent("preparedata", this, data, index, record);
16093 onUpdate : function(ds, record){
16094 // Roo.log('on update');
16095 this.clearSelections();
16096 var index = this.store.indexOf(record);
16097 var n = this.nodes[index];
16098 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16099 n.parentNode.removeChild(n);
16100 this.updateIndexes(index, index);
16106 onAdd : function(ds, records, index)
16108 //Roo.log(['on Add', ds, records, index] );
16109 this.clearSelections();
16110 if(this.nodes.length == 0){
16114 var n = this.nodes[index];
16115 for(var i = 0, len = records.length; i < len; i++){
16116 var d = this.prepareData(records[i].data, i, records[i]);
16118 this.tpl.insertBefore(n, d);
16121 this.tpl.append(this.el, d);
16124 this.updateIndexes(index);
16127 onRemove : function(ds, record, index){
16128 // Roo.log('onRemove');
16129 this.clearSelections();
16130 var el = this.dataName ?
16131 this.el.child('.roo-tpl-' + this.dataName) :
16134 el.dom.removeChild(this.nodes[index]);
16135 this.updateIndexes(index);
16139 * Refresh an individual node.
16140 * @param {Number} index
16142 refreshNode : function(index){
16143 this.onUpdate(this.store, this.store.getAt(index));
16146 updateIndexes : function(startIndex, endIndex){
16147 var ns = this.nodes;
16148 startIndex = startIndex || 0;
16149 endIndex = endIndex || ns.length - 1;
16150 for(var i = startIndex; i <= endIndex; i++){
16151 ns[i].nodeIndex = i;
16156 * Changes the data store this view uses and refresh the view.
16157 * @param {Store} store
16159 setStore : function(store, initial){
16160 if(!initial && this.store){
16161 this.store.un("datachanged", this.refresh);
16162 this.store.un("add", this.onAdd);
16163 this.store.un("remove", this.onRemove);
16164 this.store.un("update", this.onUpdate);
16165 this.store.un("clear", this.refresh);
16166 this.store.un("beforeload", this.onBeforeLoad);
16167 this.store.un("load", this.onLoad);
16168 this.store.un("loadexception", this.onLoad);
16172 store.on("datachanged", this.refresh, this);
16173 store.on("add", this.onAdd, this);
16174 store.on("remove", this.onRemove, this);
16175 store.on("update", this.onUpdate, this);
16176 store.on("clear", this.refresh, this);
16177 store.on("beforeload", this.onBeforeLoad, this);
16178 store.on("load", this.onLoad, this);
16179 store.on("loadexception", this.onLoad, this);
16187 * onbeforeLoad - masks the loading area.
16190 onBeforeLoad : function(store,opts)
16192 //Roo.log('onBeforeLoad');
16194 this.el.update("");
16196 this.el.mask(this.mask ? this.mask : "Loading" );
16198 onLoad : function ()
16205 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16206 * @param {HTMLElement} node
16207 * @return {HTMLElement} The template node
16209 findItemFromChild : function(node){
16210 var el = this.dataName ?
16211 this.el.child('.roo-tpl-' + this.dataName,true) :
16214 if(!node || node.parentNode == el){
16217 var p = node.parentNode;
16218 while(p && p != el){
16219 if(p.parentNode == el){
16228 onClick : function(e){
16229 var item = this.findItemFromChild(e.getTarget());
16231 var index = this.indexOf(item);
16232 if(this.onItemClick(item, index, e) !== false){
16233 this.fireEvent("click", this, index, item, e);
16236 this.clearSelections();
16241 onContextMenu : function(e){
16242 var item = this.findItemFromChild(e.getTarget());
16244 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16249 onDblClick : function(e){
16250 var item = this.findItemFromChild(e.getTarget());
16252 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16256 onItemClick : function(item, index, e)
16258 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16261 if (this.toggleSelect) {
16262 var m = this.isSelected(item) ? 'unselect' : 'select';
16265 _t[m](item, true, false);
16268 if(this.multiSelect || this.singleSelect){
16269 if(this.multiSelect && e.shiftKey && this.lastSelection){
16270 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16272 this.select(item, this.multiSelect && e.ctrlKey);
16273 this.lastSelection = item;
16276 if(!this.tickable){
16277 e.preventDefault();
16285 * Get the number of selected nodes.
16288 getSelectionCount : function(){
16289 return this.selections.length;
16293 * Get the currently selected nodes.
16294 * @return {Array} An array of HTMLElements
16296 getSelectedNodes : function(){
16297 return this.selections;
16301 * Get the indexes of the selected nodes.
16304 getSelectedIndexes : function(){
16305 var indexes = [], s = this.selections;
16306 for(var i = 0, len = s.length; i < len; i++){
16307 indexes.push(s[i].nodeIndex);
16313 * Clear all selections
16314 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16316 clearSelections : function(suppressEvent){
16317 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16318 this.cmp.elements = this.selections;
16319 this.cmp.removeClass(this.selectedClass);
16320 this.selections = [];
16321 if(!suppressEvent){
16322 this.fireEvent("selectionchange", this, this.selections);
16328 * Returns true if the passed node is selected
16329 * @param {HTMLElement/Number} node The node or node index
16330 * @return {Boolean}
16332 isSelected : function(node){
16333 var s = this.selections;
16337 node = this.getNode(node);
16338 return s.indexOf(node) !== -1;
16343 * @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
16344 * @param {Boolean} keepExisting (optional) true to keep existing selections
16345 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16347 select : function(nodeInfo, keepExisting, suppressEvent){
16348 if(nodeInfo instanceof Array){
16350 this.clearSelections(true);
16352 for(var i = 0, len = nodeInfo.length; i < len; i++){
16353 this.select(nodeInfo[i], true, true);
16357 var node = this.getNode(nodeInfo);
16358 if(!node || this.isSelected(node)){
16359 return; // already selected.
16362 this.clearSelections(true);
16365 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16366 Roo.fly(node).addClass(this.selectedClass);
16367 this.selections.push(node);
16368 if(!suppressEvent){
16369 this.fireEvent("selectionchange", this, this.selections);
16377 * @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
16378 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16379 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16381 unselect : function(nodeInfo, keepExisting, suppressEvent)
16383 if(nodeInfo instanceof Array){
16384 Roo.each(this.selections, function(s) {
16385 this.unselect(s, nodeInfo);
16389 var node = this.getNode(nodeInfo);
16390 if(!node || !this.isSelected(node)){
16391 //Roo.log("not selected");
16392 return; // not selected.
16396 Roo.each(this.selections, function(s) {
16398 Roo.fly(node).removeClass(this.selectedClass);
16405 this.selections= ns;
16406 this.fireEvent("selectionchange", this, this.selections);
16410 * Gets a template node.
16411 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16412 * @return {HTMLElement} The node or null if it wasn't found
16414 getNode : function(nodeInfo){
16415 if(typeof nodeInfo == "string"){
16416 return document.getElementById(nodeInfo);
16417 }else if(typeof nodeInfo == "number"){
16418 return this.nodes[nodeInfo];
16424 * Gets a range template nodes.
16425 * @param {Number} startIndex
16426 * @param {Number} endIndex
16427 * @return {Array} An array of nodes
16429 getNodes : function(start, end){
16430 var ns = this.nodes;
16431 start = start || 0;
16432 end = typeof end == "undefined" ? ns.length - 1 : end;
16435 for(var i = start; i <= end; i++){
16439 for(var i = start; i >= end; i--){
16447 * Finds the index of the passed node
16448 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16449 * @return {Number} The index of the node or -1
16451 indexOf : function(node){
16452 node = this.getNode(node);
16453 if(typeof node.nodeIndex == "number"){
16454 return node.nodeIndex;
16456 var ns = this.nodes;
16457 for(var i = 0, len = ns.length; i < len; i++){
16468 * based on jquery fullcalendar
16472 Roo.bootstrap = Roo.bootstrap || {};
16474 * @class Roo.bootstrap.Calendar
16475 * @extends Roo.bootstrap.Component
16476 * Bootstrap Calendar class
16477 * @cfg {Boolean} loadMask (true|false) default false
16478 * @cfg {Object} header generate the user specific header of the calendar, default false
16481 * Create a new Container
16482 * @param {Object} config The config object
16487 Roo.bootstrap.Calendar = function(config){
16488 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16492 * Fires when a date is selected
16493 * @param {DatePicker} this
16494 * @param {Date} date The selected date
16498 * @event monthchange
16499 * Fires when the displayed month changes
16500 * @param {DatePicker} this
16501 * @param {Date} date The selected month
16503 'monthchange': true,
16505 * @event evententer
16506 * Fires when mouse over an event
16507 * @param {Calendar} this
16508 * @param {event} Event
16510 'evententer': true,
16512 * @event eventleave
16513 * Fires when the mouse leaves an
16514 * @param {Calendar} this
16517 'eventleave': true,
16519 * @event eventclick
16520 * Fires when the mouse click an
16521 * @param {Calendar} this
16530 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16533 * @cfg {Number} startDay
16534 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16542 getAutoCreate : function(){
16545 var fc_button = function(name, corner, style, content ) {
16546 return Roo.apply({},{
16548 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16550 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16553 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16564 style : 'width:100%',
16571 cls : 'fc-header-left',
16573 fc_button('prev', 'left', 'arrow', '‹' ),
16574 fc_button('next', 'right', 'arrow', '›' ),
16575 { tag: 'span', cls: 'fc-header-space' },
16576 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16584 cls : 'fc-header-center',
16588 cls: 'fc-header-title',
16591 html : 'month / year'
16599 cls : 'fc-header-right',
16601 /* fc_button('month', 'left', '', 'month' ),
16602 fc_button('week', '', '', 'week' ),
16603 fc_button('day', 'right', '', 'day' )
16615 header = this.header;
16618 var cal_heads = function() {
16620 // fixme - handle this.
16622 for (var i =0; i < Date.dayNames.length; i++) {
16623 var d = Date.dayNames[i];
16626 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16627 html : d.substring(0,3)
16631 ret[0].cls += ' fc-first';
16632 ret[6].cls += ' fc-last';
16635 var cal_cell = function(n) {
16638 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16643 cls: 'fc-day-number',
16647 cls: 'fc-day-content',
16651 style: 'position: relative;' // height: 17px;
16663 var cal_rows = function() {
16666 for (var r = 0; r < 6; r++) {
16673 for (var i =0; i < Date.dayNames.length; i++) {
16674 var d = Date.dayNames[i];
16675 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16678 row.cn[0].cls+=' fc-first';
16679 row.cn[0].cn[0].style = 'min-height:90px';
16680 row.cn[6].cls+=' fc-last';
16684 ret[0].cls += ' fc-first';
16685 ret[4].cls += ' fc-prev-last';
16686 ret[5].cls += ' fc-last';
16693 cls: 'fc-border-separate',
16694 style : 'width:100%',
16702 cls : 'fc-first fc-last',
16720 cls : 'fc-content',
16721 style : "position: relative;",
16724 cls : 'fc-view fc-view-month fc-grid',
16725 style : 'position: relative',
16726 unselectable : 'on',
16729 cls : 'fc-event-container',
16730 style : 'position:absolute;z-index:8;top:0;left:0;'
16748 initEvents : function()
16751 throw "can not find store for calendar";
16757 style: "text-align:center",
16761 style: "background-color:white;width:50%;margin:250 auto",
16765 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16776 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16778 var size = this.el.select('.fc-content', true).first().getSize();
16779 this.maskEl.setSize(size.width, size.height);
16780 this.maskEl.enableDisplayMode("block");
16781 if(!this.loadMask){
16782 this.maskEl.hide();
16785 this.store = Roo.factory(this.store, Roo.data);
16786 this.store.on('load', this.onLoad, this);
16787 this.store.on('beforeload', this.onBeforeLoad, this);
16791 this.cells = this.el.select('.fc-day',true);
16792 //Roo.log(this.cells);
16793 this.textNodes = this.el.query('.fc-day-number');
16794 this.cells.addClassOnOver('fc-state-hover');
16796 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16797 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16798 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16799 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16801 this.on('monthchange', this.onMonthChange, this);
16803 this.update(new Date().clearTime());
16806 resize : function() {
16807 var sz = this.el.getSize();
16809 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16810 this.el.select('.fc-day-content div',true).setHeight(34);
16815 showPrevMonth : function(e){
16816 this.update(this.activeDate.add("mo", -1));
16818 showToday : function(e){
16819 this.update(new Date().clearTime());
16822 showNextMonth : function(e){
16823 this.update(this.activeDate.add("mo", 1));
16827 showPrevYear : function(){
16828 this.update(this.activeDate.add("y", -1));
16832 showNextYear : function(){
16833 this.update(this.activeDate.add("y", 1));
16838 update : function(date)
16840 var vd = this.activeDate;
16841 this.activeDate = date;
16842 // if(vd && this.el){
16843 // var t = date.getTime();
16844 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16845 // Roo.log('using add remove');
16847 // this.fireEvent('monthchange', this, date);
16849 // this.cells.removeClass("fc-state-highlight");
16850 // this.cells.each(function(c){
16851 // if(c.dateValue == t){
16852 // c.addClass("fc-state-highlight");
16853 // setTimeout(function(){
16854 // try{c.dom.firstChild.focus();}catch(e){}
16864 var days = date.getDaysInMonth();
16866 var firstOfMonth = date.getFirstDateOfMonth();
16867 var startingPos = firstOfMonth.getDay()-this.startDay;
16869 if(startingPos < this.startDay){
16873 var pm = date.add(Date.MONTH, -1);
16874 var prevStart = pm.getDaysInMonth()-startingPos;
16876 this.cells = this.el.select('.fc-day',true);
16877 this.textNodes = this.el.query('.fc-day-number');
16878 this.cells.addClassOnOver('fc-state-hover');
16880 var cells = this.cells.elements;
16881 var textEls = this.textNodes;
16883 Roo.each(cells, function(cell){
16884 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16887 days += startingPos;
16889 // convert everything to numbers so it's fast
16890 var day = 86400000;
16891 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16894 //Roo.log(prevStart);
16896 var today = new Date().clearTime().getTime();
16897 var sel = date.clearTime().getTime();
16898 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16899 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16900 var ddMatch = this.disabledDatesRE;
16901 var ddText = this.disabledDatesText;
16902 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16903 var ddaysText = this.disabledDaysText;
16904 var format = this.format;
16906 var setCellClass = function(cal, cell){
16910 //Roo.log('set Cell Class');
16912 var t = d.getTime();
16916 cell.dateValue = t;
16918 cell.className += " fc-today";
16919 cell.className += " fc-state-highlight";
16920 cell.title = cal.todayText;
16923 // disable highlight in other month..
16924 //cell.className += " fc-state-highlight";
16929 cell.className = " fc-state-disabled";
16930 cell.title = cal.minText;
16934 cell.className = " fc-state-disabled";
16935 cell.title = cal.maxText;
16939 if(ddays.indexOf(d.getDay()) != -1){
16940 cell.title = ddaysText;
16941 cell.className = " fc-state-disabled";
16944 if(ddMatch && format){
16945 var fvalue = d.dateFormat(format);
16946 if(ddMatch.test(fvalue)){
16947 cell.title = ddText.replace("%0", fvalue);
16948 cell.className = " fc-state-disabled";
16952 if (!cell.initialClassName) {
16953 cell.initialClassName = cell.dom.className;
16956 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16961 for(; i < startingPos; i++) {
16962 textEls[i].innerHTML = (++prevStart);
16963 d.setDate(d.getDate()+1);
16965 cells[i].className = "fc-past fc-other-month";
16966 setCellClass(this, cells[i]);
16971 for(; i < days; i++){
16972 intDay = i - startingPos + 1;
16973 textEls[i].innerHTML = (intDay);
16974 d.setDate(d.getDate()+1);
16976 cells[i].className = ''; // "x-date-active";
16977 setCellClass(this, cells[i]);
16981 for(; i < 42; i++) {
16982 textEls[i].innerHTML = (++extraDays);
16983 d.setDate(d.getDate()+1);
16985 cells[i].className = "fc-future fc-other-month";
16986 setCellClass(this, cells[i]);
16989 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16991 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16993 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16994 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16996 if(totalRows != 6){
16997 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16998 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17001 this.fireEvent('monthchange', this, date);
17005 if(!this.internalRender){
17006 var main = this.el.dom.firstChild;
17007 var w = main.offsetWidth;
17008 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17009 Roo.fly(main).setWidth(w);
17010 this.internalRender = true;
17011 // opera does not respect the auto grow header center column
17012 // then, after it gets a width opera refuses to recalculate
17013 // without a second pass
17014 if(Roo.isOpera && !this.secondPass){
17015 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17016 this.secondPass = true;
17017 this.update.defer(10, this, [date]);
17024 findCell : function(dt) {
17025 dt = dt.clearTime().getTime();
17027 this.cells.each(function(c){
17028 //Roo.log("check " +c.dateValue + '?=' + dt);
17029 if(c.dateValue == dt){
17039 findCells : function(ev) {
17040 var s = ev.start.clone().clearTime().getTime();
17042 var e= ev.end.clone().clearTime().getTime();
17045 this.cells.each(function(c){
17046 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17048 if(c.dateValue > e){
17051 if(c.dateValue < s){
17060 // findBestRow: function(cells)
17064 // for (var i =0 ; i < cells.length;i++) {
17065 // ret = Math.max(cells[i].rows || 0,ret);
17072 addItem : function(ev)
17074 // look for vertical location slot in
17075 var cells = this.findCells(ev);
17077 // ev.row = this.findBestRow(cells);
17079 // work out the location.
17083 for(var i =0; i < cells.length; i++) {
17085 cells[i].row = cells[0].row;
17088 cells[i].row = cells[i].row + 1;
17098 if (crow.start.getY() == cells[i].getY()) {
17100 crow.end = cells[i];
17117 cells[0].events.push(ev);
17119 this.calevents.push(ev);
17122 clearEvents: function() {
17124 if(!this.calevents){
17128 Roo.each(this.cells.elements, function(c){
17134 Roo.each(this.calevents, function(e) {
17135 Roo.each(e.els, function(el) {
17136 el.un('mouseenter' ,this.onEventEnter, this);
17137 el.un('mouseleave' ,this.onEventLeave, this);
17142 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17148 renderEvents: function()
17152 this.cells.each(function(c) {
17161 if(c.row != c.events.length){
17162 r = 4 - (4 - (c.row - c.events.length));
17165 c.events = ev.slice(0, r);
17166 c.more = ev.slice(r);
17168 if(c.more.length && c.more.length == 1){
17169 c.events.push(c.more.pop());
17172 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17176 this.cells.each(function(c) {
17178 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17181 for (var e = 0; e < c.events.length; e++){
17182 var ev = c.events[e];
17183 var rows = ev.rows;
17185 for(var i = 0; i < rows.length; i++) {
17187 // how many rows should it span..
17190 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17191 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17193 unselectable : "on",
17196 cls: 'fc-event-inner',
17200 // cls: 'fc-event-time',
17201 // html : cells.length > 1 ? '' : ev.time
17205 cls: 'fc-event-title',
17206 html : String.format('{0}', ev.title)
17213 cls: 'ui-resizable-handle ui-resizable-e',
17214 html : '  '
17221 cfg.cls += ' fc-event-start';
17223 if ((i+1) == rows.length) {
17224 cfg.cls += ' fc-event-end';
17227 var ctr = _this.el.select('.fc-event-container',true).first();
17228 var cg = ctr.createChild(cfg);
17230 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17231 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17233 var r = (c.more.length) ? 1 : 0;
17234 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17235 cg.setWidth(ebox.right - sbox.x -2);
17237 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17238 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17239 cg.on('click', _this.onEventClick, _this, ev);
17250 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17251 style : 'position: absolute',
17252 unselectable : "on",
17255 cls: 'fc-event-inner',
17259 cls: 'fc-event-title',
17267 cls: 'ui-resizable-handle ui-resizable-e',
17268 html : '  '
17274 var ctr = _this.el.select('.fc-event-container',true).first();
17275 var cg = ctr.createChild(cfg);
17277 var sbox = c.select('.fc-day-content',true).first().getBox();
17278 var ebox = c.select('.fc-day-content',true).first().getBox();
17280 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17281 cg.setWidth(ebox.right - sbox.x -2);
17283 cg.on('click', _this.onMoreEventClick, _this, c.more);
17293 onEventEnter: function (e, el,event,d) {
17294 this.fireEvent('evententer', this, el, event);
17297 onEventLeave: function (e, el,event,d) {
17298 this.fireEvent('eventleave', this, el, event);
17301 onEventClick: function (e, el,event,d) {
17302 this.fireEvent('eventclick', this, el, event);
17305 onMonthChange: function () {
17309 onMoreEventClick: function(e, el, more)
17313 this.calpopover.placement = 'right';
17314 this.calpopover.setTitle('More');
17316 this.calpopover.setContent('');
17318 var ctr = this.calpopover.el.select('.popover-content', true).first();
17320 Roo.each(more, function(m){
17322 cls : 'fc-event-hori fc-event-draggable',
17325 var cg = ctr.createChild(cfg);
17327 cg.on('click', _this.onEventClick, _this, m);
17330 this.calpopover.show(el);
17335 onLoad: function ()
17337 this.calevents = [];
17340 if(this.store.getCount() > 0){
17341 this.store.data.each(function(d){
17344 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17345 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17346 time : d.data.start_time,
17347 title : d.data.title,
17348 description : d.data.description,
17349 venue : d.data.venue
17354 this.renderEvents();
17356 if(this.calevents.length && this.loadMask){
17357 this.maskEl.hide();
17361 onBeforeLoad: function()
17363 this.clearEvents();
17365 this.maskEl.show();
17379 * @class Roo.bootstrap.Popover
17380 * @extends Roo.bootstrap.Component
17381 * Bootstrap Popover class
17382 * @cfg {String} html contents of the popover (or false to use children..)
17383 * @cfg {String} title of popover (or false to hide)
17384 * @cfg {String} placement how it is placed
17385 * @cfg {String} trigger click || hover (or false to trigger manually)
17386 * @cfg {String} over what (parent or false to trigger manually.)
17387 * @cfg {Number} delay - delay before showing
17390 * Create a new Popover
17391 * @param {Object} config The config object
17394 Roo.bootstrap.Popover = function(config){
17395 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17401 * After the popover show
17403 * @param {Roo.bootstrap.Popover} this
17408 * After the popover hide
17410 * @param {Roo.bootstrap.Popover} this
17416 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17418 title: 'Fill in a title',
17421 placement : 'right',
17422 trigger : 'hover', // hover
17428 can_build_overlaid : false,
17430 getChildContainer : function()
17432 return this.el.select('.popover-content',true).first();
17435 getAutoCreate : function(){
17438 cls : 'popover roo-dynamic',
17439 style: 'display:block',
17445 cls : 'popover-inner',
17449 cls: 'popover-title',
17453 cls : 'popover-content',
17464 setTitle: function(str)
17467 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17469 setContent: function(str)
17472 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17474 // as it get's added to the bottom of the page.
17475 onRender : function(ct, position)
17477 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17479 var cfg = Roo.apply({}, this.getAutoCreate());
17483 cfg.cls += ' ' + this.cls;
17486 cfg.style = this.style;
17488 //Roo.log("adding to ");
17489 this.el = Roo.get(document.body).createChild(cfg, position);
17490 // Roo.log(this.el);
17495 initEvents : function()
17497 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17498 this.el.enableDisplayMode('block');
17500 if (this.over === false) {
17503 if (this.triggers === false) {
17506 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17507 var triggers = this.trigger ? this.trigger.split(' ') : [];
17508 Roo.each(triggers, function(trigger) {
17510 if (trigger == 'click') {
17511 on_el.on('click', this.toggle, this);
17512 } else if (trigger != 'manual') {
17513 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17514 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17516 on_el.on(eventIn ,this.enter, this);
17517 on_el.on(eventOut, this.leave, this);
17528 toggle : function () {
17529 this.hoverState == 'in' ? this.leave() : this.enter();
17532 enter : function () {
17534 clearTimeout(this.timeout);
17536 this.hoverState = 'in';
17538 if (!this.delay || !this.delay.show) {
17543 this.timeout = setTimeout(function () {
17544 if (_t.hoverState == 'in') {
17547 }, this.delay.show)
17550 leave : function() {
17551 clearTimeout(this.timeout);
17553 this.hoverState = 'out';
17555 if (!this.delay || !this.delay.hide) {
17560 this.timeout = setTimeout(function () {
17561 if (_t.hoverState == 'out') {
17564 }, this.delay.hide)
17567 show : function (on_el)
17570 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17574 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17575 if (this.html !== false) {
17576 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17578 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17579 if (!this.title.length) {
17580 this.el.select('.popover-title',true).hide();
17583 var placement = typeof this.placement == 'function' ?
17584 this.placement.call(this, this.el, on_el) :
17587 var autoToken = /\s?auto?\s?/i;
17588 var autoPlace = autoToken.test(placement);
17590 placement = placement.replace(autoToken, '') || 'top';
17594 //this.el.setXY([0,0]);
17596 this.el.dom.style.display='block';
17597 this.el.addClass(placement);
17599 //this.el.appendTo(on_el);
17601 var p = this.getPosition();
17602 var box = this.el.getBox();
17607 var align = Roo.bootstrap.Popover.alignment[placement];
17610 this.el.alignTo(on_el, align[0],align[1]);
17611 //var arrow = this.el.select('.arrow',true).first();
17612 //arrow.set(align[2],
17614 this.el.addClass('in');
17617 if (this.el.hasClass('fade')) {
17621 this.hoverState = 'in';
17623 this.fireEvent('show', this);
17628 this.el.setXY([0,0]);
17629 this.el.removeClass('in');
17631 this.hoverState = null;
17633 this.fireEvent('hide', this);
17638 Roo.bootstrap.Popover.alignment = {
17639 'left' : ['r-l', [-10,0], 'right'],
17640 'right' : ['l-r', [10,0], 'left'],
17641 'bottom' : ['t-b', [0,10], 'top'],
17642 'top' : [ 'b-t', [0,-10], 'bottom']
17653 * @class Roo.bootstrap.Progress
17654 * @extends Roo.bootstrap.Component
17655 * Bootstrap Progress class
17656 * @cfg {Boolean} striped striped of the progress bar
17657 * @cfg {Boolean} active animated of the progress bar
17661 * Create a new Progress
17662 * @param {Object} config The config object
17665 Roo.bootstrap.Progress = function(config){
17666 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17669 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17674 getAutoCreate : function(){
17682 cfg.cls += ' progress-striped';
17686 cfg.cls += ' active';
17705 * @class Roo.bootstrap.ProgressBar
17706 * @extends Roo.bootstrap.Component
17707 * Bootstrap ProgressBar class
17708 * @cfg {Number} aria_valuenow aria-value now
17709 * @cfg {Number} aria_valuemin aria-value min
17710 * @cfg {Number} aria_valuemax aria-value max
17711 * @cfg {String} label label for the progress bar
17712 * @cfg {String} panel (success | info | warning | danger )
17713 * @cfg {String} role role of the progress bar
17714 * @cfg {String} sr_only text
17718 * Create a new ProgressBar
17719 * @param {Object} config The config object
17722 Roo.bootstrap.ProgressBar = function(config){
17723 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17726 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17730 aria_valuemax : 100,
17736 getAutoCreate : function()
17741 cls: 'progress-bar',
17742 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17754 cfg.role = this.role;
17757 if(this.aria_valuenow){
17758 cfg['aria-valuenow'] = this.aria_valuenow;
17761 if(this.aria_valuemin){
17762 cfg['aria-valuemin'] = this.aria_valuemin;
17765 if(this.aria_valuemax){
17766 cfg['aria-valuemax'] = this.aria_valuemax;
17769 if(this.label && !this.sr_only){
17770 cfg.html = this.label;
17774 cfg.cls += ' progress-bar-' + this.panel;
17780 update : function(aria_valuenow)
17782 this.aria_valuenow = aria_valuenow;
17784 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17799 * @class Roo.bootstrap.TabGroup
17800 * @extends Roo.bootstrap.Column
17801 * Bootstrap Column class
17802 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17803 * @cfg {Boolean} carousel true to make the group behave like a carousel
17804 * @cfg {Boolean} bullets show bullets for the panels
17805 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17806 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17807 * @cfg {Boolean} showarrow (true|false) show arrow default true
17810 * Create a new TabGroup
17811 * @param {Object} config The config object
17814 Roo.bootstrap.TabGroup = function(config){
17815 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17817 this.navId = Roo.id();
17820 Roo.bootstrap.TabGroup.register(this);
17824 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17827 transition : false,
17832 slideOnTouch : false,
17835 getAutoCreate : function()
17837 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17839 cfg.cls += ' tab-content';
17841 if (this.carousel) {
17842 cfg.cls += ' carousel slide';
17845 cls : 'carousel-inner',
17849 if(this.bullets && !Roo.isTouch){
17852 cls : 'carousel-bullets',
17856 if(this.bullets_cls){
17857 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17864 cfg.cn[0].cn.push(bullets);
17867 if(this.showarrow){
17868 cfg.cn[0].cn.push({
17870 class : 'carousel-arrow',
17874 class : 'carousel-prev',
17878 class : 'fa fa-chevron-left'
17884 class : 'carousel-next',
17888 class : 'fa fa-chevron-right'
17901 initEvents: function()
17903 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17904 // this.el.on("touchstart", this.onTouchStart, this);
17907 if(this.autoslide){
17910 this.slideFn = window.setInterval(function() {
17911 _this.showPanelNext();
17915 if(this.showarrow){
17916 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17917 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17923 // onTouchStart : function(e, el, o)
17925 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17929 // this.showPanelNext();
17933 getChildContainer : function()
17935 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17939 * register a Navigation item
17940 * @param {Roo.bootstrap.NavItem} the navitem to add
17942 register : function(item)
17944 this.tabs.push( item);
17945 item.navId = this.navId; // not really needed..
17950 getActivePanel : function()
17953 Roo.each(this.tabs, function(t) {
17963 getPanelByName : function(n)
17966 Roo.each(this.tabs, function(t) {
17967 if (t.tabId == n) {
17975 indexOfPanel : function(p)
17978 Roo.each(this.tabs, function(t,i) {
17979 if (t.tabId == p.tabId) {
17988 * show a specific panel
17989 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17990 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17992 showPanel : function (pan)
17994 if(this.transition || typeof(pan) == 'undefined'){
17995 Roo.log("waiting for the transitionend");
17999 if (typeof(pan) == 'number') {
18000 pan = this.tabs[pan];
18003 if (typeof(pan) == 'string') {
18004 pan = this.getPanelByName(pan);
18007 var cur = this.getActivePanel();
18010 Roo.log('pan or acitve pan is undefined');
18014 if (pan.tabId == this.getActivePanel().tabId) {
18018 if (false === cur.fireEvent('beforedeactivate')) {
18022 if(this.bullets > 0 && !Roo.isTouch){
18023 this.setActiveBullet(this.indexOfPanel(pan));
18026 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18028 this.transition = true;
18029 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18030 var lr = dir == 'next' ? 'left' : 'right';
18031 pan.el.addClass(dir); // or prev
18032 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18033 cur.el.addClass(lr); // or right
18034 pan.el.addClass(lr);
18037 cur.el.on('transitionend', function() {
18038 Roo.log("trans end?");
18040 pan.el.removeClass([lr,dir]);
18041 pan.setActive(true);
18043 cur.el.removeClass([lr]);
18044 cur.setActive(false);
18046 _this.transition = false;
18048 }, this, { single: true } );
18053 cur.setActive(false);
18054 pan.setActive(true);
18059 showPanelNext : function()
18061 var i = this.indexOfPanel(this.getActivePanel());
18063 if (i >= this.tabs.length - 1 && !this.autoslide) {
18067 if (i >= this.tabs.length - 1 && this.autoslide) {
18071 this.showPanel(this.tabs[i+1]);
18074 showPanelPrev : function()
18076 var i = this.indexOfPanel(this.getActivePanel());
18078 if (i < 1 && !this.autoslide) {
18082 if (i < 1 && this.autoslide) {
18083 i = this.tabs.length;
18086 this.showPanel(this.tabs[i-1]);
18090 addBullet: function()
18092 if(!this.bullets || Roo.isTouch){
18095 var ctr = this.el.select('.carousel-bullets',true).first();
18096 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18097 var bullet = ctr.createChild({
18098 cls : 'bullet bullet-' + i
18099 },ctr.dom.lastChild);
18104 bullet.on('click', (function(e, el, o, ii, t){
18106 e.preventDefault();
18108 this.showPanel(ii);
18110 if(this.autoslide && this.slideFn){
18111 clearInterval(this.slideFn);
18112 this.slideFn = window.setInterval(function() {
18113 _this.showPanelNext();
18117 }).createDelegate(this, [i, bullet], true));
18122 setActiveBullet : function(i)
18128 Roo.each(this.el.select('.bullet', true).elements, function(el){
18129 el.removeClass('selected');
18132 var bullet = this.el.select('.bullet-' + i, true).first();
18138 bullet.addClass('selected');
18149 Roo.apply(Roo.bootstrap.TabGroup, {
18153 * register a Navigation Group
18154 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18156 register : function(navgrp)
18158 this.groups[navgrp.navId] = navgrp;
18162 * fetch a Navigation Group based on the navigation ID
18163 * if one does not exist , it will get created.
18164 * @param {string} the navgroup to add
18165 * @returns {Roo.bootstrap.NavGroup} the navgroup
18167 get: function(navId) {
18168 if (typeof(this.groups[navId]) == 'undefined') {
18169 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18171 return this.groups[navId] ;
18186 * @class Roo.bootstrap.TabPanel
18187 * @extends Roo.bootstrap.Component
18188 * Bootstrap TabPanel class
18189 * @cfg {Boolean} active panel active
18190 * @cfg {String} html panel content
18191 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18192 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18193 * @cfg {String} href click to link..
18197 * Create a new TabPanel
18198 * @param {Object} config The config object
18201 Roo.bootstrap.TabPanel = function(config){
18202 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18206 * Fires when the active status changes
18207 * @param {Roo.bootstrap.TabPanel} this
18208 * @param {Boolean} state the new state
18213 * @event beforedeactivate
18214 * Fires before a tab is de-activated - can be used to do validation on a form.
18215 * @param {Roo.bootstrap.TabPanel} this
18216 * @return {Boolean} false if there is an error
18219 'beforedeactivate': true
18222 this.tabId = this.tabId || Roo.id();
18226 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18234 getAutoCreate : function(){
18237 // item is needed for carousel - not sure if it has any effect otherwise
18238 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18239 html: this.html || ''
18243 cfg.cls += ' active';
18247 cfg.tabId = this.tabId;
18254 initEvents: function()
18256 var p = this.parent();
18258 this.navId = this.navId || p.navId;
18260 if (typeof(this.navId) != 'undefined') {
18261 // not really needed.. but just in case.. parent should be a NavGroup.
18262 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18266 var i = tg.tabs.length - 1;
18268 if(this.active && tg.bullets > 0 && i < tg.bullets){
18269 tg.setActiveBullet(i);
18273 this.el.on('click', this.onClick, this);
18276 this.el.on("touchstart", this.onTouchStart, this);
18277 this.el.on("touchmove", this.onTouchMove, this);
18278 this.el.on("touchend", this.onTouchEnd, this);
18283 onRender : function(ct, position)
18285 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18288 setActive : function(state)
18290 Roo.log("panel - set active " + this.tabId + "=" + state);
18292 this.active = state;
18294 this.el.removeClass('active');
18296 } else if (!this.el.hasClass('active')) {
18297 this.el.addClass('active');
18300 this.fireEvent('changed', this, state);
18303 onClick : function(e)
18305 e.preventDefault();
18307 if(!this.href.length){
18311 window.location.href = this.href;
18320 onTouchStart : function(e)
18322 this.swiping = false;
18324 this.startX = e.browserEvent.touches[0].clientX;
18325 this.startY = e.browserEvent.touches[0].clientY;
18328 onTouchMove : function(e)
18330 this.swiping = true;
18332 this.endX = e.browserEvent.touches[0].clientX;
18333 this.endY = e.browserEvent.touches[0].clientY;
18336 onTouchEnd : function(e)
18343 var tabGroup = this.parent();
18345 if(this.endX > this.startX){ // swiping right
18346 tabGroup.showPanelPrev();
18350 if(this.startX > this.endX){ // swiping left
18351 tabGroup.showPanelNext();
18370 * @class Roo.bootstrap.DateField
18371 * @extends Roo.bootstrap.Input
18372 * Bootstrap DateField class
18373 * @cfg {Number} weekStart default 0
18374 * @cfg {String} viewMode default empty, (months|years)
18375 * @cfg {String} minViewMode default empty, (months|years)
18376 * @cfg {Number} startDate default -Infinity
18377 * @cfg {Number} endDate default Infinity
18378 * @cfg {Boolean} todayHighlight default false
18379 * @cfg {Boolean} todayBtn default false
18380 * @cfg {Boolean} calendarWeeks default false
18381 * @cfg {Object} daysOfWeekDisabled default empty
18382 * @cfg {Boolean} singleMode default false (true | false)
18384 * @cfg {Boolean} keyboardNavigation default true
18385 * @cfg {String} language default en
18388 * Create a new DateField
18389 * @param {Object} config The config object
18392 Roo.bootstrap.DateField = function(config){
18393 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18397 * Fires when this field show.
18398 * @param {Roo.bootstrap.DateField} this
18399 * @param {Mixed} date The date value
18404 * Fires when this field hide.
18405 * @param {Roo.bootstrap.DateField} this
18406 * @param {Mixed} date The date value
18411 * Fires when select a date.
18412 * @param {Roo.bootstrap.DateField} this
18413 * @param {Mixed} date The date value
18417 * @event beforeselect
18418 * Fires when before select a date.
18419 * @param {Roo.bootstrap.DateField} this
18420 * @param {Mixed} date The date value
18422 beforeselect : true
18426 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18429 * @cfg {String} format
18430 * The default date format string which can be overriden for localization support. The format must be
18431 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18435 * @cfg {String} altFormats
18436 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18437 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18439 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18447 todayHighlight : false,
18453 keyboardNavigation: true,
18455 calendarWeeks: false,
18457 startDate: -Infinity,
18461 daysOfWeekDisabled: [],
18465 singleMode : false,
18467 UTCDate: function()
18469 return new Date(Date.UTC.apply(Date, arguments));
18472 UTCToday: function()
18474 var today = new Date();
18475 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18478 getDate: function() {
18479 var d = this.getUTCDate();
18480 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18483 getUTCDate: function() {
18487 setDate: function(d) {
18488 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18491 setUTCDate: function(d) {
18493 this.setValue(this.formatDate(this.date));
18496 onRender: function(ct, position)
18499 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18501 this.language = this.language || 'en';
18502 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18503 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18505 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18506 this.format = this.format || 'm/d/y';
18507 this.isInline = false;
18508 this.isInput = true;
18509 this.component = this.el.select('.add-on', true).first() || false;
18510 this.component = (this.component && this.component.length === 0) ? false : this.component;
18511 this.hasInput = this.component && this.inputEl().length;
18513 if (typeof(this.minViewMode === 'string')) {
18514 switch (this.minViewMode) {
18516 this.minViewMode = 1;
18519 this.minViewMode = 2;
18522 this.minViewMode = 0;
18527 if (typeof(this.viewMode === 'string')) {
18528 switch (this.viewMode) {
18541 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18543 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18545 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18547 this.picker().on('mousedown', this.onMousedown, this);
18548 this.picker().on('click', this.onClick, this);
18550 this.picker().addClass('datepicker-dropdown');
18552 this.startViewMode = this.viewMode;
18554 if(this.singleMode){
18555 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18556 v.setVisibilityMode(Roo.Element.DISPLAY);
18560 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18561 v.setStyle('width', '189px');
18565 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18566 if(!this.calendarWeeks){
18571 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18572 v.attr('colspan', function(i, val){
18573 return parseInt(val) + 1;
18578 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18580 this.setStartDate(this.startDate);
18581 this.setEndDate(this.endDate);
18583 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18590 if(this.isInline) {
18595 picker : function()
18597 return this.pickerEl;
18598 // return this.el.select('.datepicker', true).first();
18601 fillDow: function()
18603 var dowCnt = this.weekStart;
18612 if(this.calendarWeeks){
18620 while (dowCnt < this.weekStart + 7) {
18624 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18628 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18631 fillMonths: function()
18634 var months = this.picker().select('>.datepicker-months td', true).first();
18636 months.dom.innerHTML = '';
18642 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18645 months.createChild(month);
18652 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;
18654 if (this.date < this.startDate) {
18655 this.viewDate = new Date(this.startDate);
18656 } else if (this.date > this.endDate) {
18657 this.viewDate = new Date(this.endDate);
18659 this.viewDate = new Date(this.date);
18667 var d = new Date(this.viewDate),
18668 year = d.getUTCFullYear(),
18669 month = d.getUTCMonth(),
18670 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18671 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18672 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18673 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18674 currentDate = this.date && this.date.valueOf(),
18675 today = this.UTCToday();
18677 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18679 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18681 // this.picker.select('>tfoot th.today').
18682 // .text(dates[this.language].today)
18683 // .toggle(this.todayBtn !== false);
18685 this.updateNavArrows();
18688 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18690 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18692 prevMonth.setUTCDate(day);
18694 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18696 var nextMonth = new Date(prevMonth);
18698 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18700 nextMonth = nextMonth.valueOf();
18702 var fillMonths = false;
18704 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18706 while(prevMonth.valueOf() <= nextMonth) {
18709 if (prevMonth.getUTCDay() === this.weekStart) {
18711 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18719 if(this.calendarWeeks){
18720 // ISO 8601: First week contains first thursday.
18721 // ISO also states week starts on Monday, but we can be more abstract here.
18723 // Start of current week: based on weekstart/current date
18724 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18725 // Thursday of this week
18726 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18727 // First Thursday of year, year from thursday
18728 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18729 // Calendar week: ms between thursdays, div ms per day, div 7 days
18730 calWeek = (th - yth) / 864e5 / 7 + 1;
18732 fillMonths.cn.push({
18740 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18742 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18745 if (this.todayHighlight &&
18746 prevMonth.getUTCFullYear() == today.getFullYear() &&
18747 prevMonth.getUTCMonth() == today.getMonth() &&
18748 prevMonth.getUTCDate() == today.getDate()) {
18749 clsName += ' today';
18752 if (currentDate && prevMonth.valueOf() === currentDate) {
18753 clsName += ' active';
18756 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18757 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18758 clsName += ' disabled';
18761 fillMonths.cn.push({
18763 cls: 'day ' + clsName,
18764 html: prevMonth.getDate()
18767 prevMonth.setDate(prevMonth.getDate()+1);
18770 var currentYear = this.date && this.date.getUTCFullYear();
18771 var currentMonth = this.date && this.date.getUTCMonth();
18773 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18775 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18776 v.removeClass('active');
18778 if(currentYear === year && k === currentMonth){
18779 v.addClass('active');
18782 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18783 v.addClass('disabled');
18789 year = parseInt(year/10, 10) * 10;
18791 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18793 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18796 for (var i = -1; i < 11; i++) {
18797 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18799 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18807 showMode: function(dir)
18810 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18813 Roo.each(this.picker().select('>div',true).elements, function(v){
18814 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18817 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18822 if(this.isInline) {
18826 this.picker().removeClass(['bottom', 'top']);
18828 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18830 * place to the top of element!
18834 this.picker().addClass('top');
18835 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18840 this.picker().addClass('bottom');
18842 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18845 parseDate : function(value)
18847 if(!value || value instanceof Date){
18850 var v = Date.parseDate(value, this.format);
18851 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18852 v = Date.parseDate(value, 'Y-m-d');
18854 if(!v && this.altFormats){
18855 if(!this.altFormatsArray){
18856 this.altFormatsArray = this.altFormats.split("|");
18858 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18859 v = Date.parseDate(value, this.altFormatsArray[i]);
18865 formatDate : function(date, fmt)
18867 return (!date || !(date instanceof Date)) ?
18868 date : date.dateFormat(fmt || this.format);
18871 onFocus : function()
18873 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18877 onBlur : function()
18879 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18881 var d = this.inputEl().getValue();
18888 showPopup : function()
18890 this.picker().show();
18894 this.fireEvent('showpopup', this, this.date);
18897 hidePopup : function()
18899 if(this.isInline) {
18902 this.picker().hide();
18903 this.viewMode = this.startViewMode;
18906 this.fireEvent('hidepopup', this, this.date);
18910 onMousedown: function(e)
18912 e.stopPropagation();
18913 e.preventDefault();
18918 Roo.bootstrap.DateField.superclass.keyup.call(this);
18922 setValue: function(v)
18924 if(this.fireEvent('beforeselect', this, v) !== false){
18925 var d = new Date(this.parseDate(v) ).clearTime();
18927 if(isNaN(d.getTime())){
18928 this.date = this.viewDate = '';
18929 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18933 v = this.formatDate(d);
18935 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18937 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18941 this.fireEvent('select', this, this.date);
18945 getValue: function()
18947 return this.formatDate(this.date);
18950 fireKey: function(e)
18952 if (!this.picker().isVisible()){
18953 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18959 var dateChanged = false,
18961 newDate, newViewDate;
18966 e.preventDefault();
18970 if (!this.keyboardNavigation) {
18973 dir = e.keyCode == 37 ? -1 : 1;
18976 newDate = this.moveYear(this.date, dir);
18977 newViewDate = this.moveYear(this.viewDate, dir);
18978 } else if (e.shiftKey){
18979 newDate = this.moveMonth(this.date, dir);
18980 newViewDate = this.moveMonth(this.viewDate, dir);
18982 newDate = new Date(this.date);
18983 newDate.setUTCDate(this.date.getUTCDate() + dir);
18984 newViewDate = new Date(this.viewDate);
18985 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18987 if (this.dateWithinRange(newDate)){
18988 this.date = newDate;
18989 this.viewDate = newViewDate;
18990 this.setValue(this.formatDate(this.date));
18992 e.preventDefault();
18993 dateChanged = true;
18998 if (!this.keyboardNavigation) {
19001 dir = e.keyCode == 38 ? -1 : 1;
19003 newDate = this.moveYear(this.date, dir);
19004 newViewDate = this.moveYear(this.viewDate, dir);
19005 } else if (e.shiftKey){
19006 newDate = this.moveMonth(this.date, dir);
19007 newViewDate = this.moveMonth(this.viewDate, dir);
19009 newDate = new Date(this.date);
19010 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19011 newViewDate = new Date(this.viewDate);
19012 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19014 if (this.dateWithinRange(newDate)){
19015 this.date = newDate;
19016 this.viewDate = newViewDate;
19017 this.setValue(this.formatDate(this.date));
19019 e.preventDefault();
19020 dateChanged = true;
19024 this.setValue(this.formatDate(this.date));
19026 e.preventDefault();
19029 this.setValue(this.formatDate(this.date));
19043 onClick: function(e)
19045 e.stopPropagation();
19046 e.preventDefault();
19048 var target = e.getTarget();
19050 if(target.nodeName.toLowerCase() === 'i'){
19051 target = Roo.get(target).dom.parentNode;
19054 var nodeName = target.nodeName;
19055 var className = target.className;
19056 var html = target.innerHTML;
19057 //Roo.log(nodeName);
19059 switch(nodeName.toLowerCase()) {
19061 switch(className) {
19067 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19068 switch(this.viewMode){
19070 this.viewDate = this.moveMonth(this.viewDate, dir);
19074 this.viewDate = this.moveYear(this.viewDate, dir);
19080 var date = new Date();
19081 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19083 this.setValue(this.formatDate(this.date));
19090 if (className.indexOf('disabled') < 0) {
19091 this.viewDate.setUTCDate(1);
19092 if (className.indexOf('month') > -1) {
19093 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19095 var year = parseInt(html, 10) || 0;
19096 this.viewDate.setUTCFullYear(year);
19100 if(this.singleMode){
19101 this.setValue(this.formatDate(this.viewDate));
19112 //Roo.log(className);
19113 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19114 var day = parseInt(html, 10) || 1;
19115 var year = this.viewDate.getUTCFullYear(),
19116 month = this.viewDate.getUTCMonth();
19118 if (className.indexOf('old') > -1) {
19125 } else if (className.indexOf('new') > -1) {
19133 //Roo.log([year,month,day]);
19134 this.date = this.UTCDate(year, month, day,0,0,0,0);
19135 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19137 //Roo.log(this.formatDate(this.date));
19138 this.setValue(this.formatDate(this.date));
19145 setStartDate: function(startDate)
19147 this.startDate = startDate || -Infinity;
19148 if (this.startDate !== -Infinity) {
19149 this.startDate = this.parseDate(this.startDate);
19152 this.updateNavArrows();
19155 setEndDate: function(endDate)
19157 this.endDate = endDate || Infinity;
19158 if (this.endDate !== Infinity) {
19159 this.endDate = this.parseDate(this.endDate);
19162 this.updateNavArrows();
19165 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19167 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19168 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19169 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19171 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19172 return parseInt(d, 10);
19175 this.updateNavArrows();
19178 updateNavArrows: function()
19180 if(this.singleMode){
19184 var d = new Date(this.viewDate),
19185 year = d.getUTCFullYear(),
19186 month = d.getUTCMonth();
19188 Roo.each(this.picker().select('.prev', true).elements, function(v){
19190 switch (this.viewMode) {
19193 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19199 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19206 Roo.each(this.picker().select('.next', true).elements, function(v){
19208 switch (this.viewMode) {
19211 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19217 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19225 moveMonth: function(date, dir)
19230 var new_date = new Date(date.valueOf()),
19231 day = new_date.getUTCDate(),
19232 month = new_date.getUTCMonth(),
19233 mag = Math.abs(dir),
19235 dir = dir > 0 ? 1 : -1;
19238 // If going back one month, make sure month is not current month
19239 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19241 return new_date.getUTCMonth() == month;
19243 // If going forward one month, make sure month is as expected
19244 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19246 return new_date.getUTCMonth() != new_month;
19248 new_month = month + dir;
19249 new_date.setUTCMonth(new_month);
19250 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19251 if (new_month < 0 || new_month > 11) {
19252 new_month = (new_month + 12) % 12;
19255 // For magnitudes >1, move one month at a time...
19256 for (var i=0; i<mag; i++) {
19257 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19258 new_date = this.moveMonth(new_date, dir);
19260 // ...then reset the day, keeping it in the new month
19261 new_month = new_date.getUTCMonth();
19262 new_date.setUTCDate(day);
19264 return new_month != new_date.getUTCMonth();
19267 // Common date-resetting loop -- if date is beyond end of month, make it
19270 new_date.setUTCDate(--day);
19271 new_date.setUTCMonth(new_month);
19276 moveYear: function(date, dir)
19278 return this.moveMonth(date, dir*12);
19281 dateWithinRange: function(date)
19283 return date >= this.startDate && date <= this.endDate;
19289 this.picker().remove();
19292 validateValue : function(value)
19294 if(this.getVisibilityEl().hasClass('hidden')){
19298 if(value.length < 1) {
19299 if(this.allowBlank){
19305 if(value.length < this.minLength){
19308 if(value.length > this.maxLength){
19312 var vt = Roo.form.VTypes;
19313 if(!vt[this.vtype](value, this)){
19317 if(typeof this.validator == "function"){
19318 var msg = this.validator(value);
19324 if(this.regex && !this.regex.test(value)){
19328 if(typeof(this.parseDate(value)) == 'undefined'){
19332 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19336 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19346 this.date = this.viewDate = '';
19348 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19353 Roo.apply(Roo.bootstrap.DateField, {
19364 html: '<i class="fa fa-arrow-left"/>'
19374 html: '<i class="fa fa-arrow-right"/>'
19416 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19417 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19418 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19419 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19420 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19433 navFnc: 'FullYear',
19438 navFnc: 'FullYear',
19443 Roo.apply(Roo.bootstrap.DateField, {
19447 cls: 'datepicker dropdown-menu roo-dynamic',
19451 cls: 'datepicker-days',
19455 cls: 'table-condensed',
19457 Roo.bootstrap.DateField.head,
19461 Roo.bootstrap.DateField.footer
19468 cls: 'datepicker-months',
19472 cls: 'table-condensed',
19474 Roo.bootstrap.DateField.head,
19475 Roo.bootstrap.DateField.content,
19476 Roo.bootstrap.DateField.footer
19483 cls: 'datepicker-years',
19487 cls: 'table-condensed',
19489 Roo.bootstrap.DateField.head,
19490 Roo.bootstrap.DateField.content,
19491 Roo.bootstrap.DateField.footer
19510 * @class Roo.bootstrap.TimeField
19511 * @extends Roo.bootstrap.Input
19512 * Bootstrap DateField class
19516 * Create a new TimeField
19517 * @param {Object} config The config object
19520 Roo.bootstrap.TimeField = function(config){
19521 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19525 * Fires when this field show.
19526 * @param {Roo.bootstrap.DateField} thisthis
19527 * @param {Mixed} date The date value
19532 * Fires when this field hide.
19533 * @param {Roo.bootstrap.DateField} this
19534 * @param {Mixed} date The date value
19539 * Fires when select a date.
19540 * @param {Roo.bootstrap.DateField} this
19541 * @param {Mixed} date The date value
19547 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19550 * @cfg {String} format
19551 * The default time format string which can be overriden for localization support. The format must be
19552 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19556 onRender: function(ct, position)
19559 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19561 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19563 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19565 this.pop = this.picker().select('>.datepicker-time',true).first();
19566 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19568 this.picker().on('mousedown', this.onMousedown, this);
19569 this.picker().on('click', this.onClick, this);
19571 this.picker().addClass('datepicker-dropdown');
19576 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19577 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19578 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19579 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19580 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19581 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19585 fireKey: function(e){
19586 if (!this.picker().isVisible()){
19587 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19593 e.preventDefault();
19601 this.onTogglePeriod();
19604 this.onIncrementMinutes();
19607 this.onDecrementMinutes();
19616 onClick: function(e) {
19617 e.stopPropagation();
19618 e.preventDefault();
19621 picker : function()
19623 return this.el.select('.datepicker', true).first();
19626 fillTime: function()
19628 var time = this.pop.select('tbody', true).first();
19630 time.dom.innerHTML = '';
19645 cls: 'hours-up glyphicon glyphicon-chevron-up'
19665 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19686 cls: 'timepicker-hour',
19701 cls: 'timepicker-minute',
19716 cls: 'btn btn-primary period',
19738 cls: 'hours-down glyphicon glyphicon-chevron-down'
19758 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19776 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19783 var hours = this.time.getHours();
19784 var minutes = this.time.getMinutes();
19797 hours = hours - 12;
19801 hours = '0' + hours;
19805 minutes = '0' + minutes;
19808 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19809 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19810 this.pop.select('button', true).first().dom.innerHTML = period;
19816 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19818 var cls = ['bottom'];
19820 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19827 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19832 this.picker().addClass(cls.join('-'));
19836 Roo.each(cls, function(c){
19838 _this.picker().setTop(_this.inputEl().getHeight());
19842 _this.picker().setTop(0 - _this.picker().getHeight());
19847 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19851 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19858 onFocus : function()
19860 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19864 onBlur : function()
19866 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19872 this.picker().show();
19877 this.fireEvent('show', this, this.date);
19882 this.picker().hide();
19885 this.fireEvent('hide', this, this.date);
19888 setTime : function()
19891 this.setValue(this.time.format(this.format));
19893 this.fireEvent('select', this, this.date);
19898 onMousedown: function(e){
19899 e.stopPropagation();
19900 e.preventDefault();
19903 onIncrementHours: function()
19905 Roo.log('onIncrementHours');
19906 this.time = this.time.add(Date.HOUR, 1);
19911 onDecrementHours: function()
19913 Roo.log('onDecrementHours');
19914 this.time = this.time.add(Date.HOUR, -1);
19918 onIncrementMinutes: function()
19920 Roo.log('onIncrementMinutes');
19921 this.time = this.time.add(Date.MINUTE, 1);
19925 onDecrementMinutes: function()
19927 Roo.log('onDecrementMinutes');
19928 this.time = this.time.add(Date.MINUTE, -1);
19932 onTogglePeriod: function()
19934 Roo.log('onTogglePeriod');
19935 this.time = this.time.add(Date.HOUR, 12);
19942 Roo.apply(Roo.bootstrap.TimeField, {
19972 cls: 'btn btn-info ok',
19984 Roo.apply(Roo.bootstrap.TimeField, {
19988 cls: 'datepicker dropdown-menu',
19992 cls: 'datepicker-time',
19996 cls: 'table-condensed',
19998 Roo.bootstrap.TimeField.content,
19999 Roo.bootstrap.TimeField.footer
20018 * @class Roo.bootstrap.MonthField
20019 * @extends Roo.bootstrap.Input
20020 * Bootstrap MonthField class
20022 * @cfg {String} language default en
20025 * Create a new MonthField
20026 * @param {Object} config The config object
20029 Roo.bootstrap.MonthField = function(config){
20030 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20035 * Fires when this field show.
20036 * @param {Roo.bootstrap.MonthField} this
20037 * @param {Mixed} date The date value
20042 * Fires when this field hide.
20043 * @param {Roo.bootstrap.MonthField} this
20044 * @param {Mixed} date The date value
20049 * Fires when select a date.
20050 * @param {Roo.bootstrap.MonthField} this
20051 * @param {String} oldvalue The old value
20052 * @param {String} newvalue The new value
20058 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20060 onRender: function(ct, position)
20063 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20065 this.language = this.language || 'en';
20066 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20067 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20069 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20070 this.isInline = false;
20071 this.isInput = true;
20072 this.component = this.el.select('.add-on', true).first() || false;
20073 this.component = (this.component && this.component.length === 0) ? false : this.component;
20074 this.hasInput = this.component && this.inputEL().length;
20076 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20078 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20080 this.picker().on('mousedown', this.onMousedown, this);
20081 this.picker().on('click', this.onClick, this);
20083 this.picker().addClass('datepicker-dropdown');
20085 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20086 v.setStyle('width', '189px');
20093 if(this.isInline) {
20099 setValue: function(v, suppressEvent)
20101 var o = this.getValue();
20103 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20107 if(suppressEvent !== true){
20108 this.fireEvent('select', this, o, v);
20113 getValue: function()
20118 onClick: function(e)
20120 e.stopPropagation();
20121 e.preventDefault();
20123 var target = e.getTarget();
20125 if(target.nodeName.toLowerCase() === 'i'){
20126 target = Roo.get(target).dom.parentNode;
20129 var nodeName = target.nodeName;
20130 var className = target.className;
20131 var html = target.innerHTML;
20133 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20137 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20139 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20145 picker : function()
20147 return this.pickerEl;
20150 fillMonths: function()
20153 var months = this.picker().select('>.datepicker-months td', true).first();
20155 months.dom.innerHTML = '';
20161 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20164 months.createChild(month);
20173 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20174 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20177 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20178 e.removeClass('active');
20180 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20181 e.addClass('active');
20188 if(this.isInline) {
20192 this.picker().removeClass(['bottom', 'top']);
20194 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20196 * place to the top of element!
20200 this.picker().addClass('top');
20201 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20206 this.picker().addClass('bottom');
20208 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20211 onFocus : function()
20213 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20217 onBlur : function()
20219 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20221 var d = this.inputEl().getValue();
20230 this.picker().show();
20231 this.picker().select('>.datepicker-months', true).first().show();
20235 this.fireEvent('show', this, this.date);
20240 if(this.isInline) {
20243 this.picker().hide();
20244 this.fireEvent('hide', this, this.date);
20248 onMousedown: function(e)
20250 e.stopPropagation();
20251 e.preventDefault();
20256 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20260 fireKey: function(e)
20262 if (!this.picker().isVisible()){
20263 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20274 e.preventDefault();
20278 dir = e.keyCode == 37 ? -1 : 1;
20280 this.vIndex = this.vIndex + dir;
20282 if(this.vIndex < 0){
20286 if(this.vIndex > 11){
20290 if(isNaN(this.vIndex)){
20294 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20300 dir = e.keyCode == 38 ? -1 : 1;
20302 this.vIndex = this.vIndex + dir * 4;
20304 if(this.vIndex < 0){
20308 if(this.vIndex > 11){
20312 if(isNaN(this.vIndex)){
20316 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20321 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20322 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20326 e.preventDefault();
20329 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20330 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20346 this.picker().remove();
20351 Roo.apply(Roo.bootstrap.MonthField, {
20370 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20371 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20376 Roo.apply(Roo.bootstrap.MonthField, {
20380 cls: 'datepicker dropdown-menu roo-dynamic',
20384 cls: 'datepicker-months',
20388 cls: 'table-condensed',
20390 Roo.bootstrap.DateField.content
20410 * @class Roo.bootstrap.CheckBox
20411 * @extends Roo.bootstrap.Input
20412 * Bootstrap CheckBox class
20414 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20415 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20416 * @cfg {String} boxLabel The text that appears beside the checkbox
20417 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20418 * @cfg {Boolean} checked initnal the element
20419 * @cfg {Boolean} inline inline the element (default false)
20420 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20421 * @cfg {String} tooltip label tooltip
20424 * Create a new CheckBox
20425 * @param {Object} config The config object
20428 Roo.bootstrap.CheckBox = function(config){
20429 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20434 * Fires when the element is checked or unchecked.
20435 * @param {Roo.bootstrap.CheckBox} this This input
20436 * @param {Boolean} checked The new checked value
20441 * Fires when the element is click.
20442 * @param {Roo.bootstrap.CheckBox} this This input
20449 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20451 inputType: 'checkbox',
20460 getAutoCreate : function()
20462 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20468 cfg.cls = 'form-group ' + this.inputType; //input-group
20471 cfg.cls += ' ' + this.inputType + '-inline';
20477 type : this.inputType,
20478 value : this.inputValue,
20479 cls : 'roo-' + this.inputType, //'form-box',
20480 placeholder : this.placeholder || ''
20484 if(this.inputType != 'radio'){
20488 cls : 'roo-hidden-value',
20489 value : this.checked ? this.inputValue : this.valueOff
20494 if (this.weight) { // Validity check?
20495 cfg.cls += " " + this.inputType + "-" + this.weight;
20498 if (this.disabled) {
20499 input.disabled=true;
20503 input.checked = this.checked;
20508 input.name = this.name;
20510 if(this.inputType != 'radio'){
20511 hidden.name = this.name;
20512 input.name = '_hidden_' + this.name;
20517 input.cls += ' input-' + this.size;
20522 ['xs','sm','md','lg'].map(function(size){
20523 if (settings[size]) {
20524 cfg.cls += ' col-' + size + '-' + settings[size];
20528 var inputblock = input;
20530 if (this.before || this.after) {
20533 cls : 'input-group',
20538 inputblock.cn.push({
20540 cls : 'input-group-addon',
20545 inputblock.cn.push(input);
20547 if(this.inputType != 'radio'){
20548 inputblock.cn.push(hidden);
20552 inputblock.cn.push({
20554 cls : 'input-group-addon',
20561 if (align ==='left' && this.fieldLabel.length) {
20562 // Roo.log("left and has label");
20567 cls : 'control-label',
20568 html : this.fieldLabel
20578 if(this.labelWidth > 12){
20579 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20582 if(this.labelWidth < 13 && this.labelmd == 0){
20583 this.labelmd = this.labelWidth;
20586 if(this.labellg > 0){
20587 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20588 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20591 if(this.labelmd > 0){
20592 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20593 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20596 if(this.labelsm > 0){
20597 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20598 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20601 if(this.labelxs > 0){
20602 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20603 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20606 } else if ( this.fieldLabel.length) {
20607 // Roo.log(" label");
20611 tag: this.boxLabel ? 'span' : 'label',
20613 cls: 'control-label box-input-label',
20614 //cls : 'input-group-addon',
20615 html : this.fieldLabel
20624 // Roo.log(" no label && no align");
20625 cfg.cn = [ inputblock ] ;
20631 var boxLabelCfg = {
20633 //'for': id, // box label is handled by onclick - so no for...
20635 html: this.boxLabel
20639 boxLabelCfg.tooltip = this.tooltip;
20642 cfg.cn.push(boxLabelCfg);
20645 if(this.inputType != 'radio'){
20646 cfg.cn.push(hidden);
20654 * return the real input element.
20656 inputEl: function ()
20658 return this.el.select('input.roo-' + this.inputType,true).first();
20660 hiddenEl: function ()
20662 return this.el.select('input.roo-hidden-value',true).first();
20665 labelEl: function()
20667 return this.el.select('label.control-label',true).first();
20669 /* depricated... */
20673 return this.labelEl();
20676 boxLabelEl: function()
20678 return this.el.select('label.box-label',true).first();
20681 initEvents : function()
20683 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20685 this.inputEl().on('click', this.onClick, this);
20687 if (this.boxLabel) {
20688 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20691 this.startValue = this.getValue();
20694 Roo.bootstrap.CheckBox.register(this);
20698 onClick : function(e)
20700 if(this.fireEvent('click', this, e) !== false){
20701 this.setChecked(!this.checked);
20706 setChecked : function(state,suppressEvent)
20708 this.startValue = this.getValue();
20710 if(this.inputType == 'radio'){
20712 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20713 e.dom.checked = false;
20716 this.inputEl().dom.checked = true;
20718 this.inputEl().dom.value = this.inputValue;
20720 if(suppressEvent !== true){
20721 this.fireEvent('check', this, true);
20729 this.checked = state;
20731 this.inputEl().dom.checked = state;
20734 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20736 if(suppressEvent !== true){
20737 this.fireEvent('check', this, state);
20743 getValue : function()
20745 if(this.inputType == 'radio'){
20746 return this.getGroupValue();
20749 return this.hiddenEl().dom.value;
20753 getGroupValue : function()
20755 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20759 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20762 setValue : function(v,suppressEvent)
20764 if(this.inputType == 'radio'){
20765 this.setGroupValue(v, suppressEvent);
20769 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20774 setGroupValue : function(v, suppressEvent)
20776 this.startValue = this.getValue();
20778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20779 e.dom.checked = false;
20781 if(e.dom.value == v){
20782 e.dom.checked = true;
20786 if(suppressEvent !== true){
20787 this.fireEvent('check', this, true);
20795 validate : function()
20797 if(this.getVisibilityEl().hasClass('hidden')){
20803 (this.inputType == 'radio' && this.validateRadio()) ||
20804 (this.inputType == 'checkbox' && this.validateCheckbox())
20810 this.markInvalid();
20814 validateRadio : function()
20816 if(this.getVisibilityEl().hasClass('hidden')){
20820 if(this.allowBlank){
20826 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20827 if(!e.dom.checked){
20839 validateCheckbox : function()
20842 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20843 //return (this.getValue() == this.inputValue) ? true : false;
20846 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20854 for(var i in group){
20855 if(group[i].el.isVisible(true)){
20863 for(var i in group){
20868 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20875 * Mark this field as valid
20877 markValid : function()
20881 this.fireEvent('valid', this);
20883 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20886 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20893 if(this.inputType == 'radio'){
20894 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20895 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20896 e.findParent('.form-group', false, true).addClass(_this.validClass);
20903 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20904 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20908 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20914 for(var i in group){
20915 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20916 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20921 * Mark this field as invalid
20922 * @param {String} msg The validation message
20924 markInvalid : function(msg)
20926 if(this.allowBlank){
20932 this.fireEvent('invalid', this, msg);
20934 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20937 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20941 label.markInvalid();
20944 if(this.inputType == 'radio'){
20945 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20946 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20947 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20954 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20955 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20959 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20965 for(var i in group){
20966 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20967 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20972 clearInvalid : function()
20974 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20976 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20978 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20980 if (label && label.iconEl) {
20981 label.iconEl.removeClass(label.validClass);
20982 label.iconEl.removeClass(label.invalidClass);
20986 disable : function()
20988 if(this.inputType != 'radio'){
20989 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20996 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20997 _this.getActionEl().addClass(this.disabledClass);
20998 e.dom.disabled = true;
21002 this.disabled = true;
21003 this.fireEvent("disable", this);
21007 enable : function()
21009 if(this.inputType != 'radio'){
21010 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21017 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21018 _this.getActionEl().removeClass(this.disabledClass);
21019 e.dom.disabled = false;
21023 this.disabled = false;
21024 this.fireEvent("enable", this);
21028 setBoxLabel : function(v)
21033 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21039 Roo.apply(Roo.bootstrap.CheckBox, {
21044 * register a CheckBox Group
21045 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21047 register : function(checkbox)
21049 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21050 this.groups[checkbox.groupId] = {};
21053 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21057 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21061 * fetch a CheckBox Group based on the group ID
21062 * @param {string} the group ID
21063 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21065 get: function(groupId) {
21066 if (typeof(this.groups[groupId]) == 'undefined') {
21070 return this.groups[groupId] ;
21083 * @class Roo.bootstrap.Radio
21084 * @extends Roo.bootstrap.Component
21085 * Bootstrap Radio class
21086 * @cfg {String} boxLabel - the label associated
21087 * @cfg {String} value - the value of radio
21090 * Create a new Radio
21091 * @param {Object} config The config object
21093 Roo.bootstrap.Radio = function(config){
21094 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21098 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21104 getAutoCreate : function()
21108 cls : 'form-group radio',
21113 html : this.boxLabel
21121 initEvents : function()
21123 this.parent().register(this);
21125 this.el.on('click', this.onClick, this);
21129 onClick : function(e)
21131 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21132 this.setChecked(true);
21136 setChecked : function(state, suppressEvent)
21138 this.parent().setValue(this.value, suppressEvent);
21142 setBoxLabel : function(v)
21147 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21162 * @class Roo.bootstrap.SecurePass
21163 * @extends Roo.bootstrap.Input
21164 * Bootstrap SecurePass class
21168 * Create a new SecurePass
21169 * @param {Object} config The config object
21172 Roo.bootstrap.SecurePass = function (config) {
21173 // these go here, so the translation tool can replace them..
21175 PwdEmpty: "Please type a password, and then retype it to confirm.",
21176 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21177 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21178 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21179 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21180 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21181 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21182 TooWeak: "Your password is Too Weak."
21184 this.meterLabel = "Password strength:";
21185 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21186 this.meterClass = [
21187 "roo-password-meter-tooweak",
21188 "roo-password-meter-weak",
21189 "roo-password-meter-medium",
21190 "roo-password-meter-strong",
21191 "roo-password-meter-grey"
21196 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21199 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21201 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21203 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21204 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21205 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21206 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21207 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21208 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21209 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21219 * @cfg {String/Object} Label for the strength meter (defaults to
21220 * 'Password strength:')
21225 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21226 * ['Weak', 'Medium', 'Strong'])
21229 pwdStrengths: false,
21242 initEvents: function ()
21244 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21246 if (this.el.is('input[type=password]') && Roo.isSafari) {
21247 this.el.on('keydown', this.SafariOnKeyDown, this);
21250 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21253 onRender: function (ct, position)
21255 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21256 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21257 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21259 this.trigger.createChild({
21264 cls: 'roo-password-meter-grey col-xs-12',
21267 //width: this.meterWidth + 'px'
21271 cls: 'roo-password-meter-text'
21277 if (this.hideTrigger) {
21278 this.trigger.setDisplayed(false);
21280 this.setSize(this.width || '', this.height || '');
21283 onDestroy: function ()
21285 if (this.trigger) {
21286 this.trigger.removeAllListeners();
21287 this.trigger.remove();
21290 this.wrap.remove();
21292 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21295 checkStrength: function ()
21297 var pwd = this.inputEl().getValue();
21298 if (pwd == this._lastPwd) {
21303 if (this.ClientSideStrongPassword(pwd)) {
21305 } else if (this.ClientSideMediumPassword(pwd)) {
21307 } else if (this.ClientSideWeakPassword(pwd)) {
21313 Roo.log('strength1: ' + strength);
21315 //var pm = this.trigger.child('div/div/div').dom;
21316 var pm = this.trigger.child('div/div');
21317 pm.removeClass(this.meterClass);
21318 pm.addClass(this.meterClass[strength]);
21321 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21323 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21325 this._lastPwd = pwd;
21329 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21331 this._lastPwd = '';
21333 var pm = this.trigger.child('div/div');
21334 pm.removeClass(this.meterClass);
21335 pm.addClass('roo-password-meter-grey');
21338 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21341 this.inputEl().dom.type='password';
21344 validateValue: function (value)
21347 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21350 if (value.length == 0) {
21351 if (this.allowBlank) {
21352 this.clearInvalid();
21356 this.markInvalid(this.errors.PwdEmpty);
21357 this.errorMsg = this.errors.PwdEmpty;
21365 if ('[\x21-\x7e]*'.match(value)) {
21366 this.markInvalid(this.errors.PwdBadChar);
21367 this.errorMsg = this.errors.PwdBadChar;
21370 if (value.length < 6) {
21371 this.markInvalid(this.errors.PwdShort);
21372 this.errorMsg = this.errors.PwdShort;
21375 if (value.length > 16) {
21376 this.markInvalid(this.errors.PwdLong);
21377 this.errorMsg = this.errors.PwdLong;
21381 if (this.ClientSideStrongPassword(value)) {
21383 } else if (this.ClientSideMediumPassword(value)) {
21385 } else if (this.ClientSideWeakPassword(value)) {
21392 if (strength < 2) {
21393 //this.markInvalid(this.errors.TooWeak);
21394 this.errorMsg = this.errors.TooWeak;
21399 console.log('strength2: ' + strength);
21401 //var pm = this.trigger.child('div/div/div').dom;
21403 var pm = this.trigger.child('div/div');
21404 pm.removeClass(this.meterClass);
21405 pm.addClass(this.meterClass[strength]);
21407 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21409 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21411 this.errorMsg = '';
21415 CharacterSetChecks: function (type)
21418 this.fResult = false;
21421 isctype: function (character, type)
21424 case this.kCapitalLetter:
21425 if (character >= 'A' && character <= 'Z') {
21430 case this.kSmallLetter:
21431 if (character >= 'a' && character <= 'z') {
21437 if (character >= '0' && character <= '9') {
21442 case this.kPunctuation:
21443 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21454 IsLongEnough: function (pwd, size)
21456 return !(pwd == null || isNaN(size) || pwd.length < size);
21459 SpansEnoughCharacterSets: function (word, nb)
21461 if (!this.IsLongEnough(word, nb))
21466 var characterSetChecks = new Array(
21467 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21468 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21471 for (var index = 0; index < word.length; ++index) {
21472 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21473 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21474 characterSetChecks[nCharSet].fResult = true;
21481 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21482 if (characterSetChecks[nCharSet].fResult) {
21487 if (nCharSets < nb) {
21493 ClientSideStrongPassword: function (pwd)
21495 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21498 ClientSideMediumPassword: function (pwd)
21500 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21503 ClientSideWeakPassword: function (pwd)
21505 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21508 })//<script type="text/javascript">
21511 * Based Ext JS Library 1.1.1
21512 * Copyright(c) 2006-2007, Ext JS, LLC.
21518 * @class Roo.HtmlEditorCore
21519 * @extends Roo.Component
21520 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21522 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21525 Roo.HtmlEditorCore = function(config){
21528 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21533 * @event initialize
21534 * Fires when the editor is fully initialized (including the iframe)
21535 * @param {Roo.HtmlEditorCore} this
21540 * Fires when the editor is first receives the focus. Any insertion must wait
21541 * until after this event.
21542 * @param {Roo.HtmlEditorCore} this
21546 * @event beforesync
21547 * Fires before the textarea is updated with content from the editor iframe. Return false
21548 * to cancel the sync.
21549 * @param {Roo.HtmlEditorCore} this
21550 * @param {String} html
21554 * @event beforepush
21555 * Fires before the iframe editor is updated with content from the textarea. Return false
21556 * to cancel the push.
21557 * @param {Roo.HtmlEditorCore} this
21558 * @param {String} html
21563 * Fires when the textarea is updated with content from the editor iframe.
21564 * @param {Roo.HtmlEditorCore} this
21565 * @param {String} html
21570 * Fires when the iframe editor is updated with content from the textarea.
21571 * @param {Roo.HtmlEditorCore} this
21572 * @param {String} html
21577 * @event editorevent
21578 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21579 * @param {Roo.HtmlEditorCore} this
21585 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21587 // defaults : white / black...
21588 this.applyBlacklists();
21595 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21599 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21605 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21610 * @cfg {Number} height (in pixels)
21614 * @cfg {Number} width (in pixels)
21619 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21622 stylesheets: false,
21627 // private properties
21628 validationEvent : false,
21630 initialized : false,
21632 sourceEditMode : false,
21633 onFocus : Roo.emptyFn,
21635 hideMode:'offsets',
21639 // blacklist + whitelisted elements..
21646 * Protected method that will not generally be called directly. It
21647 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21648 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21650 getDocMarkup : function(){
21654 // inherit styels from page...??
21655 if (this.stylesheets === false) {
21657 Roo.get(document.head).select('style').each(function(node) {
21658 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21661 Roo.get(document.head).select('link').each(function(node) {
21662 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21665 } else if (!this.stylesheets.length) {
21667 st = '<style type="text/css">' +
21668 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21671 st = '<style type="text/css">' +
21676 st += '<style type="text/css">' +
21677 'IMG { cursor: pointer } ' +
21680 var cls = 'roo-htmleditor-body';
21682 if(this.bodyCls.length){
21683 cls += ' ' + this.bodyCls;
21686 return '<html><head>' + st +
21687 //<style type="text/css">' +
21688 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21690 ' </head><body class="' + cls + '"></body></html>';
21694 onRender : function(ct, position)
21697 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21698 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21701 this.el.dom.style.border = '0 none';
21702 this.el.dom.setAttribute('tabIndex', -1);
21703 this.el.addClass('x-hidden hide');
21707 if(Roo.isIE){ // fix IE 1px bogus margin
21708 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21712 this.frameId = Roo.id();
21716 var iframe = this.owner.wrap.createChild({
21718 cls: 'form-control', // bootstrap..
21720 name: this.frameId,
21721 frameBorder : 'no',
21722 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21727 this.iframe = iframe.dom;
21729 this.assignDocWin();
21731 this.doc.designMode = 'on';
21734 this.doc.write(this.getDocMarkup());
21738 var task = { // must defer to wait for browser to be ready
21740 //console.log("run task?" + this.doc.readyState);
21741 this.assignDocWin();
21742 if(this.doc.body || this.doc.readyState == 'complete'){
21744 this.doc.designMode="on";
21748 Roo.TaskMgr.stop(task);
21749 this.initEditor.defer(10, this);
21756 Roo.TaskMgr.start(task);
21761 onResize : function(w, h)
21763 Roo.log('resize: ' +w + ',' + h );
21764 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21768 if(typeof w == 'number'){
21770 this.iframe.style.width = w + 'px';
21772 if(typeof h == 'number'){
21774 this.iframe.style.height = h + 'px';
21776 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21783 * Toggles the editor between standard and source edit mode.
21784 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21786 toggleSourceEdit : function(sourceEditMode){
21788 this.sourceEditMode = sourceEditMode === true;
21790 if(this.sourceEditMode){
21792 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21795 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21796 //this.iframe.className = '';
21799 //this.setSize(this.owner.wrap.getSize());
21800 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21807 * Protected method that will not generally be called directly. If you need/want
21808 * custom HTML cleanup, this is the method you should override.
21809 * @param {String} html The HTML to be cleaned
21810 * return {String} The cleaned HTML
21812 cleanHtml : function(html){
21813 html = String(html);
21814 if(html.length > 5){
21815 if(Roo.isSafari){ // strip safari nonsense
21816 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21819 if(html == ' '){
21826 * HTML Editor -> Textarea
21827 * Protected method that will not generally be called directly. Syncs the contents
21828 * of the editor iframe with the textarea.
21830 syncValue : function(){
21831 if(this.initialized){
21832 var bd = (this.doc.body || this.doc.documentElement);
21833 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21834 var html = bd.innerHTML;
21836 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21837 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21839 html = '<div style="'+m[0]+'">' + html + '</div>';
21842 html = this.cleanHtml(html);
21843 // fix up the special chars.. normaly like back quotes in word...
21844 // however we do not want to do this with chinese..
21845 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21846 var cc = b.charCodeAt();
21848 (cc >= 0x4E00 && cc < 0xA000 ) ||
21849 (cc >= 0x3400 && cc < 0x4E00 ) ||
21850 (cc >= 0xf900 && cc < 0xfb00 )
21856 if(this.owner.fireEvent('beforesync', this, html) !== false){
21857 this.el.dom.value = html;
21858 this.owner.fireEvent('sync', this, html);
21864 * Protected method that will not generally be called directly. Pushes the value of the textarea
21865 * into the iframe editor.
21867 pushValue : function(){
21868 if(this.initialized){
21869 var v = this.el.dom.value.trim();
21871 // if(v.length < 1){
21875 if(this.owner.fireEvent('beforepush', this, v) !== false){
21876 var d = (this.doc.body || this.doc.documentElement);
21878 this.cleanUpPaste();
21879 this.el.dom.value = d.innerHTML;
21880 this.owner.fireEvent('push', this, v);
21886 deferFocus : function(){
21887 this.focus.defer(10, this);
21891 focus : function(){
21892 if(this.win && !this.sourceEditMode){
21899 assignDocWin: function()
21901 var iframe = this.iframe;
21904 this.doc = iframe.contentWindow.document;
21905 this.win = iframe.contentWindow;
21907 // if (!Roo.get(this.frameId)) {
21910 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21911 // this.win = Roo.get(this.frameId).dom.contentWindow;
21913 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21917 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21918 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21923 initEditor : function(){
21924 //console.log("INIT EDITOR");
21925 this.assignDocWin();
21929 this.doc.designMode="on";
21931 this.doc.write(this.getDocMarkup());
21934 var dbody = (this.doc.body || this.doc.documentElement);
21935 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21936 // this copies styles from the containing element into thsi one..
21937 // not sure why we need all of this..
21938 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21940 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21941 //ss['background-attachment'] = 'fixed'; // w3c
21942 dbody.bgProperties = 'fixed'; // ie
21943 //Roo.DomHelper.applyStyles(dbody, ss);
21944 Roo.EventManager.on(this.doc, {
21945 //'mousedown': this.onEditorEvent,
21946 'mouseup': this.onEditorEvent,
21947 'dblclick': this.onEditorEvent,
21948 'click': this.onEditorEvent,
21949 'keyup': this.onEditorEvent,
21954 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21956 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21957 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21959 this.initialized = true;
21961 this.owner.fireEvent('initialize', this);
21966 onDestroy : function(){
21972 //for (var i =0; i < this.toolbars.length;i++) {
21973 // // fixme - ask toolbars for heights?
21974 // this.toolbars[i].onDestroy();
21977 //this.wrap.dom.innerHTML = '';
21978 //this.wrap.remove();
21983 onFirstFocus : function(){
21985 this.assignDocWin();
21988 this.activated = true;
21991 if(Roo.isGecko){ // prevent silly gecko errors
21993 var s = this.win.getSelection();
21994 if(!s.focusNode || s.focusNode.nodeType != 3){
21995 var r = s.getRangeAt(0);
21996 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22001 this.execCmd('useCSS', true);
22002 this.execCmd('styleWithCSS', false);
22005 this.owner.fireEvent('activate', this);
22009 adjustFont: function(btn){
22010 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22011 //if(Roo.isSafari){ // safari
22014 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22015 if(Roo.isSafari){ // safari
22016 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22017 v = (v < 10) ? 10 : v;
22018 v = (v > 48) ? 48 : v;
22019 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22024 v = Math.max(1, v+adjust);
22026 this.execCmd('FontSize', v );
22029 onEditorEvent : function(e)
22031 this.owner.fireEvent('editorevent', this, e);
22032 // this.updateToolbar();
22033 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22036 insertTag : function(tg)
22038 // could be a bit smarter... -> wrap the current selected tRoo..
22039 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22041 range = this.createRange(this.getSelection());
22042 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22043 wrappingNode.appendChild(range.extractContents());
22044 range.insertNode(wrappingNode);
22051 this.execCmd("formatblock", tg);
22055 insertText : function(txt)
22059 var range = this.createRange();
22060 range.deleteContents();
22061 //alert(Sender.getAttribute('label'));
22063 range.insertNode(this.doc.createTextNode(txt));
22069 * Executes a Midas editor command on the editor document and performs necessary focus and
22070 * toolbar updates. <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 relayCmd : function(cmd, value){
22076 this.execCmd(cmd, value);
22077 this.owner.fireEvent('editorevent', this);
22078 //this.updateToolbar();
22079 this.owner.deferFocus();
22083 * Executes a Midas editor command directly on the editor document.
22084 * For visual commands, you should use {@link #relayCmd} instead.
22085 * <b>This should only be called after the editor is initialized.</b>
22086 * @param {String} cmd The Midas command
22087 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22089 execCmd : function(cmd, value){
22090 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22097 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22099 * @param {String} text | dom node..
22101 insertAtCursor : function(text)
22104 if(!this.activated){
22110 var r = this.doc.selection.createRange();
22121 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22125 // from jquery ui (MIT licenced)
22127 var win = this.win;
22129 if (win.getSelection && win.getSelection().getRangeAt) {
22130 range = win.getSelection().getRangeAt(0);
22131 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22132 range.insertNode(node);
22133 } else if (win.document.selection && win.document.selection.createRange) {
22134 // no firefox support
22135 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22136 win.document.selection.createRange().pasteHTML(txt);
22138 // no firefox support
22139 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22140 this.execCmd('InsertHTML', txt);
22149 mozKeyPress : function(e){
22151 var c = e.getCharCode(), cmd;
22154 c = String.fromCharCode(c).toLowerCase();
22168 this.cleanUpPaste.defer(100, this);
22176 e.preventDefault();
22184 fixKeys : function(){ // load time branching for fastest keydown performance
22186 return function(e){
22187 var k = e.getKey(), r;
22190 r = this.doc.selection.createRange();
22193 r.pasteHTML('    ');
22200 r = this.doc.selection.createRange();
22202 var target = r.parentElement();
22203 if(!target || target.tagName.toLowerCase() != 'li'){
22205 r.pasteHTML('<br />');
22211 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22212 this.cleanUpPaste.defer(100, this);
22218 }else if(Roo.isOpera){
22219 return function(e){
22220 var k = e.getKey();
22224 this.execCmd('InsertHTML','    ');
22227 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22228 this.cleanUpPaste.defer(100, this);
22233 }else if(Roo.isSafari){
22234 return function(e){
22235 var k = e.getKey();
22239 this.execCmd('InsertText','\t');
22243 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22244 this.cleanUpPaste.defer(100, this);
22252 getAllAncestors: function()
22254 var p = this.getSelectedNode();
22257 a.push(p); // push blank onto stack..
22258 p = this.getParentElement();
22262 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22266 a.push(this.doc.body);
22270 lastSelNode : false,
22273 getSelection : function()
22275 this.assignDocWin();
22276 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22279 getSelectedNode: function()
22281 // this may only work on Gecko!!!
22283 // should we cache this!!!!
22288 var range = this.createRange(this.getSelection()).cloneRange();
22291 var parent = range.parentElement();
22293 var testRange = range.duplicate();
22294 testRange.moveToElementText(parent);
22295 if (testRange.inRange(range)) {
22298 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22301 parent = parent.parentElement;
22306 // is ancestor a text element.
22307 var ac = range.commonAncestorContainer;
22308 if (ac.nodeType == 3) {
22309 ac = ac.parentNode;
22312 var ar = ac.childNodes;
22315 var other_nodes = [];
22316 var has_other_nodes = false;
22317 for (var i=0;i<ar.length;i++) {
22318 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22321 // fullly contained node.
22323 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22328 // probably selected..
22329 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22330 other_nodes.push(ar[i]);
22334 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22339 has_other_nodes = true;
22341 if (!nodes.length && other_nodes.length) {
22342 nodes= other_nodes;
22344 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22350 createRange: function(sel)
22352 // this has strange effects when using with
22353 // top toolbar - not sure if it's a great idea.
22354 //this.editor.contentWindow.focus();
22355 if (typeof sel != "undefined") {
22357 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22359 return this.doc.createRange();
22362 return this.doc.createRange();
22365 getParentElement: function()
22368 this.assignDocWin();
22369 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22371 var range = this.createRange(sel);
22374 var p = range.commonAncestorContainer;
22375 while (p.nodeType == 3) { // text node
22386 * Range intersection.. the hard stuff...
22390 * [ -- selected range --- ]
22394 * if end is before start or hits it. fail.
22395 * if start is after end or hits it fail.
22397 * if either hits (but other is outside. - then it's not
22403 // @see http://www.thismuchiknow.co.uk/?p=64.
22404 rangeIntersectsNode : function(range, node)
22406 var nodeRange = node.ownerDocument.createRange();
22408 nodeRange.selectNode(node);
22410 nodeRange.selectNodeContents(node);
22413 var rangeStartRange = range.cloneRange();
22414 rangeStartRange.collapse(true);
22416 var rangeEndRange = range.cloneRange();
22417 rangeEndRange.collapse(false);
22419 var nodeStartRange = nodeRange.cloneRange();
22420 nodeStartRange.collapse(true);
22422 var nodeEndRange = nodeRange.cloneRange();
22423 nodeEndRange.collapse(false);
22425 return rangeStartRange.compareBoundaryPoints(
22426 Range.START_TO_START, nodeEndRange) == -1 &&
22427 rangeEndRange.compareBoundaryPoints(
22428 Range.START_TO_START, nodeStartRange) == 1;
22432 rangeCompareNode : function(range, node)
22434 var nodeRange = node.ownerDocument.createRange();
22436 nodeRange.selectNode(node);
22438 nodeRange.selectNodeContents(node);
22442 range.collapse(true);
22444 nodeRange.collapse(true);
22446 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22447 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22449 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22451 var nodeIsBefore = ss == 1;
22452 var nodeIsAfter = ee == -1;
22454 if (nodeIsBefore && nodeIsAfter) {
22457 if (!nodeIsBefore && nodeIsAfter) {
22458 return 1; //right trailed.
22461 if (nodeIsBefore && !nodeIsAfter) {
22462 return 2; // left trailed.
22468 // private? - in a new class?
22469 cleanUpPaste : function()
22471 // cleans up the whole document..
22472 Roo.log('cleanuppaste');
22474 this.cleanUpChildren(this.doc.body);
22475 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22476 if (clean != this.doc.body.innerHTML) {
22477 this.doc.body.innerHTML = clean;
22482 cleanWordChars : function(input) {// change the chars to hex code
22483 var he = Roo.HtmlEditorCore;
22485 var output = input;
22486 Roo.each(he.swapCodes, function(sw) {
22487 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22489 output = output.replace(swapper, sw[1]);
22496 cleanUpChildren : function (n)
22498 if (!n.childNodes.length) {
22501 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22502 this.cleanUpChild(n.childNodes[i]);
22509 cleanUpChild : function (node)
22512 //console.log(node);
22513 if (node.nodeName == "#text") {
22514 // clean up silly Windows -- stuff?
22517 if (node.nodeName == "#comment") {
22518 node.parentNode.removeChild(node);
22519 // clean up silly Windows -- stuff?
22522 var lcname = node.tagName.toLowerCase();
22523 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22524 // whitelist of tags..
22526 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22528 node.parentNode.removeChild(node);
22533 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22535 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22536 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22538 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22539 // remove_keep_children = true;
22542 if (remove_keep_children) {
22543 this.cleanUpChildren(node);
22544 // inserts everything just before this node...
22545 while (node.childNodes.length) {
22546 var cn = node.childNodes[0];
22547 node.removeChild(cn);
22548 node.parentNode.insertBefore(cn, node);
22550 node.parentNode.removeChild(node);
22554 if (!node.attributes || !node.attributes.length) {
22555 this.cleanUpChildren(node);
22559 function cleanAttr(n,v)
22562 if (v.match(/^\./) || v.match(/^\//)) {
22565 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22568 if (v.match(/^#/)) {
22571 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22572 node.removeAttribute(n);
22576 var cwhite = this.cwhite;
22577 var cblack = this.cblack;
22579 function cleanStyle(n,v)
22581 if (v.match(/expression/)) { //XSS?? should we even bother..
22582 node.removeAttribute(n);
22586 var parts = v.split(/;/);
22589 Roo.each(parts, function(p) {
22590 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22594 var l = p.split(':').shift().replace(/\s+/g,'');
22595 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22597 if ( cwhite.length && cblack.indexOf(l) > -1) {
22598 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22599 //node.removeAttribute(n);
22603 // only allow 'c whitelisted system attributes'
22604 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22605 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22606 //node.removeAttribute(n);
22616 if (clean.length) {
22617 node.setAttribute(n, clean.join(';'));
22619 node.removeAttribute(n);
22625 for (var i = node.attributes.length-1; i > -1 ; i--) {
22626 var a = node.attributes[i];
22629 if (a.name.toLowerCase().substr(0,2)=='on') {
22630 node.removeAttribute(a.name);
22633 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22634 node.removeAttribute(a.name);
22637 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22638 cleanAttr(a.name,a.value); // fixme..
22641 if (a.name == 'style') {
22642 cleanStyle(a.name,a.value);
22645 /// clean up MS crap..
22646 // tecnically this should be a list of valid class'es..
22649 if (a.name == 'class') {
22650 if (a.value.match(/^Mso/)) {
22651 node.className = '';
22654 if (a.value.match(/^body$/)) {
22655 node.className = '';
22666 this.cleanUpChildren(node);
22672 * Clean up MS wordisms...
22674 cleanWord : function(node)
22679 this.cleanWord(this.doc.body);
22682 if (node.nodeName == "#text") {
22683 // clean up silly Windows -- stuff?
22686 if (node.nodeName == "#comment") {
22687 node.parentNode.removeChild(node);
22688 // clean up silly Windows -- stuff?
22692 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22693 node.parentNode.removeChild(node);
22697 // remove - but keep children..
22698 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22699 while (node.childNodes.length) {
22700 var cn = node.childNodes[0];
22701 node.removeChild(cn);
22702 node.parentNode.insertBefore(cn, node);
22704 node.parentNode.removeChild(node);
22705 this.iterateChildren(node, this.cleanWord);
22709 if (node.className.length) {
22711 var cn = node.className.split(/\W+/);
22713 Roo.each(cn, function(cls) {
22714 if (cls.match(/Mso[a-zA-Z]+/)) {
22719 node.className = cna.length ? cna.join(' ') : '';
22721 node.removeAttribute("class");
22725 if (node.hasAttribute("lang")) {
22726 node.removeAttribute("lang");
22729 if (node.hasAttribute("style")) {
22731 var styles = node.getAttribute("style").split(";");
22733 Roo.each(styles, function(s) {
22734 if (!s.match(/:/)) {
22737 var kv = s.split(":");
22738 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22741 // what ever is left... we allow.
22744 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22745 if (!nstyle.length) {
22746 node.removeAttribute('style');
22749 this.iterateChildren(node, this.cleanWord);
22755 * iterateChildren of a Node, calling fn each time, using this as the scole..
22756 * @param {DomNode} node node to iterate children of.
22757 * @param {Function} fn method of this class to call on each item.
22759 iterateChildren : function(node, fn)
22761 if (!node.childNodes.length) {
22764 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22765 fn.call(this, node.childNodes[i])
22771 * cleanTableWidths.
22773 * Quite often pasting from word etc.. results in tables with column and widths.
22774 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22777 cleanTableWidths : function(node)
22782 this.cleanTableWidths(this.doc.body);
22787 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22790 Roo.log(node.tagName);
22791 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22792 this.iterateChildren(node, this.cleanTableWidths);
22795 if (node.hasAttribute('width')) {
22796 node.removeAttribute('width');
22800 if (node.hasAttribute("style")) {
22803 var styles = node.getAttribute("style").split(";");
22805 Roo.each(styles, function(s) {
22806 if (!s.match(/:/)) {
22809 var kv = s.split(":");
22810 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22813 // what ever is left... we allow.
22816 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22817 if (!nstyle.length) {
22818 node.removeAttribute('style');
22822 this.iterateChildren(node, this.cleanTableWidths);
22830 domToHTML : function(currentElement, depth, nopadtext) {
22832 depth = depth || 0;
22833 nopadtext = nopadtext || false;
22835 if (!currentElement) {
22836 return this.domToHTML(this.doc.body);
22839 //Roo.log(currentElement);
22841 var allText = false;
22842 var nodeName = currentElement.nodeName;
22843 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22845 if (nodeName == '#text') {
22847 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22852 if (nodeName != 'BODY') {
22855 // Prints the node tagName, such as <A>, <IMG>, etc
22858 for(i = 0; i < currentElement.attributes.length;i++) {
22860 var aname = currentElement.attributes.item(i).name;
22861 if (!currentElement.attributes.item(i).value.length) {
22864 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22867 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22876 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22879 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22884 // Traverse the tree
22886 var currentElementChild = currentElement.childNodes.item(i);
22887 var allText = true;
22888 var innerHTML = '';
22890 while (currentElementChild) {
22891 // Formatting code (indent the tree so it looks nice on the screen)
22892 var nopad = nopadtext;
22893 if (lastnode == 'SPAN') {
22897 if (currentElementChild.nodeName == '#text') {
22898 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22899 toadd = nopadtext ? toadd : toadd.trim();
22900 if (!nopad && toadd.length > 80) {
22901 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22903 innerHTML += toadd;
22906 currentElementChild = currentElement.childNodes.item(i);
22912 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22914 // Recursively traverse the tree structure of the child node
22915 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22916 lastnode = currentElementChild.nodeName;
22918 currentElementChild=currentElement.childNodes.item(i);
22924 // The remaining code is mostly for formatting the tree
22925 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22930 ret+= "</"+tagName+">";
22936 applyBlacklists : function()
22938 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22939 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22943 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22944 if (b.indexOf(tag) > -1) {
22947 this.white.push(tag);
22951 Roo.each(w, function(tag) {
22952 if (b.indexOf(tag) > -1) {
22955 if (this.white.indexOf(tag) > -1) {
22958 this.white.push(tag);
22963 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22964 if (w.indexOf(tag) > -1) {
22967 this.black.push(tag);
22971 Roo.each(b, function(tag) {
22972 if (w.indexOf(tag) > -1) {
22975 if (this.black.indexOf(tag) > -1) {
22978 this.black.push(tag);
22983 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22984 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22988 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22989 if (b.indexOf(tag) > -1) {
22992 this.cwhite.push(tag);
22996 Roo.each(w, function(tag) {
22997 if (b.indexOf(tag) > -1) {
23000 if (this.cwhite.indexOf(tag) > -1) {
23003 this.cwhite.push(tag);
23008 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23009 if (w.indexOf(tag) > -1) {
23012 this.cblack.push(tag);
23016 Roo.each(b, function(tag) {
23017 if (w.indexOf(tag) > -1) {
23020 if (this.cblack.indexOf(tag) > -1) {
23023 this.cblack.push(tag);
23028 setStylesheets : function(stylesheets)
23030 if(typeof(stylesheets) == 'string'){
23031 Roo.get(this.iframe.contentDocument.head).createChild({
23033 rel : 'stylesheet',
23042 Roo.each(stylesheets, function(s) {
23047 Roo.get(_this.iframe.contentDocument.head).createChild({
23049 rel : 'stylesheet',
23058 removeStylesheets : function()
23062 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23067 setStyle : function(style)
23069 Roo.get(this.iframe.contentDocument.head).createChild({
23078 // hide stuff that is not compatible
23092 * @event specialkey
23096 * @cfg {String} fieldClass @hide
23099 * @cfg {String} focusClass @hide
23102 * @cfg {String} autoCreate @hide
23105 * @cfg {String} inputType @hide
23108 * @cfg {String} invalidClass @hide
23111 * @cfg {String} invalidText @hide
23114 * @cfg {String} msgFx @hide
23117 * @cfg {String} validateOnBlur @hide
23121 Roo.HtmlEditorCore.white = [
23122 'area', 'br', 'img', 'input', 'hr', 'wbr',
23124 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23125 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23126 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23127 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23128 'table', 'ul', 'xmp',
23130 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23133 'dir', 'menu', 'ol', 'ul', 'dl',
23139 Roo.HtmlEditorCore.black = [
23140 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23142 'base', 'basefont', 'bgsound', 'blink', 'body',
23143 'frame', 'frameset', 'head', 'html', 'ilayer',
23144 'iframe', 'layer', 'link', 'meta', 'object',
23145 'script', 'style' ,'title', 'xml' // clean later..
23147 Roo.HtmlEditorCore.clean = [
23148 'script', 'style', 'title', 'xml'
23150 Roo.HtmlEditorCore.remove = [
23155 Roo.HtmlEditorCore.ablack = [
23159 Roo.HtmlEditorCore.aclean = [
23160 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23164 Roo.HtmlEditorCore.pwhite= [
23165 'http', 'https', 'mailto'
23168 // white listed style attributes.
23169 Roo.HtmlEditorCore.cwhite= [
23170 // 'text-align', /// default is to allow most things..
23176 // black listed style attributes.
23177 Roo.HtmlEditorCore.cblack= [
23178 // 'font-size' -- this can be set by the project
23182 Roo.HtmlEditorCore.swapCodes =[
23201 * @class Roo.bootstrap.HtmlEditor
23202 * @extends Roo.bootstrap.TextArea
23203 * Bootstrap HtmlEditor class
23206 * Create a new HtmlEditor
23207 * @param {Object} config The config object
23210 Roo.bootstrap.HtmlEditor = function(config){
23211 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23212 if (!this.toolbars) {
23213 this.toolbars = [];
23216 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23219 * @event initialize
23220 * Fires when the editor is fully initialized (including the iframe)
23221 * @param {HtmlEditor} this
23226 * Fires when the editor is first receives the focus. Any insertion must wait
23227 * until after this event.
23228 * @param {HtmlEditor} this
23232 * @event beforesync
23233 * Fires before the textarea is updated with content from the editor iframe. Return false
23234 * to cancel the sync.
23235 * @param {HtmlEditor} this
23236 * @param {String} html
23240 * @event beforepush
23241 * Fires before the iframe editor is updated with content from the textarea. Return false
23242 * to cancel the push.
23243 * @param {HtmlEditor} this
23244 * @param {String} html
23249 * Fires when the textarea is updated with content from the editor iframe.
23250 * @param {HtmlEditor} this
23251 * @param {String} html
23256 * Fires when the iframe editor is updated with content from the textarea.
23257 * @param {HtmlEditor} this
23258 * @param {String} html
23262 * @event editmodechange
23263 * Fires when the editor switches edit modes
23264 * @param {HtmlEditor} this
23265 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23267 editmodechange: true,
23269 * @event editorevent
23270 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23271 * @param {HtmlEditor} this
23275 * @event firstfocus
23276 * Fires when on first focus - needed by toolbars..
23277 * @param {HtmlEditor} this
23282 * Auto save the htmlEditor value as a file into Events
23283 * @param {HtmlEditor} this
23287 * @event savedpreview
23288 * preview the saved version of htmlEditor
23289 * @param {HtmlEditor} this
23296 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23300 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23305 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23310 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23315 * @cfg {Number} height (in pixels)
23319 * @cfg {Number} width (in pixels)
23324 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23327 stylesheets: false,
23332 // private properties
23333 validationEvent : false,
23335 initialized : false,
23338 onFocus : Roo.emptyFn,
23340 hideMode:'offsets',
23342 tbContainer : false,
23346 toolbarContainer :function() {
23347 return this.wrap.select('.x-html-editor-tb',true).first();
23351 * Protected method that will not generally be called directly. It
23352 * is called when the editor creates its toolbar. Override this method if you need to
23353 * add custom toolbar buttons.
23354 * @param {HtmlEditor} editor
23356 createToolbar : function(){
23357 Roo.log('renewing');
23358 Roo.log("create toolbars");
23360 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23361 this.toolbars[0].render(this.toolbarContainer());
23365 // if (!editor.toolbars || !editor.toolbars.length) {
23366 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23369 // for (var i =0 ; i < editor.toolbars.length;i++) {
23370 // editor.toolbars[i] = Roo.factory(
23371 // typeof(editor.toolbars[i]) == 'string' ?
23372 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23373 // Roo.bootstrap.HtmlEditor);
23374 // editor.toolbars[i].init(editor);
23380 onRender : function(ct, position)
23382 // Roo.log("Call onRender: " + this.xtype);
23384 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23386 this.wrap = this.inputEl().wrap({
23387 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23390 this.editorcore.onRender(ct, position);
23392 if (this.resizable) {
23393 this.resizeEl = new Roo.Resizable(this.wrap, {
23397 minHeight : this.height,
23398 height: this.height,
23399 handles : this.resizable,
23402 resize : function(r, w, h) {
23403 _t.onResize(w,h); // -something
23409 this.createToolbar(this);
23412 if(!this.width && this.resizable){
23413 this.setSize(this.wrap.getSize());
23415 if (this.resizeEl) {
23416 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23417 // should trigger onReize..
23423 onResize : function(w, h)
23425 Roo.log('resize: ' +w + ',' + h );
23426 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23430 if(this.inputEl() ){
23431 if(typeof w == 'number'){
23432 var aw = w - this.wrap.getFrameWidth('lr');
23433 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23436 if(typeof h == 'number'){
23437 var tbh = -11; // fixme it needs to tool bar size!
23438 for (var i =0; i < this.toolbars.length;i++) {
23439 // fixme - ask toolbars for heights?
23440 tbh += this.toolbars[i].el.getHeight();
23441 //if (this.toolbars[i].footer) {
23442 // tbh += this.toolbars[i].footer.el.getHeight();
23450 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23451 ah -= 5; // knock a few pixes off for look..
23452 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23456 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23457 this.editorcore.onResize(ew,eh);
23462 * Toggles the editor between standard and source edit mode.
23463 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23465 toggleSourceEdit : function(sourceEditMode)
23467 this.editorcore.toggleSourceEdit(sourceEditMode);
23469 if(this.editorcore.sourceEditMode){
23470 Roo.log('editor - showing textarea');
23473 // Roo.log(this.syncValue());
23475 this.inputEl().removeClass(['hide', 'x-hidden']);
23476 this.inputEl().dom.removeAttribute('tabIndex');
23477 this.inputEl().focus();
23479 Roo.log('editor - hiding textarea');
23481 // Roo.log(this.pushValue());
23484 this.inputEl().addClass(['hide', 'x-hidden']);
23485 this.inputEl().dom.setAttribute('tabIndex', -1);
23486 //this.deferFocus();
23489 if(this.resizable){
23490 this.setSize(this.wrap.getSize());
23493 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23496 // private (for BoxComponent)
23497 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23499 // private (for BoxComponent)
23500 getResizeEl : function(){
23504 // private (for BoxComponent)
23505 getPositionEl : function(){
23510 initEvents : function(){
23511 this.originalValue = this.getValue();
23515 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23518 // markInvalid : Roo.emptyFn,
23520 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23523 // clearInvalid : Roo.emptyFn,
23525 setValue : function(v){
23526 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23527 this.editorcore.pushValue();
23532 deferFocus : function(){
23533 this.focus.defer(10, this);
23537 focus : function(){
23538 this.editorcore.focus();
23544 onDestroy : function(){
23550 for (var i =0; i < this.toolbars.length;i++) {
23551 // fixme - ask toolbars for heights?
23552 this.toolbars[i].onDestroy();
23555 this.wrap.dom.innerHTML = '';
23556 this.wrap.remove();
23561 onFirstFocus : function(){
23562 //Roo.log("onFirstFocus");
23563 this.editorcore.onFirstFocus();
23564 for (var i =0; i < this.toolbars.length;i++) {
23565 this.toolbars[i].onFirstFocus();
23571 syncValue : function()
23573 this.editorcore.syncValue();
23576 pushValue : function()
23578 this.editorcore.pushValue();
23582 // hide stuff that is not compatible
23596 * @event specialkey
23600 * @cfg {String} fieldClass @hide
23603 * @cfg {String} focusClass @hide
23606 * @cfg {String} autoCreate @hide
23609 * @cfg {String} inputType @hide
23612 * @cfg {String} invalidClass @hide
23615 * @cfg {String} invalidText @hide
23618 * @cfg {String} msgFx @hide
23621 * @cfg {String} validateOnBlur @hide
23630 Roo.namespace('Roo.bootstrap.htmleditor');
23632 * @class Roo.bootstrap.HtmlEditorToolbar1
23637 new Roo.bootstrap.HtmlEditor({
23640 new Roo.bootstrap.HtmlEditorToolbar1({
23641 disable : { fonts: 1 , format: 1, ..., ... , ...],
23647 * @cfg {Object} disable List of elements to disable..
23648 * @cfg {Array} btns List of additional buttons.
23652 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23655 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23658 Roo.apply(this, config);
23660 // default disabled, based on 'good practice'..
23661 this.disable = this.disable || {};
23662 Roo.applyIf(this.disable, {
23665 specialElements : true
23667 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23669 this.editor = config.editor;
23670 this.editorcore = config.editor.editorcore;
23672 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23674 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23675 // dont call parent... till later.
23677 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23682 editorcore : false,
23687 "h1","h2","h3","h4","h5","h6",
23689 "abbr", "acronym", "address", "cite", "samp", "var",
23693 onRender : function(ct, position)
23695 // Roo.log("Call onRender: " + this.xtype);
23697 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23699 this.el.dom.style.marginBottom = '0';
23701 var editorcore = this.editorcore;
23702 var editor= this.editor;
23705 var btn = function(id,cmd , toggle, handler, html){
23707 var event = toggle ? 'toggle' : 'click';
23712 xns: Roo.bootstrap,
23715 enableToggle:toggle !== false,
23717 pressed : toggle ? false : null,
23720 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23721 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23727 // var cb_box = function...
23732 xns: Roo.bootstrap,
23733 glyphicon : 'font',
23737 xns: Roo.bootstrap,
23741 Roo.each(this.formats, function(f) {
23742 style.menu.items.push({
23744 xns: Roo.bootstrap,
23745 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23750 editorcore.insertTag(this.tagname);
23757 children.push(style);
23759 btn('bold',false,true);
23760 btn('italic',false,true);
23761 btn('align-left', 'justifyleft',true);
23762 btn('align-center', 'justifycenter',true);
23763 btn('align-right' , 'justifyright',true);
23764 btn('link', false, false, function(btn) {
23765 //Roo.log("create link?");
23766 var url = prompt(this.createLinkText, this.defaultLinkValue);
23767 if(url && url != 'http:/'+'/'){
23768 this.editorcore.relayCmd('createlink', url);
23771 btn('list','insertunorderedlist',true);
23772 btn('pencil', false,true, function(btn){
23774 this.toggleSourceEdit(btn.pressed);
23777 if (this.editor.btns.length > 0) {
23778 for (var i = 0; i<this.editor.btns.length; i++) {
23779 children.push(this.editor.btns[i]);
23787 xns: Roo.bootstrap,
23792 xns: Roo.bootstrap,
23797 cog.menu.items.push({
23799 xns: Roo.bootstrap,
23800 html : Clean styles,
23805 editorcore.insertTag(this.tagname);
23814 this.xtype = 'NavSimplebar';
23816 for(var i=0;i< children.length;i++) {
23818 this.buttons.add(this.addxtypeChild(children[i]));
23822 editor.on('editorevent', this.updateToolbar, this);
23824 onBtnClick : function(id)
23826 this.editorcore.relayCmd(id);
23827 this.editorcore.focus();
23831 * Protected method that will not generally be called directly. It triggers
23832 * a toolbar update by reading the markup state of the current selection in the editor.
23834 updateToolbar: function(){
23836 if(!this.editorcore.activated){
23837 this.editor.onFirstFocus(); // is this neeed?
23841 var btns = this.buttons;
23842 var doc = this.editorcore.doc;
23843 btns.get('bold').setActive(doc.queryCommandState('bold'));
23844 btns.get('italic').setActive(doc.queryCommandState('italic'));
23845 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23847 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23848 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23849 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23851 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23852 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23855 var ans = this.editorcore.getAllAncestors();
23856 if (this.formatCombo) {
23859 var store = this.formatCombo.store;
23860 this.formatCombo.setValue("");
23861 for (var i =0; i < ans.length;i++) {
23862 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23864 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23872 // hides menus... - so this cant be on a menu...
23873 Roo.bootstrap.MenuMgr.hideAll();
23875 Roo.bootstrap.MenuMgr.hideAll();
23876 //this.editorsyncValue();
23878 onFirstFocus: function() {
23879 this.buttons.each(function(item){
23883 toggleSourceEdit : function(sourceEditMode){
23886 if(sourceEditMode){
23887 Roo.log("disabling buttons");
23888 this.buttons.each( function(item){
23889 if(item.cmd != 'pencil'){
23895 Roo.log("enabling buttons");
23896 if(this.editorcore.initialized){
23897 this.buttons.each( function(item){
23903 Roo.log("calling toggole on editor");
23904 // tell the editor that it's been pressed..
23905 this.editor.toggleSourceEdit(sourceEditMode);
23915 * @class Roo.bootstrap.Table.AbstractSelectionModel
23916 * @extends Roo.util.Observable
23917 * Abstract base class for grid SelectionModels. It provides the interface that should be
23918 * implemented by descendant classes. This class should not be directly instantiated.
23921 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23922 this.locked = false;
23923 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23927 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23928 /** @ignore Called by the grid automatically. Do not call directly. */
23929 init : function(grid){
23935 * Locks the selections.
23938 this.locked = true;
23942 * Unlocks the selections.
23944 unlock : function(){
23945 this.locked = false;
23949 * Returns true if the selections are locked.
23950 * @return {Boolean}
23952 isLocked : function(){
23953 return this.locked;
23957 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23958 * @class Roo.bootstrap.Table.RowSelectionModel
23959 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23960 * It supports multiple selections and keyboard selection/navigation.
23962 * @param {Object} config
23965 Roo.bootstrap.Table.RowSelectionModel = function(config){
23966 Roo.apply(this, config);
23967 this.selections = new Roo.util.MixedCollection(false, function(o){
23972 this.lastActive = false;
23976 * @event selectionchange
23977 * Fires when the selection changes
23978 * @param {SelectionModel} this
23980 "selectionchange" : true,
23982 * @event afterselectionchange
23983 * Fires after the selection changes (eg. by key press or clicking)
23984 * @param {SelectionModel} this
23986 "afterselectionchange" : true,
23988 * @event beforerowselect
23989 * Fires when a row is selected being selected, return false to cancel.
23990 * @param {SelectionModel} this
23991 * @param {Number} rowIndex The selected index
23992 * @param {Boolean} keepExisting False if other selections will be cleared
23994 "beforerowselect" : true,
23997 * Fires when a row is selected.
23998 * @param {SelectionModel} this
23999 * @param {Number} rowIndex The selected index
24000 * @param {Roo.data.Record} r The record
24002 "rowselect" : true,
24004 * @event rowdeselect
24005 * Fires when a row is deselected.
24006 * @param {SelectionModel} this
24007 * @param {Number} rowIndex The selected index
24009 "rowdeselect" : true
24011 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24012 this.locked = false;
24015 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24017 * @cfg {Boolean} singleSelect
24018 * True to allow selection of only one row at a time (defaults to false)
24020 singleSelect : false,
24023 initEvents : function()
24026 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24027 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24028 //}else{ // allow click to work like normal
24029 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24031 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24032 this.grid.on("rowclick", this.handleMouseDown, this);
24034 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24035 "up" : function(e){
24037 this.selectPrevious(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);
24050 "down" : function(e){
24052 this.selectNext(e.shiftKey);
24053 }else if(this.last !== false && this.lastActive !== false){
24054 var last = this.last;
24055 this.selectRange(this.last, this.lastActive+1);
24056 this.grid.getView().focusRow(this.lastActive);
24057 if(last !== false){
24061 this.selectFirstRow();
24063 this.fireEvent("afterselectionchange", this);
24067 this.grid.store.on('load', function(){
24068 this.selections.clear();
24071 var view = this.grid.view;
24072 view.on("refresh", this.onRefresh, this);
24073 view.on("rowupdated", this.onRowUpdated, this);
24074 view.on("rowremoved", this.onRemove, this);
24079 onRefresh : function()
24081 var ds = this.grid.store, i, v = this.grid.view;
24082 var s = this.selections;
24083 s.each(function(r){
24084 if((i = ds.indexOfId(r.id)) != -1){
24093 onRemove : function(v, index, r){
24094 this.selections.remove(r);
24098 onRowUpdated : function(v, index, r){
24099 if(this.isSelected(r)){
24100 v.onRowSelect(index);
24106 * @param {Array} records The records to select
24107 * @param {Boolean} keepExisting (optional) True to keep existing selections
24109 selectRecords : function(records, keepExisting)
24112 this.clearSelections();
24114 var ds = this.grid.store;
24115 for(var i = 0, len = records.length; i < len; i++){
24116 this.selectRow(ds.indexOf(records[i]), true);
24121 * Gets the number of selected rows.
24124 getCount : function(){
24125 return this.selections.length;
24129 * Selects the first row in the grid.
24131 selectFirstRow : function(){
24136 * Select the last row.
24137 * @param {Boolean} keepExisting (optional) True to keep existing selections
24139 selectLastRow : function(keepExisting){
24140 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24141 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24145 * Selects the row immediately following the last selected row.
24146 * @param {Boolean} keepExisting (optional) True to keep existing selections
24148 selectNext : function(keepExisting)
24150 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24151 this.selectRow(this.last+1, keepExisting);
24152 this.grid.getView().focusRow(this.last);
24157 * Selects the row that precedes the last selected row.
24158 * @param {Boolean} keepExisting (optional) True to keep existing selections
24160 selectPrevious : function(keepExisting){
24162 this.selectRow(this.last-1, keepExisting);
24163 this.grid.getView().focusRow(this.last);
24168 * Returns the selected records
24169 * @return {Array} Array of selected records
24171 getSelections : function(){
24172 return [].concat(this.selections.items);
24176 * Returns the first selected record.
24179 getSelected : function(){
24180 return this.selections.itemAt(0);
24185 * Clears all selections.
24187 clearSelections : function(fast)
24193 var ds = this.grid.store;
24194 var s = this.selections;
24195 s.each(function(r){
24196 this.deselectRow(ds.indexOfId(r.id));
24200 this.selections.clear();
24207 * Selects all rows.
24209 selectAll : function(){
24213 this.selections.clear();
24214 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24215 this.selectRow(i, true);
24220 * Returns True if there is a selection.
24221 * @return {Boolean}
24223 hasSelection : function(){
24224 return this.selections.length > 0;
24228 * Returns True if the specified row is selected.
24229 * @param {Number/Record} record The record or index of the record to check
24230 * @return {Boolean}
24232 isSelected : function(index){
24233 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24234 return (r && this.selections.key(r.id) ? true : false);
24238 * Returns True if the specified record id is selected.
24239 * @param {String} id The id of record to check
24240 * @return {Boolean}
24242 isIdSelected : function(id){
24243 return (this.selections.key(id) ? true : false);
24248 handleMouseDBClick : function(e, t){
24252 handleMouseDown : function(e, t)
24254 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24255 if(this.isLocked() || rowIndex < 0 ){
24258 if(e.shiftKey && this.last !== false){
24259 var last = this.last;
24260 this.selectRange(last, rowIndex, e.ctrlKey);
24261 this.last = last; // reset the last
24265 var isSelected = this.isSelected(rowIndex);
24266 //Roo.log("select row:" + rowIndex);
24268 this.deselectRow(rowIndex);
24270 this.selectRow(rowIndex, true);
24274 if(e.button !== 0 && isSelected){
24275 alert('rowIndex 2: ' + rowIndex);
24276 view.focusRow(rowIndex);
24277 }else if(e.ctrlKey && isSelected){
24278 this.deselectRow(rowIndex);
24279 }else if(!isSelected){
24280 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24281 view.focusRow(rowIndex);
24285 this.fireEvent("afterselectionchange", this);
24288 handleDragableRowClick : function(grid, rowIndex, e)
24290 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24291 this.selectRow(rowIndex, false);
24292 grid.view.focusRow(rowIndex);
24293 this.fireEvent("afterselectionchange", this);
24298 * Selects multiple rows.
24299 * @param {Array} rows Array of the indexes of the row to select
24300 * @param {Boolean} keepExisting (optional) True to keep existing selections
24302 selectRows : function(rows, keepExisting){
24304 this.clearSelections();
24306 for(var i = 0, len = rows.length; i < len; i++){
24307 this.selectRow(rows[i], true);
24312 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24313 * @param {Number} startRow The index of the first row in the range
24314 * @param {Number} endRow The index of the last row in the range
24315 * @param {Boolean} keepExisting (optional) True to retain existing selections
24317 selectRange : function(startRow, endRow, keepExisting){
24322 this.clearSelections();
24324 if(startRow <= endRow){
24325 for(var i = startRow; i <= endRow; i++){
24326 this.selectRow(i, true);
24329 for(var i = startRow; i >= endRow; i--){
24330 this.selectRow(i, true);
24336 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24337 * @param {Number} startRow The index of the first row in the range
24338 * @param {Number} endRow The index of the last row in the range
24340 deselectRange : function(startRow, endRow, preventViewNotify){
24344 for(var i = startRow; i <= endRow; i++){
24345 this.deselectRow(i, preventViewNotify);
24351 * @param {Number} row The index of the row to select
24352 * @param {Boolean} keepExisting (optional) True to keep existing selections
24354 selectRow : function(index, keepExisting, preventViewNotify)
24356 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24359 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24360 if(!keepExisting || this.singleSelect){
24361 this.clearSelections();
24364 var r = this.grid.store.getAt(index);
24365 //console.log('selectRow - record id :' + r.id);
24367 this.selections.add(r);
24368 this.last = this.lastActive = index;
24369 if(!preventViewNotify){
24370 var proxy = new Roo.Element(
24371 this.grid.getRowDom(index)
24373 proxy.addClass('bg-info info');
24375 this.fireEvent("rowselect", this, index, r);
24376 this.fireEvent("selectionchange", this);
24382 * @param {Number} row The index of the row to deselect
24384 deselectRow : function(index, preventViewNotify)
24389 if(this.last == index){
24392 if(this.lastActive == index){
24393 this.lastActive = false;
24396 var r = this.grid.store.getAt(index);
24401 this.selections.remove(r);
24402 //.console.log('deselectRow - record id :' + r.id);
24403 if(!preventViewNotify){
24405 var proxy = new Roo.Element(
24406 this.grid.getRowDom(index)
24408 proxy.removeClass('bg-info info');
24410 this.fireEvent("rowdeselect", this, index);
24411 this.fireEvent("selectionchange", this);
24415 restoreLast : function(){
24417 this.last = this._last;
24422 acceptsNav : function(row, col, cm){
24423 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24427 onEditorKey : function(field, e){
24428 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24433 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24435 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24437 }else if(k == e.ENTER && !e.ctrlKey){
24441 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24443 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24445 }else if(k == e.ESC){
24449 g.startEditing(newCell[0], newCell[1]);
24455 * Ext JS Library 1.1.1
24456 * Copyright(c) 2006-2007, Ext JS, LLC.
24458 * Originally Released Under LGPL - original licence link has changed is not relivant.
24461 * <script type="text/javascript">
24465 * @class Roo.bootstrap.PagingToolbar
24466 * @extends Roo.bootstrap.NavSimplebar
24467 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24469 * Create a new PagingToolbar
24470 * @param {Object} config The config object
24471 * @param {Roo.data.Store} store
24473 Roo.bootstrap.PagingToolbar = function(config)
24475 // old args format still supported... - xtype is prefered..
24476 // created from xtype...
24478 this.ds = config.dataSource;
24480 if (config.store && !this.ds) {
24481 this.store= Roo.factory(config.store, Roo.data);
24482 this.ds = this.store;
24483 this.ds.xmodule = this.xmodule || false;
24486 this.toolbarItems = [];
24487 if (config.items) {
24488 this.toolbarItems = config.items;
24491 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24496 this.bind(this.ds);
24499 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24503 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24505 * @cfg {Roo.data.Store} dataSource
24506 * The underlying data store providing the paged data
24509 * @cfg {String/HTMLElement/Element} container
24510 * container The id or element that will contain the toolbar
24513 * @cfg {Boolean} displayInfo
24514 * True to display the displayMsg (defaults to false)
24517 * @cfg {Number} pageSize
24518 * The number of records to display per page (defaults to 20)
24522 * @cfg {String} displayMsg
24523 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24525 displayMsg : 'Displaying {0} - {1} of {2}',
24527 * @cfg {String} emptyMsg
24528 * The message to display when no records are found (defaults to "No data to display")
24530 emptyMsg : 'No data to display',
24532 * Customizable piece of the default paging text (defaults to "Page")
24535 beforePageText : "Page",
24537 * Customizable piece of the default paging text (defaults to "of %0")
24540 afterPageText : "of {0}",
24542 * Customizable piece of the default paging text (defaults to "First Page")
24545 firstText : "First Page",
24547 * Customizable piece of the default paging text (defaults to "Previous Page")
24550 prevText : "Previous Page",
24552 * Customizable piece of the default paging text (defaults to "Next Page")
24555 nextText : "Next Page",
24557 * Customizable piece of the default paging text (defaults to "Last Page")
24560 lastText : "Last Page",
24562 * Customizable piece of the default paging text (defaults to "Refresh")
24565 refreshText : "Refresh",
24569 onRender : function(ct, position)
24571 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24572 this.navgroup.parentId = this.id;
24573 this.navgroup.onRender(this.el, null);
24574 // add the buttons to the navgroup
24576 if(this.displayInfo){
24577 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24578 this.displayEl = this.el.select('.x-paging-info', true).first();
24579 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24580 // this.displayEl = navel.el.select('span',true).first();
24586 Roo.each(_this.buttons, function(e){ // this might need to use render????
24587 Roo.factory(e).render(_this.el);
24591 Roo.each(_this.toolbarItems, function(e) {
24592 _this.navgroup.addItem(e);
24596 this.first = this.navgroup.addItem({
24597 tooltip: this.firstText,
24599 icon : 'fa fa-backward',
24601 preventDefault: true,
24602 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24605 this.prev = this.navgroup.addItem({
24606 tooltip: this.prevText,
24608 icon : 'fa fa-step-backward',
24610 preventDefault: true,
24611 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24613 //this.addSeparator();
24616 var field = this.navgroup.addItem( {
24618 cls : 'x-paging-position',
24620 html : this.beforePageText +
24621 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24622 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24625 this.field = field.el.select('input', true).first();
24626 this.field.on("keydown", this.onPagingKeydown, this);
24627 this.field.on("focus", function(){this.dom.select();});
24630 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24631 //this.field.setHeight(18);
24632 //this.addSeparator();
24633 this.next = this.navgroup.addItem({
24634 tooltip: this.nextText,
24636 html : ' <i class="fa fa-step-forward">',
24638 preventDefault: true,
24639 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24641 this.last = this.navgroup.addItem({
24642 tooltip: this.lastText,
24643 icon : 'fa fa-forward',
24646 preventDefault: true,
24647 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24649 //this.addSeparator();
24650 this.loading = this.navgroup.addItem({
24651 tooltip: this.refreshText,
24652 icon: 'fa fa-refresh',
24653 preventDefault: true,
24654 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24660 updateInfo : function(){
24661 if(this.displayEl){
24662 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24663 var msg = count == 0 ?
24667 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24669 this.displayEl.update(msg);
24674 onLoad : function(ds, r, o)
24676 this.cursor = o.params.start ? o.params.start : 0;
24678 var d = this.getPageData(),
24683 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24684 this.field.dom.value = ap;
24685 this.first.setDisabled(ap == 1);
24686 this.prev.setDisabled(ap == 1);
24687 this.next.setDisabled(ap == ps);
24688 this.last.setDisabled(ap == ps);
24689 this.loading.enable();
24694 getPageData : function(){
24695 var total = this.ds.getTotalCount();
24698 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24699 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24704 onLoadError : function(){
24705 this.loading.enable();
24709 onPagingKeydown : function(e){
24710 var k = e.getKey();
24711 var d = this.getPageData();
24713 var v = this.field.dom.value, pageNum;
24714 if(!v || isNaN(pageNum = parseInt(v, 10))){
24715 this.field.dom.value = d.activePage;
24718 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24719 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24722 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))
24724 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24725 this.field.dom.value = pageNum;
24726 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24729 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24731 var v = this.field.dom.value, pageNum;
24732 var increment = (e.shiftKey) ? 10 : 1;
24733 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24736 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24737 this.field.dom.value = d.activePage;
24740 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24742 this.field.dom.value = parseInt(v, 10) + increment;
24743 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24744 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24751 beforeLoad : function(){
24753 this.loading.disable();
24758 onClick : function(which){
24767 ds.load({params:{start: 0, limit: this.pageSize}});
24770 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24773 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24776 var total = ds.getTotalCount();
24777 var extra = total % this.pageSize;
24778 var lastStart = extra ? (total - extra) : total-this.pageSize;
24779 ds.load({params:{start: lastStart, limit: this.pageSize}});
24782 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24788 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24789 * @param {Roo.data.Store} store The data store to unbind
24791 unbind : function(ds){
24792 ds.un("beforeload", this.beforeLoad, this);
24793 ds.un("load", this.onLoad, this);
24794 ds.un("loadexception", this.onLoadError, this);
24795 ds.un("remove", this.updateInfo, this);
24796 ds.un("add", this.updateInfo, this);
24797 this.ds = undefined;
24801 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24802 * @param {Roo.data.Store} store The data store to bind
24804 bind : function(ds){
24805 ds.on("beforeload", this.beforeLoad, this);
24806 ds.on("load", this.onLoad, this);
24807 ds.on("loadexception", this.onLoadError, this);
24808 ds.on("remove", this.updateInfo, this);
24809 ds.on("add", this.updateInfo, this);
24820 * @class Roo.bootstrap.MessageBar
24821 * @extends Roo.bootstrap.Component
24822 * Bootstrap MessageBar class
24823 * @cfg {String} html contents of the MessageBar
24824 * @cfg {String} weight (info | success | warning | danger) default info
24825 * @cfg {String} beforeClass insert the bar before the given class
24826 * @cfg {Boolean} closable (true | false) default false
24827 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24830 * Create a new Element
24831 * @param {Object} config The config object
24834 Roo.bootstrap.MessageBar = function(config){
24835 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24838 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24844 beforeClass: 'bootstrap-sticky-wrap',
24846 getAutoCreate : function(){
24850 cls: 'alert alert-dismissable alert-' + this.weight,
24855 html: this.html || ''
24861 cfg.cls += ' alert-messages-fixed';
24875 onRender : function(ct, position)
24877 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24880 var cfg = Roo.apply({}, this.getAutoCreate());
24884 cfg.cls += ' ' + this.cls;
24887 cfg.style = this.style;
24889 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24891 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24894 this.el.select('>button.close').on('click', this.hide, this);
24900 if (!this.rendered) {
24906 this.fireEvent('show', this);
24912 if (!this.rendered) {
24918 this.fireEvent('hide', this);
24921 update : function()
24923 // var e = this.el.dom.firstChild;
24925 // if(this.closable){
24926 // e = e.nextSibling;
24929 // e.data = this.html || '';
24931 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24947 * @class Roo.bootstrap.Graph
24948 * @extends Roo.bootstrap.Component
24949 * Bootstrap Graph class
24953 @cfg {String} graphtype bar | vbar | pie
24954 @cfg {number} g_x coodinator | centre x (pie)
24955 @cfg {number} g_y coodinator | centre y (pie)
24956 @cfg {number} g_r radius (pie)
24957 @cfg {number} g_height height of the chart (respected by all elements in the set)
24958 @cfg {number} g_width width of the chart (respected by all elements in the set)
24959 @cfg {Object} title The title of the chart
24962 -opts (object) options for the chart
24964 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24965 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24967 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.
24968 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24970 o stretch (boolean)
24972 -opts (object) options for the pie
24975 o startAngle (number)
24976 o endAngle (number)
24980 * Create a new Input
24981 * @param {Object} config The config object
24984 Roo.bootstrap.Graph = function(config){
24985 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24991 * The img click event for the img.
24992 * @param {Roo.EventObject} e
24998 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25009 //g_colors: this.colors,
25016 getAutoCreate : function(){
25027 onRender : function(ct,position){
25030 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25032 if (typeof(Raphael) == 'undefined') {
25033 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25037 this.raphael = Raphael(this.el.dom);
25039 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25040 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25041 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25042 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25044 r.text(160, 10, "Single Series Chart").attr(txtattr);
25045 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25046 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25047 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25049 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25050 r.barchart(330, 10, 300, 220, data1);
25051 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25052 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25055 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25056 // r.barchart(30, 30, 560, 250, xdata, {
25057 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25058 // axis : "0 0 1 1",
25059 // axisxlabels : xdata
25060 // //yvalues : cols,
25063 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25065 // this.load(null,xdata,{
25066 // axis : "0 0 1 1",
25067 // axisxlabels : xdata
25072 load : function(graphtype,xdata,opts)
25074 this.raphael.clear();
25076 graphtype = this.graphtype;
25081 var r = this.raphael,
25082 fin = function () {
25083 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25085 fout = function () {
25086 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25088 pfin = function() {
25089 this.sector.stop();
25090 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25093 this.label[0].stop();
25094 this.label[0].attr({ r: 7.5 });
25095 this.label[1].attr({ "font-weight": 800 });
25098 pfout = function() {
25099 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25102 this.label[0].animate({ r: 5 }, 500, "bounce");
25103 this.label[1].attr({ "font-weight": 400 });
25109 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25112 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25115 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25116 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25118 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25125 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25130 setTitle: function(o)
25135 initEvents: function() {
25138 this.el.on('click', this.onClick, this);
25142 onClick : function(e)
25144 Roo.log('img onclick');
25145 this.fireEvent('click', this, e);
25157 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25160 * @class Roo.bootstrap.dash.NumberBox
25161 * @extends Roo.bootstrap.Component
25162 * Bootstrap NumberBox class
25163 * @cfg {String} headline Box headline
25164 * @cfg {String} content Box content
25165 * @cfg {String} icon Box icon
25166 * @cfg {String} footer Footer text
25167 * @cfg {String} fhref Footer href
25170 * Create a new NumberBox
25171 * @param {Object} config The config object
25175 Roo.bootstrap.dash.NumberBox = function(config){
25176 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25180 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25189 getAutoCreate : function(){
25193 cls : 'small-box ',
25201 cls : 'roo-headline',
25202 html : this.headline
25206 cls : 'roo-content',
25207 html : this.content
25221 cls : 'ion ' + this.icon
25230 cls : 'small-box-footer',
25231 href : this.fhref || '#',
25235 cfg.cn.push(footer);
25242 onRender : function(ct,position){
25243 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25250 setHeadline: function (value)
25252 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25255 setFooter: function (value, href)
25257 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25260 this.el.select('a.small-box-footer',true).first().attr('href', href);
25265 setContent: function (value)
25267 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25270 initEvents: function()
25284 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25287 * @class Roo.bootstrap.dash.TabBox
25288 * @extends Roo.bootstrap.Component
25289 * Bootstrap TabBox class
25290 * @cfg {String} title Title of the TabBox
25291 * @cfg {String} icon Icon of the TabBox
25292 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25293 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25296 * Create a new TabBox
25297 * @param {Object} config The config object
25301 Roo.bootstrap.dash.TabBox = function(config){
25302 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25307 * When a pane is added
25308 * @param {Roo.bootstrap.dash.TabPane} pane
25312 * @event activatepane
25313 * When a pane is activated
25314 * @param {Roo.bootstrap.dash.TabPane} pane
25316 "activatepane" : true
25324 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25329 tabScrollable : false,
25331 getChildContainer : function()
25333 return this.el.select('.tab-content', true).first();
25336 getAutoCreate : function(){
25340 cls: 'pull-left header',
25348 cls: 'fa ' + this.icon
25354 cls: 'nav nav-tabs pull-right',
25360 if(this.tabScrollable){
25367 cls: 'nav nav-tabs pull-right',
25378 cls: 'nav-tabs-custom',
25383 cls: 'tab-content no-padding',
25391 initEvents : function()
25393 //Roo.log('add add pane handler');
25394 this.on('addpane', this.onAddPane, this);
25397 * Updates the box title
25398 * @param {String} html to set the title to.
25400 setTitle : function(value)
25402 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25404 onAddPane : function(pane)
25406 this.panes.push(pane);
25407 //Roo.log('addpane');
25409 // tabs are rendere left to right..
25410 if(!this.showtabs){
25414 var ctr = this.el.select('.nav-tabs', true).first();
25417 var existing = ctr.select('.nav-tab',true);
25418 var qty = existing.getCount();;
25421 var tab = ctr.createChild({
25423 cls : 'nav-tab' + (qty ? '' : ' active'),
25431 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25434 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25436 pane.el.addClass('active');
25441 onTabClick : function(ev,un,ob,pane)
25443 //Roo.log('tab - prev default');
25444 ev.preventDefault();
25447 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25448 pane.tab.addClass('active');
25449 //Roo.log(pane.title);
25450 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25451 // technically we should have a deactivate event.. but maybe add later.
25452 // and it should not de-activate the selected tab...
25453 this.fireEvent('activatepane', pane);
25454 pane.el.addClass('active');
25455 pane.fireEvent('activate');
25460 getActivePane : function()
25463 Roo.each(this.panes, function(p) {
25464 if(p.el.hasClass('active')){
25485 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25487 * @class Roo.bootstrap.TabPane
25488 * @extends Roo.bootstrap.Component
25489 * Bootstrap TabPane class
25490 * @cfg {Boolean} active (false | true) Default false
25491 * @cfg {String} title title of panel
25495 * Create a new TabPane
25496 * @param {Object} config The config object
25499 Roo.bootstrap.dash.TabPane = function(config){
25500 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25506 * When a pane is activated
25507 * @param {Roo.bootstrap.dash.TabPane} pane
25514 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25519 // the tabBox that this is attached to.
25522 getAutoCreate : function()
25530 cfg.cls += ' active';
25535 initEvents : function()
25537 //Roo.log('trigger add pane handler');
25538 this.parent().fireEvent('addpane', this)
25542 * Updates the tab title
25543 * @param {String} html to set the title to.
25545 setTitle: function(str)
25551 this.tab.select('a', true).first().dom.innerHTML = str;
25568 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25571 * @class Roo.bootstrap.menu.Menu
25572 * @extends Roo.bootstrap.Component
25573 * Bootstrap Menu class - container for Menu
25574 * @cfg {String} html Text of the menu
25575 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25576 * @cfg {String} icon Font awesome icon
25577 * @cfg {String} pos Menu align to (top | bottom) default bottom
25581 * Create a new Menu
25582 * @param {Object} config The config object
25586 Roo.bootstrap.menu.Menu = function(config){
25587 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25591 * @event beforeshow
25592 * Fires before this menu is displayed
25593 * @param {Roo.bootstrap.menu.Menu} this
25597 * @event beforehide
25598 * Fires before this menu is hidden
25599 * @param {Roo.bootstrap.menu.Menu} this
25604 * Fires after this menu is displayed
25605 * @param {Roo.bootstrap.menu.Menu} this
25610 * Fires after this menu is hidden
25611 * @param {Roo.bootstrap.menu.Menu} this
25616 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25617 * @param {Roo.bootstrap.menu.Menu} this
25618 * @param {Roo.EventObject} e
25625 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25629 weight : 'default',
25634 getChildContainer : function() {
25635 if(this.isSubMenu){
25639 return this.el.select('ul.dropdown-menu', true).first();
25642 getAutoCreate : function()
25647 cls : 'roo-menu-text',
25655 cls : 'fa ' + this.icon
25666 cls : 'dropdown-button btn btn-' + this.weight,
25671 cls : 'dropdown-toggle btn btn-' + this.weight,
25681 cls : 'dropdown-menu'
25687 if(this.pos == 'top'){
25688 cfg.cls += ' dropup';
25691 if(this.isSubMenu){
25694 cls : 'dropdown-menu'
25701 onRender : function(ct, position)
25703 this.isSubMenu = ct.hasClass('dropdown-submenu');
25705 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25708 initEvents : function()
25710 if(this.isSubMenu){
25714 this.hidden = true;
25716 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25717 this.triggerEl.on('click', this.onTriggerPress, this);
25719 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25720 this.buttonEl.on('click', this.onClick, this);
25726 if(this.isSubMenu){
25730 return this.el.select('ul.dropdown-menu', true).first();
25733 onClick : function(e)
25735 this.fireEvent("click", this, e);
25738 onTriggerPress : function(e)
25740 if (this.isVisible()) {
25747 isVisible : function(){
25748 return !this.hidden;
25753 this.fireEvent("beforeshow", this);
25755 this.hidden = false;
25756 this.el.addClass('open');
25758 Roo.get(document).on("mouseup", this.onMouseUp, this);
25760 this.fireEvent("show", this);
25767 this.fireEvent("beforehide", this);
25769 this.hidden = true;
25770 this.el.removeClass('open');
25772 Roo.get(document).un("mouseup", this.onMouseUp);
25774 this.fireEvent("hide", this);
25777 onMouseUp : function()
25791 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25794 * @class Roo.bootstrap.menu.Item
25795 * @extends Roo.bootstrap.Component
25796 * Bootstrap MenuItem class
25797 * @cfg {Boolean} submenu (true | false) default false
25798 * @cfg {String} html text of the item
25799 * @cfg {String} href the link
25800 * @cfg {Boolean} disable (true | false) default false
25801 * @cfg {Boolean} preventDefault (true | false) default true
25802 * @cfg {String} icon Font awesome icon
25803 * @cfg {String} pos Submenu align to (left | right) default right
25807 * Create a new Item
25808 * @param {Object} config The config object
25812 Roo.bootstrap.menu.Item = function(config){
25813 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25817 * Fires when the mouse is hovering over this menu
25818 * @param {Roo.bootstrap.menu.Item} this
25819 * @param {Roo.EventObject} e
25824 * Fires when the mouse exits this menu
25825 * @param {Roo.bootstrap.menu.Item} this
25826 * @param {Roo.EventObject} e
25832 * The raw click event for the entire grid.
25833 * @param {Roo.EventObject} e
25839 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25844 preventDefault: true,
25849 getAutoCreate : function()
25854 cls : 'roo-menu-item-text',
25862 cls : 'fa ' + this.icon
25871 href : this.href || '#',
25878 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25882 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25884 if(this.pos == 'left'){
25885 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25892 initEvents : function()
25894 this.el.on('mouseover', this.onMouseOver, this);
25895 this.el.on('mouseout', this.onMouseOut, this);
25897 this.el.select('a', true).first().on('click', this.onClick, this);
25901 onClick : function(e)
25903 if(this.preventDefault){
25904 e.preventDefault();
25907 this.fireEvent("click", this, e);
25910 onMouseOver : function(e)
25912 if(this.submenu && this.pos == 'left'){
25913 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25916 this.fireEvent("mouseover", this, e);
25919 onMouseOut : function(e)
25921 this.fireEvent("mouseout", this, e);
25933 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25936 * @class Roo.bootstrap.menu.Separator
25937 * @extends Roo.bootstrap.Component
25938 * Bootstrap Separator class
25941 * Create a new Separator
25942 * @param {Object} config The config object
25946 Roo.bootstrap.menu.Separator = function(config){
25947 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25950 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25952 getAutoCreate : function(){
25973 * @class Roo.bootstrap.Tooltip
25974 * Bootstrap Tooltip class
25975 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25976 * to determine which dom element triggers the tooltip.
25978 * It needs to add support for additional attributes like tooltip-position
25981 * Create a new Toolti
25982 * @param {Object} config The config object
25985 Roo.bootstrap.Tooltip = function(config){
25986 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25988 this.alignment = Roo.bootstrap.Tooltip.alignment;
25990 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25991 this.alignment = config.alignment;
25996 Roo.apply(Roo.bootstrap.Tooltip, {
25998 * @function init initialize tooltip monitoring.
26002 currentTip : false,
26003 currentRegion : false,
26009 Roo.get(document).on('mouseover', this.enter ,this);
26010 Roo.get(document).on('mouseout', this.leave, this);
26013 this.currentTip = new Roo.bootstrap.Tooltip();
26016 enter : function(ev)
26018 var dom = ev.getTarget();
26020 //Roo.log(['enter',dom]);
26021 var el = Roo.fly(dom);
26022 if (this.currentEl) {
26024 //Roo.log(this.currentEl);
26025 //Roo.log(this.currentEl.contains(dom));
26026 if (this.currentEl == el) {
26029 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26035 if (this.currentTip.el) {
26036 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26040 if(!el || el.dom == document){
26046 // you can not look for children, as if el is the body.. then everythign is the child..
26047 if (!el.attr('tooltip')) { //
26048 if (!el.select("[tooltip]").elements.length) {
26051 // is the mouse over this child...?
26052 bindEl = el.select("[tooltip]").first();
26053 var xy = ev.getXY();
26054 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26055 //Roo.log("not in region.");
26058 //Roo.log("child element over..");
26061 this.currentEl = bindEl;
26062 this.currentTip.bind(bindEl);
26063 this.currentRegion = Roo.lib.Region.getRegion(dom);
26064 this.currentTip.enter();
26067 leave : function(ev)
26069 var dom = ev.getTarget();
26070 //Roo.log(['leave',dom]);
26071 if (!this.currentEl) {
26076 if (dom != this.currentEl.dom) {
26079 var xy = ev.getXY();
26080 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26083 // only activate leave if mouse cursor is outside... bounding box..
26088 if (this.currentTip) {
26089 this.currentTip.leave();
26091 //Roo.log('clear currentEl');
26092 this.currentEl = false;
26097 'left' : ['r-l', [-2,0], 'right'],
26098 'right' : ['l-r', [2,0], 'left'],
26099 'bottom' : ['t-b', [0,2], 'top'],
26100 'top' : [ 'b-t', [0,-2], 'bottom']
26106 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26111 delay : null, // can be { show : 300 , hide: 500}
26115 hoverState : null, //???
26117 placement : 'bottom',
26121 getAutoCreate : function(){
26128 cls : 'tooltip-arrow'
26131 cls : 'tooltip-inner'
26138 bind : function(el)
26144 enter : function () {
26146 if (this.timeout != null) {
26147 clearTimeout(this.timeout);
26150 this.hoverState = 'in';
26151 //Roo.log("enter - show");
26152 if (!this.delay || !this.delay.show) {
26157 this.timeout = setTimeout(function () {
26158 if (_t.hoverState == 'in') {
26161 }, this.delay.show);
26165 clearTimeout(this.timeout);
26167 this.hoverState = 'out';
26168 if (!this.delay || !this.delay.hide) {
26174 this.timeout = setTimeout(function () {
26175 //Roo.log("leave - timeout");
26177 if (_t.hoverState == 'out') {
26179 Roo.bootstrap.Tooltip.currentEl = false;
26184 show : function (msg)
26187 this.render(document.body);
26190 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26192 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26194 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26196 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26198 var placement = typeof this.placement == 'function' ?
26199 this.placement.call(this, this.el, on_el) :
26202 var autoToken = /\s?auto?\s?/i;
26203 var autoPlace = autoToken.test(placement);
26205 placement = placement.replace(autoToken, '') || 'top';
26209 //this.el.setXY([0,0]);
26211 //this.el.dom.style.display='block';
26213 //this.el.appendTo(on_el);
26215 var p = this.getPosition();
26216 var box = this.el.getBox();
26222 var align = this.alignment[placement];
26224 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26226 if(placement == 'top' || placement == 'bottom'){
26228 placement = 'right';
26231 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26232 placement = 'left';
26235 var scroll = Roo.select('body', true).first().getScroll();
26237 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26241 align = this.alignment[placement];
26244 this.el.alignTo(this.bindEl, align[0],align[1]);
26245 //var arrow = this.el.select('.arrow',true).first();
26246 //arrow.set(align[2],
26248 this.el.addClass(placement);
26250 this.el.addClass('in fade');
26252 this.hoverState = null;
26254 if (this.el.hasClass('fade')) {
26265 //this.el.setXY([0,0]);
26266 this.el.removeClass('in');
26282 * @class Roo.bootstrap.LocationPicker
26283 * @extends Roo.bootstrap.Component
26284 * Bootstrap LocationPicker class
26285 * @cfg {Number} latitude Position when init default 0
26286 * @cfg {Number} longitude Position when init default 0
26287 * @cfg {Number} zoom default 15
26288 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26289 * @cfg {Boolean} mapTypeControl default false
26290 * @cfg {Boolean} disableDoubleClickZoom default false
26291 * @cfg {Boolean} scrollwheel default true
26292 * @cfg {Boolean} streetViewControl default false
26293 * @cfg {Number} radius default 0
26294 * @cfg {String} locationName
26295 * @cfg {Boolean} draggable default true
26296 * @cfg {Boolean} enableAutocomplete default false
26297 * @cfg {Boolean} enableReverseGeocode default true
26298 * @cfg {String} markerTitle
26301 * Create a new LocationPicker
26302 * @param {Object} config The config object
26306 Roo.bootstrap.LocationPicker = function(config){
26308 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26313 * Fires when the picker initialized.
26314 * @param {Roo.bootstrap.LocationPicker} this
26315 * @param {Google Location} location
26319 * @event positionchanged
26320 * Fires when the picker position changed.
26321 * @param {Roo.bootstrap.LocationPicker} this
26322 * @param {Google Location} location
26324 positionchanged : true,
26327 * Fires when the map resize.
26328 * @param {Roo.bootstrap.LocationPicker} this
26333 * Fires when the map show.
26334 * @param {Roo.bootstrap.LocationPicker} this
26339 * Fires when the map hide.
26340 * @param {Roo.bootstrap.LocationPicker} this
26345 * Fires when click the map.
26346 * @param {Roo.bootstrap.LocationPicker} this
26347 * @param {Map event} e
26351 * @event mapRightClick
26352 * Fires when right click the map.
26353 * @param {Roo.bootstrap.LocationPicker} this
26354 * @param {Map event} e
26356 mapRightClick : true,
26358 * @event markerClick
26359 * Fires when click the marker.
26360 * @param {Roo.bootstrap.LocationPicker} this
26361 * @param {Map event} e
26363 markerClick : true,
26365 * @event markerRightClick
26366 * Fires when right click the marker.
26367 * @param {Roo.bootstrap.LocationPicker} this
26368 * @param {Map event} e
26370 markerRightClick : true,
26372 * @event OverlayViewDraw
26373 * Fires when OverlayView Draw
26374 * @param {Roo.bootstrap.LocationPicker} this
26376 OverlayViewDraw : true,
26378 * @event OverlayViewOnAdd
26379 * Fires when OverlayView Draw
26380 * @param {Roo.bootstrap.LocationPicker} this
26382 OverlayViewOnAdd : true,
26384 * @event OverlayViewOnRemove
26385 * Fires when OverlayView Draw
26386 * @param {Roo.bootstrap.LocationPicker} this
26388 OverlayViewOnRemove : true,
26390 * @event OverlayViewShow
26391 * Fires when OverlayView Draw
26392 * @param {Roo.bootstrap.LocationPicker} this
26393 * @param {Pixel} cpx
26395 OverlayViewShow : true,
26397 * @event OverlayViewHide
26398 * Fires when OverlayView Draw
26399 * @param {Roo.bootstrap.LocationPicker} this
26401 OverlayViewHide : true,
26403 * @event loadexception
26404 * Fires when load google lib failed.
26405 * @param {Roo.bootstrap.LocationPicker} this
26407 loadexception : true
26412 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26414 gMapContext: false,
26420 mapTypeControl: false,
26421 disableDoubleClickZoom: false,
26423 streetViewControl: false,
26427 enableAutocomplete: false,
26428 enableReverseGeocode: true,
26431 getAutoCreate: function()
26436 cls: 'roo-location-picker'
26442 initEvents: function(ct, position)
26444 if(!this.el.getWidth() || this.isApplied()){
26448 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26453 initial: function()
26455 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26456 this.fireEvent('loadexception', this);
26460 if(!this.mapTypeId){
26461 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26464 this.gMapContext = this.GMapContext();
26466 this.initOverlayView();
26468 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26472 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26473 _this.setPosition(_this.gMapContext.marker.position);
26476 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26477 _this.fireEvent('mapClick', this, event);
26481 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26482 _this.fireEvent('mapRightClick', this, event);
26486 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26487 _this.fireEvent('markerClick', this, event);
26491 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26492 _this.fireEvent('markerRightClick', this, event);
26496 this.setPosition(this.gMapContext.location);
26498 this.fireEvent('initial', this, this.gMapContext.location);
26501 initOverlayView: function()
26505 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26509 _this.fireEvent('OverlayViewDraw', _this);
26514 _this.fireEvent('OverlayViewOnAdd', _this);
26517 onRemove: function()
26519 _this.fireEvent('OverlayViewOnRemove', _this);
26522 show: function(cpx)
26524 _this.fireEvent('OverlayViewShow', _this, cpx);
26529 _this.fireEvent('OverlayViewHide', _this);
26535 fromLatLngToContainerPixel: function(event)
26537 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26540 isApplied: function()
26542 return this.getGmapContext() == false ? false : true;
26545 getGmapContext: function()
26547 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26550 GMapContext: function()
26552 var position = new google.maps.LatLng(this.latitude, this.longitude);
26554 var _map = new google.maps.Map(this.el.dom, {
26557 mapTypeId: this.mapTypeId,
26558 mapTypeControl: this.mapTypeControl,
26559 disableDoubleClickZoom: this.disableDoubleClickZoom,
26560 scrollwheel: this.scrollwheel,
26561 streetViewControl: this.streetViewControl,
26562 locationName: this.locationName,
26563 draggable: this.draggable,
26564 enableAutocomplete: this.enableAutocomplete,
26565 enableReverseGeocode: this.enableReverseGeocode
26568 var _marker = new google.maps.Marker({
26569 position: position,
26571 title: this.markerTitle,
26572 draggable: this.draggable
26579 location: position,
26580 radius: this.radius,
26581 locationName: this.locationName,
26582 addressComponents: {
26583 formatted_address: null,
26584 addressLine1: null,
26585 addressLine2: null,
26587 streetNumber: null,
26591 stateOrProvince: null
26594 domContainer: this.el.dom,
26595 geodecoder: new google.maps.Geocoder()
26599 drawCircle: function(center, radius, options)
26601 if (this.gMapContext.circle != null) {
26602 this.gMapContext.circle.setMap(null);
26606 options = Roo.apply({}, options, {
26607 strokeColor: "#0000FF",
26608 strokeOpacity: .35,
26610 fillColor: "#0000FF",
26614 options.map = this.gMapContext.map;
26615 options.radius = radius;
26616 options.center = center;
26617 this.gMapContext.circle = new google.maps.Circle(options);
26618 return this.gMapContext.circle;
26624 setPosition: function(location)
26626 this.gMapContext.location = location;
26627 this.gMapContext.marker.setPosition(location);
26628 this.gMapContext.map.panTo(location);
26629 this.drawCircle(location, this.gMapContext.radius, {});
26633 if (this.gMapContext.settings.enableReverseGeocode) {
26634 this.gMapContext.geodecoder.geocode({
26635 latLng: this.gMapContext.location
26636 }, function(results, status) {
26638 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26639 _this.gMapContext.locationName = results[0].formatted_address;
26640 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26642 _this.fireEvent('positionchanged', this, location);
26649 this.fireEvent('positionchanged', this, location);
26654 google.maps.event.trigger(this.gMapContext.map, "resize");
26656 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26658 this.fireEvent('resize', this);
26661 setPositionByLatLng: function(latitude, longitude)
26663 this.setPosition(new google.maps.LatLng(latitude, longitude));
26666 getCurrentPosition: function()
26669 latitude: this.gMapContext.location.lat(),
26670 longitude: this.gMapContext.location.lng()
26674 getAddressName: function()
26676 return this.gMapContext.locationName;
26679 getAddressComponents: function()
26681 return this.gMapContext.addressComponents;
26684 address_component_from_google_geocode: function(address_components)
26688 for (var i = 0; i < address_components.length; i++) {
26689 var component = address_components[i];
26690 if (component.types.indexOf("postal_code") >= 0) {
26691 result.postalCode = component.short_name;
26692 } else if (component.types.indexOf("street_number") >= 0) {
26693 result.streetNumber = component.short_name;
26694 } else if (component.types.indexOf("route") >= 0) {
26695 result.streetName = component.short_name;
26696 } else if (component.types.indexOf("neighborhood") >= 0) {
26697 result.city = component.short_name;
26698 } else if (component.types.indexOf("locality") >= 0) {
26699 result.city = component.short_name;
26700 } else if (component.types.indexOf("sublocality") >= 0) {
26701 result.district = component.short_name;
26702 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26703 result.stateOrProvince = component.short_name;
26704 } else if (component.types.indexOf("country") >= 0) {
26705 result.country = component.short_name;
26709 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26710 result.addressLine2 = "";
26714 setZoomLevel: function(zoom)
26716 this.gMapContext.map.setZoom(zoom);
26729 this.fireEvent('show', this);
26740 this.fireEvent('hide', this);
26745 Roo.apply(Roo.bootstrap.LocationPicker, {
26747 OverlayView : function(map, options)
26749 options = options || {};
26763 * @class Roo.bootstrap.Alert
26764 * @extends Roo.bootstrap.Component
26765 * Bootstrap Alert class
26766 * @cfg {String} title The title of alert
26767 * @cfg {String} html The content of alert
26768 * @cfg {String} weight ( success | info | warning | danger )
26769 * @cfg {String} faicon font-awesomeicon
26772 * Create a new alert
26773 * @param {Object} config The config object
26777 Roo.bootstrap.Alert = function(config){
26778 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26782 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26789 getAutoCreate : function()
26798 cls : 'roo-alert-icon'
26803 cls : 'roo-alert-title',
26808 cls : 'roo-alert-text',
26815 cfg.cn[0].cls += ' fa ' + this.faicon;
26819 cfg.cls += ' alert-' + this.weight;
26825 initEvents: function()
26827 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26830 setTitle : function(str)
26832 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26835 setText : function(str)
26837 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26840 setWeight : function(weight)
26843 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26846 this.weight = weight;
26848 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26851 setIcon : function(icon)
26854 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26857 this.faicon = icon;
26859 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26880 * @class Roo.bootstrap.UploadCropbox
26881 * @extends Roo.bootstrap.Component
26882 * Bootstrap UploadCropbox class
26883 * @cfg {String} emptyText show when image has been loaded
26884 * @cfg {String} rotateNotify show when image too small to rotate
26885 * @cfg {Number} errorTimeout default 3000
26886 * @cfg {Number} minWidth default 300
26887 * @cfg {Number} minHeight default 300
26888 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26889 * @cfg {Boolean} isDocument (true|false) default false
26890 * @cfg {String} url action url
26891 * @cfg {String} paramName default 'imageUpload'
26892 * @cfg {String} method default POST
26893 * @cfg {Boolean} loadMask (true|false) default true
26894 * @cfg {Boolean} loadingText default 'Loading...'
26897 * Create a new UploadCropbox
26898 * @param {Object} config The config object
26901 Roo.bootstrap.UploadCropbox = function(config){
26902 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26906 * @event beforeselectfile
26907 * Fire before select file
26908 * @param {Roo.bootstrap.UploadCropbox} this
26910 "beforeselectfile" : true,
26913 * Fire after initEvent
26914 * @param {Roo.bootstrap.UploadCropbox} this
26919 * Fire after initEvent
26920 * @param {Roo.bootstrap.UploadCropbox} this
26921 * @param {String} data
26926 * Fire when preparing the file data
26927 * @param {Roo.bootstrap.UploadCropbox} this
26928 * @param {Object} file
26933 * Fire when get exception
26934 * @param {Roo.bootstrap.UploadCropbox} this
26935 * @param {XMLHttpRequest} xhr
26937 "exception" : true,
26939 * @event beforeloadcanvas
26940 * Fire before load the canvas
26941 * @param {Roo.bootstrap.UploadCropbox} this
26942 * @param {String} src
26944 "beforeloadcanvas" : true,
26947 * Fire when trash image
26948 * @param {Roo.bootstrap.UploadCropbox} this
26953 * Fire when download the image
26954 * @param {Roo.bootstrap.UploadCropbox} this
26958 * @event footerbuttonclick
26959 * Fire when footerbuttonclick
26960 * @param {Roo.bootstrap.UploadCropbox} this
26961 * @param {String} type
26963 "footerbuttonclick" : true,
26967 * @param {Roo.bootstrap.UploadCropbox} this
26972 * Fire when rotate the image
26973 * @param {Roo.bootstrap.UploadCropbox} this
26974 * @param {String} pos
26979 * Fire when inspect the file
26980 * @param {Roo.bootstrap.UploadCropbox} this
26981 * @param {Object} file
26986 * Fire when xhr upload the file
26987 * @param {Roo.bootstrap.UploadCropbox} this
26988 * @param {Object} data
26993 * Fire when arrange the file data
26994 * @param {Roo.bootstrap.UploadCropbox} this
26995 * @param {Object} formData
27000 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27003 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27005 emptyText : 'Click to upload image',
27006 rotateNotify : 'Image is too small to rotate',
27007 errorTimeout : 3000,
27021 cropType : 'image/jpeg',
27023 canvasLoaded : false,
27024 isDocument : false,
27026 paramName : 'imageUpload',
27028 loadingText : 'Loading...',
27031 getAutoCreate : function()
27035 cls : 'roo-upload-cropbox',
27039 cls : 'roo-upload-cropbox-selector',
27044 cls : 'roo-upload-cropbox-body',
27045 style : 'cursor:pointer',
27049 cls : 'roo-upload-cropbox-preview'
27053 cls : 'roo-upload-cropbox-thumb'
27057 cls : 'roo-upload-cropbox-empty-notify',
27058 html : this.emptyText
27062 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27063 html : this.rotateNotify
27069 cls : 'roo-upload-cropbox-footer',
27072 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27082 onRender : function(ct, position)
27084 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27086 if (this.buttons.length) {
27088 Roo.each(this.buttons, function(bb) {
27090 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27092 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27098 this.maskEl = this.el;
27102 initEvents : function()
27104 this.urlAPI = (window.createObjectURL && window) ||
27105 (window.URL && URL.revokeObjectURL && URL) ||
27106 (window.webkitURL && webkitURL);
27108 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27109 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27111 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27112 this.selectorEl.hide();
27114 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27115 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27117 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27118 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27119 this.thumbEl.hide();
27121 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27122 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27124 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27125 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27126 this.errorEl.hide();
27128 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27129 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27130 this.footerEl.hide();
27132 this.setThumbBoxSize();
27138 this.fireEvent('initial', this);
27145 window.addEventListener("resize", function() { _this.resize(); } );
27147 this.bodyEl.on('click', this.beforeSelectFile, this);
27150 this.bodyEl.on('touchstart', this.onTouchStart, this);
27151 this.bodyEl.on('touchmove', this.onTouchMove, this);
27152 this.bodyEl.on('touchend', this.onTouchEnd, this);
27156 this.bodyEl.on('mousedown', this.onMouseDown, this);
27157 this.bodyEl.on('mousemove', this.onMouseMove, this);
27158 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27159 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27160 Roo.get(document).on('mouseup', this.onMouseUp, this);
27163 this.selectorEl.on('change', this.onFileSelected, this);
27169 this.baseScale = 1;
27171 this.baseRotate = 1;
27172 this.dragable = false;
27173 this.pinching = false;
27176 this.cropData = false;
27177 this.notifyEl.dom.innerHTML = this.emptyText;
27179 this.selectorEl.dom.value = '';
27183 resize : function()
27185 if(this.fireEvent('resize', this) != false){
27186 this.setThumbBoxPosition();
27187 this.setCanvasPosition();
27191 onFooterButtonClick : function(e, el, o, type)
27194 case 'rotate-left' :
27195 this.onRotateLeft(e);
27197 case 'rotate-right' :
27198 this.onRotateRight(e);
27201 this.beforeSelectFile(e);
27216 this.fireEvent('footerbuttonclick', this, type);
27219 beforeSelectFile : function(e)
27221 e.preventDefault();
27223 if(this.fireEvent('beforeselectfile', this) != false){
27224 this.selectorEl.dom.click();
27228 onFileSelected : function(e)
27230 e.preventDefault();
27232 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27236 var file = this.selectorEl.dom.files[0];
27238 if(this.fireEvent('inspect', this, file) != false){
27239 this.prepare(file);
27244 trash : function(e)
27246 this.fireEvent('trash', this);
27249 download : function(e)
27251 this.fireEvent('download', this);
27254 loadCanvas : function(src)
27256 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27260 this.imageEl = document.createElement('img');
27264 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27266 this.imageEl.src = src;
27270 onLoadCanvas : function()
27272 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27273 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27275 this.bodyEl.un('click', this.beforeSelectFile, this);
27277 this.notifyEl.hide();
27278 this.thumbEl.show();
27279 this.footerEl.show();
27281 this.baseRotateLevel();
27283 if(this.isDocument){
27284 this.setThumbBoxSize();
27287 this.setThumbBoxPosition();
27289 this.baseScaleLevel();
27295 this.canvasLoaded = true;
27298 this.maskEl.unmask();
27303 setCanvasPosition : function()
27305 if(!this.canvasEl){
27309 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27310 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27312 this.previewEl.setLeft(pw);
27313 this.previewEl.setTop(ph);
27317 onMouseDown : function(e)
27321 this.dragable = true;
27322 this.pinching = false;
27324 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27325 this.dragable = false;
27329 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27330 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27334 onMouseMove : function(e)
27338 if(!this.canvasLoaded){
27342 if (!this.dragable){
27346 var minX = Math.ceil(this.thumbEl.getLeft(true));
27347 var minY = Math.ceil(this.thumbEl.getTop(true));
27349 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27350 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27352 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27353 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27355 x = x - this.mouseX;
27356 y = y - this.mouseY;
27358 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27359 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27361 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27362 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27364 this.previewEl.setLeft(bgX);
27365 this.previewEl.setTop(bgY);
27367 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27368 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27371 onMouseUp : function(e)
27375 this.dragable = false;
27378 onMouseWheel : function(e)
27382 this.startScale = this.scale;
27384 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27386 if(!this.zoomable()){
27387 this.scale = this.startScale;
27396 zoomable : function()
27398 var minScale = this.thumbEl.getWidth() / this.minWidth;
27400 if(this.minWidth < this.minHeight){
27401 minScale = this.thumbEl.getHeight() / this.minHeight;
27404 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27405 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27409 (this.rotate == 0 || this.rotate == 180) &&
27411 width > this.imageEl.OriginWidth ||
27412 height > this.imageEl.OriginHeight ||
27413 (width < this.minWidth && height < this.minHeight)
27421 (this.rotate == 90 || this.rotate == 270) &&
27423 width > this.imageEl.OriginWidth ||
27424 height > this.imageEl.OriginHeight ||
27425 (width < this.minHeight && height < this.minWidth)
27432 !this.isDocument &&
27433 (this.rotate == 0 || this.rotate == 180) &&
27435 width < this.minWidth ||
27436 width > this.imageEl.OriginWidth ||
27437 height < this.minHeight ||
27438 height > this.imageEl.OriginHeight
27445 !this.isDocument &&
27446 (this.rotate == 90 || this.rotate == 270) &&
27448 width < this.minHeight ||
27449 width > this.imageEl.OriginWidth ||
27450 height < this.minWidth ||
27451 height > this.imageEl.OriginHeight
27461 onRotateLeft : function(e)
27463 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27465 var minScale = this.thumbEl.getWidth() / this.minWidth;
27467 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27468 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27470 this.startScale = this.scale;
27472 while (this.getScaleLevel() < minScale){
27474 this.scale = this.scale + 1;
27476 if(!this.zoomable()){
27481 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27482 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27487 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27494 this.scale = this.startScale;
27496 this.onRotateFail();
27501 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27503 if(this.isDocument){
27504 this.setThumbBoxSize();
27505 this.setThumbBoxPosition();
27506 this.setCanvasPosition();
27511 this.fireEvent('rotate', this, 'left');
27515 onRotateRight : function(e)
27517 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27519 var minScale = this.thumbEl.getWidth() / this.minWidth;
27521 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27522 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27524 this.startScale = this.scale;
27526 while (this.getScaleLevel() < minScale){
27528 this.scale = this.scale + 1;
27530 if(!this.zoomable()){
27535 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27536 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27541 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27548 this.scale = this.startScale;
27550 this.onRotateFail();
27555 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27557 if(this.isDocument){
27558 this.setThumbBoxSize();
27559 this.setThumbBoxPosition();
27560 this.setCanvasPosition();
27565 this.fireEvent('rotate', this, 'right');
27568 onRotateFail : function()
27570 this.errorEl.show(true);
27574 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27579 this.previewEl.dom.innerHTML = '';
27581 var canvasEl = document.createElement("canvas");
27583 var contextEl = canvasEl.getContext("2d");
27585 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27586 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27587 var center = this.imageEl.OriginWidth / 2;
27589 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27590 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27591 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27592 center = this.imageEl.OriginHeight / 2;
27595 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27597 contextEl.translate(center, center);
27598 contextEl.rotate(this.rotate * Math.PI / 180);
27600 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27602 this.canvasEl = document.createElement("canvas");
27604 this.contextEl = this.canvasEl.getContext("2d");
27606 switch (this.rotate) {
27609 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27610 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27612 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27617 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27618 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27620 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27621 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27625 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27630 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27631 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27633 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27634 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);
27638 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);
27643 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27644 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27646 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27647 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27651 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);
27658 this.previewEl.appendChild(this.canvasEl);
27660 this.setCanvasPosition();
27665 if(!this.canvasLoaded){
27669 var imageCanvas = document.createElement("canvas");
27671 var imageContext = imageCanvas.getContext("2d");
27673 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27674 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27676 var center = imageCanvas.width / 2;
27678 imageContext.translate(center, center);
27680 imageContext.rotate(this.rotate * Math.PI / 180);
27682 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27684 var canvas = document.createElement("canvas");
27686 var context = canvas.getContext("2d");
27688 canvas.width = this.minWidth;
27689 canvas.height = this.minHeight;
27691 switch (this.rotate) {
27694 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27695 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27697 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27698 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27700 var targetWidth = this.minWidth - 2 * x;
27701 var targetHeight = this.minHeight - 2 * y;
27705 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27706 scale = targetWidth / width;
27709 if(x > 0 && y == 0){
27710 scale = targetHeight / height;
27713 if(x > 0 && y > 0){
27714 scale = targetWidth / width;
27716 if(width < height){
27717 scale = targetHeight / height;
27721 context.scale(scale, scale);
27723 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27724 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27726 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27727 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27729 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27734 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27735 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27737 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27738 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27740 var targetWidth = this.minWidth - 2 * x;
27741 var targetHeight = this.minHeight - 2 * y;
27745 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27746 scale = targetWidth / width;
27749 if(x > 0 && y == 0){
27750 scale = targetHeight / height;
27753 if(x > 0 && y > 0){
27754 scale = targetWidth / width;
27756 if(width < height){
27757 scale = targetHeight / height;
27761 context.scale(scale, scale);
27763 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27764 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27766 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27767 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27769 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27771 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27776 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27777 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27779 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27780 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27782 var targetWidth = this.minWidth - 2 * x;
27783 var targetHeight = this.minHeight - 2 * y;
27787 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27788 scale = targetWidth / width;
27791 if(x > 0 && y == 0){
27792 scale = targetHeight / height;
27795 if(x > 0 && y > 0){
27796 scale = targetWidth / width;
27798 if(width < height){
27799 scale = targetHeight / height;
27803 context.scale(scale, scale);
27805 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27806 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27808 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27809 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27811 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27812 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27814 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27819 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27820 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27822 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27823 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27825 var targetWidth = this.minWidth - 2 * x;
27826 var targetHeight = this.minHeight - 2 * y;
27830 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27831 scale = targetWidth / width;
27834 if(x > 0 && y == 0){
27835 scale = targetHeight / height;
27838 if(x > 0 && y > 0){
27839 scale = targetWidth / width;
27841 if(width < height){
27842 scale = targetHeight / height;
27846 context.scale(scale, scale);
27848 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27849 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27851 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27852 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27854 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27856 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27863 this.cropData = canvas.toDataURL(this.cropType);
27865 if(this.fireEvent('crop', this, this.cropData) !== false){
27866 this.process(this.file, this.cropData);
27873 setThumbBoxSize : function()
27877 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27878 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27879 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27881 this.minWidth = width;
27882 this.minHeight = height;
27884 if(this.rotate == 90 || this.rotate == 270){
27885 this.minWidth = height;
27886 this.minHeight = width;
27891 width = Math.ceil(this.minWidth * height / this.minHeight);
27893 if(this.minWidth > this.minHeight){
27895 height = Math.ceil(this.minHeight * width / this.minWidth);
27898 this.thumbEl.setStyle({
27899 width : width + 'px',
27900 height : height + 'px'
27907 setThumbBoxPosition : function()
27909 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27910 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27912 this.thumbEl.setLeft(x);
27913 this.thumbEl.setTop(y);
27917 baseRotateLevel : function()
27919 this.baseRotate = 1;
27922 typeof(this.exif) != 'undefined' &&
27923 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27924 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27926 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27929 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27933 baseScaleLevel : function()
27937 if(this.isDocument){
27939 if(this.baseRotate == 6 || this.baseRotate == 8){
27941 height = this.thumbEl.getHeight();
27942 this.baseScale = height / this.imageEl.OriginWidth;
27944 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27945 width = this.thumbEl.getWidth();
27946 this.baseScale = width / this.imageEl.OriginHeight;
27952 height = this.thumbEl.getHeight();
27953 this.baseScale = height / this.imageEl.OriginHeight;
27955 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27956 width = this.thumbEl.getWidth();
27957 this.baseScale = width / this.imageEl.OriginWidth;
27963 if(this.baseRotate == 6 || this.baseRotate == 8){
27965 width = this.thumbEl.getHeight();
27966 this.baseScale = width / this.imageEl.OriginHeight;
27968 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27969 height = this.thumbEl.getWidth();
27970 this.baseScale = height / this.imageEl.OriginHeight;
27973 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27974 height = this.thumbEl.getWidth();
27975 this.baseScale = height / this.imageEl.OriginHeight;
27977 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27978 width = this.thumbEl.getHeight();
27979 this.baseScale = width / this.imageEl.OriginWidth;
27986 width = this.thumbEl.getWidth();
27987 this.baseScale = width / this.imageEl.OriginWidth;
27989 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27990 height = this.thumbEl.getHeight();
27991 this.baseScale = height / this.imageEl.OriginHeight;
27994 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27996 height = this.thumbEl.getHeight();
27997 this.baseScale = height / this.imageEl.OriginHeight;
27999 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28000 width = this.thumbEl.getWidth();
28001 this.baseScale = width / this.imageEl.OriginWidth;
28009 getScaleLevel : function()
28011 return this.baseScale * Math.pow(1.1, this.scale);
28014 onTouchStart : function(e)
28016 if(!this.canvasLoaded){
28017 this.beforeSelectFile(e);
28021 var touches = e.browserEvent.touches;
28027 if(touches.length == 1){
28028 this.onMouseDown(e);
28032 if(touches.length != 2){
28038 for(var i = 0, finger; finger = touches[i]; i++){
28039 coords.push(finger.pageX, finger.pageY);
28042 var x = Math.pow(coords[0] - coords[2], 2);
28043 var y = Math.pow(coords[1] - coords[3], 2);
28045 this.startDistance = Math.sqrt(x + y);
28047 this.startScale = this.scale;
28049 this.pinching = true;
28050 this.dragable = false;
28054 onTouchMove : function(e)
28056 if(!this.pinching && !this.dragable){
28060 var touches = e.browserEvent.touches;
28067 this.onMouseMove(e);
28073 for(var i = 0, finger; finger = touches[i]; i++){
28074 coords.push(finger.pageX, finger.pageY);
28077 var x = Math.pow(coords[0] - coords[2], 2);
28078 var y = Math.pow(coords[1] - coords[3], 2);
28080 this.endDistance = Math.sqrt(x + y);
28082 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28084 if(!this.zoomable()){
28085 this.scale = this.startScale;
28093 onTouchEnd : function(e)
28095 this.pinching = false;
28096 this.dragable = false;
28100 process : function(file, crop)
28103 this.maskEl.mask(this.loadingText);
28106 this.xhr = new XMLHttpRequest();
28108 file.xhr = this.xhr;
28110 this.xhr.open(this.method, this.url, true);
28113 "Accept": "application/json",
28114 "Cache-Control": "no-cache",
28115 "X-Requested-With": "XMLHttpRequest"
28118 for (var headerName in headers) {
28119 var headerValue = headers[headerName];
28121 this.xhr.setRequestHeader(headerName, headerValue);
28127 this.xhr.onload = function()
28129 _this.xhrOnLoad(_this.xhr);
28132 this.xhr.onerror = function()
28134 _this.xhrOnError(_this.xhr);
28137 var formData = new FormData();
28139 formData.append('returnHTML', 'NO');
28142 formData.append('crop', crop);
28145 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28146 formData.append(this.paramName, file, file.name);
28149 if(typeof(file.filename) != 'undefined'){
28150 formData.append('filename', file.filename);
28153 if(typeof(file.mimetype) != 'undefined'){
28154 formData.append('mimetype', file.mimetype);
28157 if(this.fireEvent('arrange', this, formData) != false){
28158 this.xhr.send(formData);
28162 xhrOnLoad : function(xhr)
28165 this.maskEl.unmask();
28168 if (xhr.readyState !== 4) {
28169 this.fireEvent('exception', this, xhr);
28173 var response = Roo.decode(xhr.responseText);
28175 if(!response.success){
28176 this.fireEvent('exception', this, xhr);
28180 var response = Roo.decode(xhr.responseText);
28182 this.fireEvent('upload', this, response);
28186 xhrOnError : function()
28189 this.maskEl.unmask();
28192 Roo.log('xhr on error');
28194 var response = Roo.decode(xhr.responseText);
28200 prepare : function(file)
28203 this.maskEl.mask(this.loadingText);
28209 if(typeof(file) === 'string'){
28210 this.loadCanvas(file);
28214 if(!file || !this.urlAPI){
28219 this.cropType = file.type;
28223 if(this.fireEvent('prepare', this, this.file) != false){
28225 var reader = new FileReader();
28227 reader.onload = function (e) {
28228 if (e.target.error) {
28229 Roo.log(e.target.error);
28233 var buffer = e.target.result,
28234 dataView = new DataView(buffer),
28236 maxOffset = dataView.byteLength - 4,
28240 if (dataView.getUint16(0) === 0xffd8) {
28241 while (offset < maxOffset) {
28242 markerBytes = dataView.getUint16(offset);
28244 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28245 markerLength = dataView.getUint16(offset + 2) + 2;
28246 if (offset + markerLength > dataView.byteLength) {
28247 Roo.log('Invalid meta data: Invalid segment size.');
28251 if(markerBytes == 0xffe1){
28252 _this.parseExifData(
28259 offset += markerLength;
28269 var url = _this.urlAPI.createObjectURL(_this.file);
28271 _this.loadCanvas(url);
28276 reader.readAsArrayBuffer(this.file);
28282 parseExifData : function(dataView, offset, length)
28284 var tiffOffset = offset + 10,
28288 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28289 // No Exif data, might be XMP data instead
28293 // Check for the ASCII code for "Exif" (0x45786966):
28294 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28295 // No Exif data, might be XMP data instead
28298 if (tiffOffset + 8 > dataView.byteLength) {
28299 Roo.log('Invalid Exif data: Invalid segment size.');
28302 // Check for the two null bytes:
28303 if (dataView.getUint16(offset + 8) !== 0x0000) {
28304 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28307 // Check the byte alignment:
28308 switch (dataView.getUint16(tiffOffset)) {
28310 littleEndian = true;
28313 littleEndian = false;
28316 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28319 // Check for the TIFF tag marker (0x002A):
28320 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28321 Roo.log('Invalid Exif data: Missing TIFF marker.');
28324 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28325 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28327 this.parseExifTags(
28330 tiffOffset + dirOffset,
28335 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28340 if (dirOffset + 6 > dataView.byteLength) {
28341 Roo.log('Invalid Exif data: Invalid directory offset.');
28344 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28345 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28346 if (dirEndOffset + 4 > dataView.byteLength) {
28347 Roo.log('Invalid Exif data: Invalid directory size.');
28350 for (i = 0; i < tagsNumber; i += 1) {
28354 dirOffset + 2 + 12 * i, // tag offset
28358 // Return the offset to the next directory:
28359 return dataView.getUint32(dirEndOffset, littleEndian);
28362 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28364 var tag = dataView.getUint16(offset, littleEndian);
28366 this.exif[tag] = this.getExifValue(
28370 dataView.getUint16(offset + 2, littleEndian), // tag type
28371 dataView.getUint32(offset + 4, littleEndian), // tag length
28376 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28378 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28387 Roo.log('Invalid Exif data: Invalid tag type.');
28391 tagSize = tagType.size * length;
28392 // Determine if the value is contained in the dataOffset bytes,
28393 // or if the value at the dataOffset is a pointer to the actual data:
28394 dataOffset = tagSize > 4 ?
28395 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28396 if (dataOffset + tagSize > dataView.byteLength) {
28397 Roo.log('Invalid Exif data: Invalid data offset.');
28400 if (length === 1) {
28401 return tagType.getValue(dataView, dataOffset, littleEndian);
28404 for (i = 0; i < length; i += 1) {
28405 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28408 if (tagType.ascii) {
28410 // Concatenate the chars:
28411 for (i = 0; i < values.length; i += 1) {
28413 // Ignore the terminating NULL byte(s):
28414 if (c === '\u0000') {
28426 Roo.apply(Roo.bootstrap.UploadCropbox, {
28428 'Orientation': 0x0112
28432 1: 0, //'top-left',
28434 3: 180, //'bottom-right',
28435 // 4: 'bottom-left',
28437 6: 90, //'right-top',
28438 // 7: 'right-bottom',
28439 8: 270 //'left-bottom'
28443 // byte, 8-bit unsigned int:
28445 getValue: function (dataView, dataOffset) {
28446 return dataView.getUint8(dataOffset);
28450 // ascii, 8-bit byte:
28452 getValue: function (dataView, dataOffset) {
28453 return String.fromCharCode(dataView.getUint8(dataOffset));
28458 // short, 16 bit int:
28460 getValue: function (dataView, dataOffset, littleEndian) {
28461 return dataView.getUint16(dataOffset, littleEndian);
28465 // long, 32 bit int:
28467 getValue: function (dataView, dataOffset, littleEndian) {
28468 return dataView.getUint32(dataOffset, littleEndian);
28472 // rational = two long values, first is numerator, second is denominator:
28474 getValue: function (dataView, dataOffset, littleEndian) {
28475 return dataView.getUint32(dataOffset, littleEndian) /
28476 dataView.getUint32(dataOffset + 4, littleEndian);
28480 // slong, 32 bit signed int:
28482 getValue: function (dataView, dataOffset, littleEndian) {
28483 return dataView.getInt32(dataOffset, littleEndian);
28487 // srational, two slongs, first is numerator, second is denominator:
28489 getValue: function (dataView, dataOffset, littleEndian) {
28490 return dataView.getInt32(dataOffset, littleEndian) /
28491 dataView.getInt32(dataOffset + 4, littleEndian);
28501 cls : 'btn-group roo-upload-cropbox-rotate-left',
28502 action : 'rotate-left',
28506 cls : 'btn btn-default',
28507 html : '<i class="fa fa-undo"></i>'
28513 cls : 'btn-group roo-upload-cropbox-picture',
28514 action : 'picture',
28518 cls : 'btn btn-default',
28519 html : '<i class="fa fa-picture-o"></i>'
28525 cls : 'btn-group roo-upload-cropbox-rotate-right',
28526 action : 'rotate-right',
28530 cls : 'btn btn-default',
28531 html : '<i class="fa fa-repeat"></i>'
28539 cls : 'btn-group roo-upload-cropbox-rotate-left',
28540 action : 'rotate-left',
28544 cls : 'btn btn-default',
28545 html : '<i class="fa fa-undo"></i>'
28551 cls : 'btn-group roo-upload-cropbox-download',
28552 action : 'download',
28556 cls : 'btn btn-default',
28557 html : '<i class="fa fa-download"></i>'
28563 cls : 'btn-group roo-upload-cropbox-crop',
28568 cls : 'btn btn-default',
28569 html : '<i class="fa fa-crop"></i>'
28575 cls : 'btn-group roo-upload-cropbox-trash',
28580 cls : 'btn btn-default',
28581 html : '<i class="fa fa-trash"></i>'
28587 cls : 'btn-group roo-upload-cropbox-rotate-right',
28588 action : 'rotate-right',
28592 cls : 'btn btn-default',
28593 html : '<i class="fa fa-repeat"></i>'
28601 cls : 'btn-group roo-upload-cropbox-rotate-left',
28602 action : 'rotate-left',
28606 cls : 'btn btn-default',
28607 html : '<i class="fa fa-undo"></i>'
28613 cls : 'btn-group roo-upload-cropbox-rotate-right',
28614 action : 'rotate-right',
28618 cls : 'btn btn-default',
28619 html : '<i class="fa fa-repeat"></i>'
28632 * @class Roo.bootstrap.DocumentManager
28633 * @extends Roo.bootstrap.Component
28634 * Bootstrap DocumentManager class
28635 * @cfg {String} paramName default 'imageUpload'
28636 * @cfg {String} toolTipName default 'filename'
28637 * @cfg {String} method default POST
28638 * @cfg {String} url action url
28639 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28640 * @cfg {Boolean} multiple multiple upload default true
28641 * @cfg {Number} thumbSize default 300
28642 * @cfg {String} fieldLabel
28643 * @cfg {Number} labelWidth default 4
28644 * @cfg {String} labelAlign (left|top) default left
28645 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28646 * @cfg {Number} labellg set the width of label (1-12)
28647 * @cfg {Number} labelmd set the width of label (1-12)
28648 * @cfg {Number} labelsm set the width of label (1-12)
28649 * @cfg {Number} labelxs set the width of label (1-12)
28652 * Create a new DocumentManager
28653 * @param {Object} config The config object
28656 Roo.bootstrap.DocumentManager = function(config){
28657 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28660 this.delegates = [];
28665 * Fire when initial the DocumentManager
28666 * @param {Roo.bootstrap.DocumentManager} this
28671 * inspect selected file
28672 * @param {Roo.bootstrap.DocumentManager} this
28673 * @param {File} file
28678 * Fire when xhr load exception
28679 * @param {Roo.bootstrap.DocumentManager} this
28680 * @param {XMLHttpRequest} xhr
28682 "exception" : true,
28684 * @event afterupload
28685 * Fire when xhr load exception
28686 * @param {Roo.bootstrap.DocumentManager} this
28687 * @param {XMLHttpRequest} xhr
28689 "afterupload" : true,
28692 * prepare the form data
28693 * @param {Roo.bootstrap.DocumentManager} this
28694 * @param {Object} formData
28699 * Fire when remove the file
28700 * @param {Roo.bootstrap.DocumentManager} this
28701 * @param {Object} file
28706 * Fire after refresh the file
28707 * @param {Roo.bootstrap.DocumentManager} this
28712 * Fire after click the image
28713 * @param {Roo.bootstrap.DocumentManager} this
28714 * @param {Object} file
28719 * Fire when upload a image and editable set to true
28720 * @param {Roo.bootstrap.DocumentManager} this
28721 * @param {Object} file
28725 * @event beforeselectfile
28726 * Fire before select file
28727 * @param {Roo.bootstrap.DocumentManager} this
28729 "beforeselectfile" : true,
28732 * Fire before process file
28733 * @param {Roo.bootstrap.DocumentManager} this
28734 * @param {Object} file
28738 * @event previewrendered
28739 * Fire when preview rendered
28740 * @param {Roo.bootstrap.DocumentManager} this
28741 * @param {Object} file
28743 "previewrendered" : true,
28746 "previewResize" : true
28751 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28760 paramName : 'imageUpload',
28761 toolTipName : 'filename',
28764 labelAlign : 'left',
28774 getAutoCreate : function()
28776 var managerWidget = {
28778 cls : 'roo-document-manager',
28782 cls : 'roo-document-manager-selector',
28787 cls : 'roo-document-manager-uploader',
28791 cls : 'roo-document-manager-upload-btn',
28792 html : '<i class="fa fa-plus"></i>'
28803 cls : 'column col-md-12',
28808 if(this.fieldLabel.length){
28813 cls : 'column col-md-12',
28814 html : this.fieldLabel
28818 cls : 'column col-md-12',
28823 if(this.labelAlign == 'left'){
28828 html : this.fieldLabel
28837 if(this.labelWidth > 12){
28838 content[0].style = "width: " + this.labelWidth + 'px';
28841 if(this.labelWidth < 13 && this.labelmd == 0){
28842 this.labelmd = this.labelWidth;
28845 if(this.labellg > 0){
28846 content[0].cls += ' col-lg-' + this.labellg;
28847 content[1].cls += ' col-lg-' + (12 - this.labellg);
28850 if(this.labelmd > 0){
28851 content[0].cls += ' col-md-' + this.labelmd;
28852 content[1].cls += ' col-md-' + (12 - this.labelmd);
28855 if(this.labelsm > 0){
28856 content[0].cls += ' col-sm-' + this.labelsm;
28857 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28860 if(this.labelxs > 0){
28861 content[0].cls += ' col-xs-' + this.labelxs;
28862 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28870 cls : 'row clearfix',
28878 initEvents : function()
28880 this.managerEl = this.el.select('.roo-document-manager', true).first();
28881 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28883 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28884 this.selectorEl.hide();
28887 this.selectorEl.attr('multiple', 'multiple');
28890 this.selectorEl.on('change', this.onFileSelected, this);
28892 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28893 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28895 this.uploader.on('click', this.onUploaderClick, this);
28897 this.renderProgressDialog();
28901 window.addEventListener("resize", function() { _this.refresh(); } );
28903 this.fireEvent('initial', this);
28906 renderProgressDialog : function()
28910 this.progressDialog = new Roo.bootstrap.Modal({
28911 cls : 'roo-document-manager-progress-dialog',
28912 allow_close : false,
28922 btnclick : function() {
28923 _this.uploadCancel();
28929 this.progressDialog.render(Roo.get(document.body));
28931 this.progress = new Roo.bootstrap.Progress({
28932 cls : 'roo-document-manager-progress',
28937 this.progress.render(this.progressDialog.getChildContainer());
28939 this.progressBar = new Roo.bootstrap.ProgressBar({
28940 cls : 'roo-document-manager-progress-bar',
28943 aria_valuemax : 12,
28947 this.progressBar.render(this.progress.getChildContainer());
28950 onUploaderClick : function(e)
28952 e.preventDefault();
28954 if(this.fireEvent('beforeselectfile', this) != false){
28955 this.selectorEl.dom.click();
28960 onFileSelected : function(e)
28962 e.preventDefault();
28964 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28968 Roo.each(this.selectorEl.dom.files, function(file){
28969 if(this.fireEvent('inspect', this, file) != false){
28970 this.files.push(file);
28980 this.selectorEl.dom.value = '';
28982 if(!this.files || !this.files.length){
28986 if(this.boxes > 0 && this.files.length > this.boxes){
28987 this.files = this.files.slice(0, this.boxes);
28990 this.uploader.show();
28992 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28993 this.uploader.hide();
29002 Roo.each(this.files, function(file){
29004 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29005 var f = this.renderPreview(file);
29010 if(file.type.indexOf('image') != -1){
29011 this.delegates.push(
29013 _this.process(file);
29014 }).createDelegate(this)
29022 _this.process(file);
29023 }).createDelegate(this)
29028 this.files = files;
29030 this.delegates = this.delegates.concat(docs);
29032 if(!this.delegates.length){
29037 this.progressBar.aria_valuemax = this.delegates.length;
29044 arrange : function()
29046 if(!this.delegates.length){
29047 this.progressDialog.hide();
29052 var delegate = this.delegates.shift();
29054 this.progressDialog.show();
29056 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29058 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29063 refresh : function()
29065 this.uploader.show();
29067 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29068 this.uploader.hide();
29071 Roo.isTouch ? this.closable(false) : this.closable(true);
29073 this.fireEvent('refresh', this);
29076 onRemove : function(e, el, o)
29078 e.preventDefault();
29080 this.fireEvent('remove', this, o);
29084 remove : function(o)
29088 Roo.each(this.files, function(file){
29089 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29098 this.files = files;
29105 Roo.each(this.files, function(file){
29110 file.target.remove();
29119 onClick : function(e, el, o)
29121 e.preventDefault();
29123 this.fireEvent('click', this, o);
29127 closable : function(closable)
29129 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29131 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29143 xhrOnLoad : function(xhr)
29145 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29149 if (xhr.readyState !== 4) {
29151 this.fireEvent('exception', this, xhr);
29155 var response = Roo.decode(xhr.responseText);
29157 if(!response.success){
29159 this.fireEvent('exception', this, xhr);
29163 var file = this.renderPreview(response.data);
29165 this.files.push(file);
29169 this.fireEvent('afterupload', this, xhr);
29173 xhrOnError : function(xhr)
29175 Roo.log('xhr on error');
29177 var response = Roo.decode(xhr.responseText);
29184 process : function(file)
29186 if(this.fireEvent('process', this, file) !== false){
29187 if(this.editable && file.type.indexOf('image') != -1){
29188 this.fireEvent('edit', this, file);
29192 this.uploadStart(file, false);
29199 uploadStart : function(file, crop)
29201 this.xhr = new XMLHttpRequest();
29203 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29208 file.xhr = this.xhr;
29210 this.managerEl.createChild({
29212 cls : 'roo-document-manager-loading',
29216 tooltip : file.name,
29217 cls : 'roo-document-manager-thumb',
29218 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29224 this.xhr.open(this.method, this.url, true);
29227 "Accept": "application/json",
29228 "Cache-Control": "no-cache",
29229 "X-Requested-With": "XMLHttpRequest"
29232 for (var headerName in headers) {
29233 var headerValue = headers[headerName];
29235 this.xhr.setRequestHeader(headerName, headerValue);
29241 this.xhr.onload = function()
29243 _this.xhrOnLoad(_this.xhr);
29246 this.xhr.onerror = function()
29248 _this.xhrOnError(_this.xhr);
29251 var formData = new FormData();
29253 formData.append('returnHTML', 'NO');
29256 formData.append('crop', crop);
29259 formData.append(this.paramName, file, file.name);
29266 if(this.fireEvent('prepare', this, formData, options) != false){
29268 if(options.manually){
29272 this.xhr.send(formData);
29276 this.uploadCancel();
29279 uploadCancel : function()
29285 this.delegates = [];
29287 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29294 renderPreview : function(file)
29296 if(typeof(file.target) != 'undefined' && file.target){
29300 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29302 var previewEl = this.managerEl.createChild({
29304 cls : 'roo-document-manager-preview',
29308 tooltip : file[this.toolTipName],
29309 cls : 'roo-document-manager-thumb',
29310 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29315 html : '<i class="fa fa-times-circle"></i>'
29320 var close = previewEl.select('button.close', true).first();
29322 close.on('click', this.onRemove, this, file);
29324 file.target = previewEl;
29326 var image = previewEl.select('img', true).first();
29330 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29332 image.on('click', this.onClick, this, file);
29334 this.fireEvent('previewrendered', this, file);
29340 onPreviewLoad : function(file, image)
29342 if(typeof(file.target) == 'undefined' || !file.target){
29346 var width = image.dom.naturalWidth || image.dom.width;
29347 var height = image.dom.naturalHeight || image.dom.height;
29349 if(!this.previewResize) {
29353 if(width > height){
29354 file.target.addClass('wide');
29358 file.target.addClass('tall');
29363 uploadFromSource : function(file, crop)
29365 this.xhr = new XMLHttpRequest();
29367 this.managerEl.createChild({
29369 cls : 'roo-document-manager-loading',
29373 tooltip : file.name,
29374 cls : 'roo-document-manager-thumb',
29375 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29381 this.xhr.open(this.method, this.url, true);
29384 "Accept": "application/json",
29385 "Cache-Control": "no-cache",
29386 "X-Requested-With": "XMLHttpRequest"
29389 for (var headerName in headers) {
29390 var headerValue = headers[headerName];
29392 this.xhr.setRequestHeader(headerName, headerValue);
29398 this.xhr.onload = function()
29400 _this.xhrOnLoad(_this.xhr);
29403 this.xhr.onerror = function()
29405 _this.xhrOnError(_this.xhr);
29408 var formData = new FormData();
29410 formData.append('returnHTML', 'NO');
29412 formData.append('crop', crop);
29414 if(typeof(file.filename) != 'undefined'){
29415 formData.append('filename', file.filename);
29418 if(typeof(file.mimetype) != 'undefined'){
29419 formData.append('mimetype', file.mimetype);
29424 if(this.fireEvent('prepare', this, formData) != false){
29425 this.xhr.send(formData);
29435 * @class Roo.bootstrap.DocumentViewer
29436 * @extends Roo.bootstrap.Component
29437 * Bootstrap DocumentViewer class
29438 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29439 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29442 * Create a new DocumentViewer
29443 * @param {Object} config The config object
29446 Roo.bootstrap.DocumentViewer = function(config){
29447 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29452 * Fire after initEvent
29453 * @param {Roo.bootstrap.DocumentViewer} this
29459 * @param {Roo.bootstrap.DocumentViewer} this
29464 * Fire after download button
29465 * @param {Roo.bootstrap.DocumentViewer} this
29470 * Fire after trash button
29471 * @param {Roo.bootstrap.DocumentViewer} this
29478 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29480 showDownload : true,
29484 getAutoCreate : function()
29488 cls : 'roo-document-viewer',
29492 cls : 'roo-document-viewer-body',
29496 cls : 'roo-document-viewer-thumb',
29500 cls : 'roo-document-viewer-image'
29508 cls : 'roo-document-viewer-footer',
29511 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29515 cls : 'btn-group roo-document-viewer-download',
29519 cls : 'btn btn-default',
29520 html : '<i class="fa fa-download"></i>'
29526 cls : 'btn-group roo-document-viewer-trash',
29530 cls : 'btn btn-default',
29531 html : '<i class="fa fa-trash"></i>'
29544 initEvents : function()
29546 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29547 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29549 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29550 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29552 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29553 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29555 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29556 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29558 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29559 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29561 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29562 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29564 this.bodyEl.on('click', this.onClick, this);
29565 this.downloadBtn.on('click', this.onDownload, this);
29566 this.trashBtn.on('click', this.onTrash, this);
29568 this.downloadBtn.hide();
29569 this.trashBtn.hide();
29571 if(this.showDownload){
29572 this.downloadBtn.show();
29575 if(this.showTrash){
29576 this.trashBtn.show();
29579 if(!this.showDownload && !this.showTrash) {
29580 this.footerEl.hide();
29585 initial : function()
29587 this.fireEvent('initial', this);
29591 onClick : function(e)
29593 e.preventDefault();
29595 this.fireEvent('click', this);
29598 onDownload : function(e)
29600 e.preventDefault();
29602 this.fireEvent('download', this);
29605 onTrash : function(e)
29607 e.preventDefault();
29609 this.fireEvent('trash', this);
29621 * @class Roo.bootstrap.NavProgressBar
29622 * @extends Roo.bootstrap.Component
29623 * Bootstrap NavProgressBar class
29626 * Create a new nav progress bar
29627 * @param {Object} config The config object
29630 Roo.bootstrap.NavProgressBar = function(config){
29631 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29633 this.bullets = this.bullets || [];
29635 // Roo.bootstrap.NavProgressBar.register(this);
29639 * Fires when the active item changes
29640 * @param {Roo.bootstrap.NavProgressBar} this
29641 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29642 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29649 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29654 getAutoCreate : function()
29656 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29660 cls : 'roo-navigation-bar-group',
29664 cls : 'roo-navigation-top-bar'
29668 cls : 'roo-navigation-bullets-bar',
29672 cls : 'roo-navigation-bar'
29679 cls : 'roo-navigation-bottom-bar'
29689 initEvents: function()
29694 onRender : function(ct, position)
29696 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29698 if(this.bullets.length){
29699 Roo.each(this.bullets, function(b){
29708 addItem : function(cfg)
29710 var item = new Roo.bootstrap.NavProgressItem(cfg);
29712 item.parentId = this.id;
29713 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29716 var top = new Roo.bootstrap.Element({
29718 cls : 'roo-navigation-bar-text'
29721 var bottom = new Roo.bootstrap.Element({
29723 cls : 'roo-navigation-bar-text'
29726 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29727 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29729 var topText = new Roo.bootstrap.Element({
29731 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29734 var bottomText = new Roo.bootstrap.Element({
29736 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29739 topText.onRender(top.el, null);
29740 bottomText.onRender(bottom.el, null);
29743 item.bottomEl = bottom;
29746 this.barItems.push(item);
29751 getActive : function()
29753 var active = false;
29755 Roo.each(this.barItems, function(v){
29757 if (!v.isActive()) {
29769 setActiveItem : function(item)
29773 Roo.each(this.barItems, function(v){
29774 if (v.rid == item.rid) {
29778 if (v.isActive()) {
29779 v.setActive(false);
29784 item.setActive(true);
29786 this.fireEvent('changed', this, item, prev);
29789 getBarItem: function(rid)
29793 Roo.each(this.barItems, function(e) {
29794 if (e.rid != rid) {
29805 indexOfItem : function(item)
29809 Roo.each(this.barItems, function(v, i){
29811 if (v.rid != item.rid) {
29822 setActiveNext : function()
29824 var i = this.indexOfItem(this.getActive());
29826 if (i > this.barItems.length) {
29830 this.setActiveItem(this.barItems[i+1]);
29833 setActivePrev : function()
29835 var i = this.indexOfItem(this.getActive());
29841 this.setActiveItem(this.barItems[i-1]);
29844 format : function()
29846 if(!this.barItems.length){
29850 var width = 100 / this.barItems.length;
29852 Roo.each(this.barItems, function(i){
29853 i.el.setStyle('width', width + '%');
29854 i.topEl.el.setStyle('width', width + '%');
29855 i.bottomEl.el.setStyle('width', width + '%');
29864 * Nav Progress Item
29869 * @class Roo.bootstrap.NavProgressItem
29870 * @extends Roo.bootstrap.Component
29871 * Bootstrap NavProgressItem class
29872 * @cfg {String} rid the reference id
29873 * @cfg {Boolean} active (true|false) Is item active default false
29874 * @cfg {Boolean} disabled (true|false) Is item active default false
29875 * @cfg {String} html
29876 * @cfg {String} position (top|bottom) text position default bottom
29877 * @cfg {String} icon show icon instead of number
29880 * Create a new NavProgressItem
29881 * @param {Object} config The config object
29883 Roo.bootstrap.NavProgressItem = function(config){
29884 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29889 * The raw click event for the entire grid.
29890 * @param {Roo.bootstrap.NavProgressItem} this
29891 * @param {Roo.EventObject} e
29898 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29904 position : 'bottom',
29907 getAutoCreate : function()
29909 var iconCls = 'roo-navigation-bar-item-icon';
29911 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29915 cls: 'roo-navigation-bar-item',
29925 cfg.cls += ' active';
29928 cfg.cls += ' disabled';
29934 disable : function()
29936 this.setDisabled(true);
29939 enable : function()
29941 this.setDisabled(false);
29944 initEvents: function()
29946 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29948 this.iconEl.on('click', this.onClick, this);
29951 onClick : function(e)
29953 e.preventDefault();
29959 if(this.fireEvent('click', this, e) === false){
29963 this.parent().setActiveItem(this);
29966 isActive: function ()
29968 return this.active;
29971 setActive : function(state)
29973 if(this.active == state){
29977 this.active = state;
29980 this.el.addClass('active');
29984 this.el.removeClass('active');
29989 setDisabled : function(state)
29991 if(this.disabled == state){
29995 this.disabled = state;
29998 this.el.addClass('disabled');
30002 this.el.removeClass('disabled');
30005 tooltipEl : function()
30007 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30020 * @class Roo.bootstrap.FieldLabel
30021 * @extends Roo.bootstrap.Component
30022 * Bootstrap FieldLabel class
30023 * @cfg {String} html contents of the element
30024 * @cfg {String} tag tag of the element default label
30025 * @cfg {String} cls class of the element
30026 * @cfg {String} target label target
30027 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30028 * @cfg {String} invalidClass default "text-warning"
30029 * @cfg {String} validClass default "text-success"
30030 * @cfg {String} iconTooltip default "This field is required"
30031 * @cfg {String} indicatorpos (left|right) default left
30034 * Create a new FieldLabel
30035 * @param {Object} config The config object
30038 Roo.bootstrap.FieldLabel = function(config){
30039 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30044 * Fires after the field has been marked as invalid.
30045 * @param {Roo.form.FieldLabel} this
30046 * @param {String} msg The validation message
30051 * Fires after the field has been validated with no errors.
30052 * @param {Roo.form.FieldLabel} this
30058 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30065 invalidClass : 'has-warning',
30066 validClass : 'has-success',
30067 iconTooltip : 'This field is required',
30068 indicatorpos : 'left',
30070 getAutoCreate : function(){
30073 if (!this.allowBlank) {
30079 cls : 'roo-bootstrap-field-label ' + this.cls,
30084 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30085 tooltip : this.iconTooltip
30094 if(this.indicatorpos == 'right'){
30097 cls : 'roo-bootstrap-field-label ' + this.cls,
30106 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30107 tooltip : this.iconTooltip
30116 initEvents: function()
30118 Roo.bootstrap.Element.superclass.initEvents.call(this);
30120 this.indicator = this.indicatorEl();
30122 if(this.indicator){
30123 this.indicator.removeClass('visible');
30124 this.indicator.addClass('invisible');
30127 Roo.bootstrap.FieldLabel.register(this);
30130 indicatorEl : function()
30132 var indicator = this.el.select('i.roo-required-indicator',true).first();
30143 * Mark this field as valid
30145 markValid : function()
30147 if(this.indicator){
30148 this.indicator.removeClass('visible');
30149 this.indicator.addClass('invisible');
30152 this.el.removeClass(this.invalidClass);
30154 this.el.addClass(this.validClass);
30156 this.fireEvent('valid', this);
30160 * Mark this field as invalid
30161 * @param {String} msg The validation message
30163 markInvalid : function(msg)
30165 if(this.indicator){
30166 this.indicator.removeClass('invisible');
30167 this.indicator.addClass('visible');
30170 this.el.removeClass(this.validClass);
30172 this.el.addClass(this.invalidClass);
30174 this.fireEvent('invalid', this, msg);
30180 Roo.apply(Roo.bootstrap.FieldLabel, {
30185 * register a FieldLabel Group
30186 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30188 register : function(label)
30190 if(this.groups.hasOwnProperty(label.target)){
30194 this.groups[label.target] = label;
30198 * fetch a FieldLabel Group based on the target
30199 * @param {string} target
30200 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30202 get: function(target) {
30203 if (typeof(this.groups[target]) == 'undefined') {
30207 return this.groups[target] ;
30216 * page DateSplitField.
30222 * @class Roo.bootstrap.DateSplitField
30223 * @extends Roo.bootstrap.Component
30224 * Bootstrap DateSplitField class
30225 * @cfg {string} fieldLabel - the label associated
30226 * @cfg {Number} labelWidth set the width of label (0-12)
30227 * @cfg {String} labelAlign (top|left)
30228 * @cfg {Boolean} dayAllowBlank (true|false) default false
30229 * @cfg {Boolean} monthAllowBlank (true|false) default false
30230 * @cfg {Boolean} yearAllowBlank (true|false) default false
30231 * @cfg {string} dayPlaceholder
30232 * @cfg {string} monthPlaceholder
30233 * @cfg {string} yearPlaceholder
30234 * @cfg {string} dayFormat default 'd'
30235 * @cfg {string} monthFormat default 'm'
30236 * @cfg {string} yearFormat default 'Y'
30237 * @cfg {Number} labellg set the width of label (1-12)
30238 * @cfg {Number} labelmd set the width of label (1-12)
30239 * @cfg {Number} labelsm set the width of label (1-12)
30240 * @cfg {Number} labelxs set the width of label (1-12)
30244 * Create a new DateSplitField
30245 * @param {Object} config The config object
30248 Roo.bootstrap.DateSplitField = function(config){
30249 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30255 * getting the data of years
30256 * @param {Roo.bootstrap.DateSplitField} this
30257 * @param {Object} years
30262 * getting the data of days
30263 * @param {Roo.bootstrap.DateSplitField} this
30264 * @param {Object} days
30269 * Fires after the field has been marked as invalid.
30270 * @param {Roo.form.Field} this
30271 * @param {String} msg The validation message
30276 * Fires after the field has been validated with no errors.
30277 * @param {Roo.form.Field} this
30283 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30286 labelAlign : 'top',
30288 dayAllowBlank : false,
30289 monthAllowBlank : false,
30290 yearAllowBlank : false,
30291 dayPlaceholder : '',
30292 monthPlaceholder : '',
30293 yearPlaceholder : '',
30297 isFormField : true,
30303 getAutoCreate : function()
30307 cls : 'row roo-date-split-field-group',
30312 cls : 'form-hidden-field roo-date-split-field-group-value',
30318 var labelCls = 'col-md-12';
30319 var contentCls = 'col-md-4';
30321 if(this.fieldLabel){
30325 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30329 html : this.fieldLabel
30334 if(this.labelAlign == 'left'){
30336 if(this.labelWidth > 12){
30337 label.style = "width: " + this.labelWidth + 'px';
30340 if(this.labelWidth < 13 && this.labelmd == 0){
30341 this.labelmd = this.labelWidth;
30344 if(this.labellg > 0){
30345 labelCls = ' col-lg-' + this.labellg;
30346 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30349 if(this.labelmd > 0){
30350 labelCls = ' col-md-' + this.labelmd;
30351 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30354 if(this.labelsm > 0){
30355 labelCls = ' col-sm-' + this.labelsm;
30356 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30359 if(this.labelxs > 0){
30360 labelCls = ' col-xs-' + this.labelxs;
30361 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30365 label.cls += ' ' + labelCls;
30367 cfg.cn.push(label);
30370 Roo.each(['day', 'month', 'year'], function(t){
30373 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30380 inputEl: function ()
30382 return this.el.select('.roo-date-split-field-group-value', true).first();
30385 onRender : function(ct, position)
30389 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30391 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30393 this.dayField = new Roo.bootstrap.ComboBox({
30394 allowBlank : this.dayAllowBlank,
30395 alwaysQuery : true,
30396 displayField : 'value',
30399 forceSelection : true,
30401 placeholder : this.dayPlaceholder,
30402 selectOnFocus : true,
30403 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30404 triggerAction : 'all',
30406 valueField : 'value',
30407 store : new Roo.data.SimpleStore({
30408 data : (function() {
30410 _this.fireEvent('days', _this, days);
30413 fields : [ 'value' ]
30416 select : function (_self, record, index)
30418 _this.setValue(_this.getValue());
30423 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30425 this.monthField = new Roo.bootstrap.MonthField({
30426 after : '<i class=\"fa fa-calendar\"></i>',
30427 allowBlank : this.monthAllowBlank,
30428 placeholder : this.monthPlaceholder,
30431 render : function (_self)
30433 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30434 e.preventDefault();
30438 select : function (_self, oldvalue, newvalue)
30440 _this.setValue(_this.getValue());
30445 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30447 this.yearField = new Roo.bootstrap.ComboBox({
30448 allowBlank : this.yearAllowBlank,
30449 alwaysQuery : true,
30450 displayField : 'value',
30453 forceSelection : true,
30455 placeholder : this.yearPlaceholder,
30456 selectOnFocus : true,
30457 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30458 triggerAction : 'all',
30460 valueField : 'value',
30461 store : new Roo.data.SimpleStore({
30462 data : (function() {
30464 _this.fireEvent('years', _this, years);
30467 fields : [ 'value' ]
30470 select : function (_self, record, index)
30472 _this.setValue(_this.getValue());
30477 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30480 setValue : function(v, format)
30482 this.inputEl.dom.value = v;
30484 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30486 var d = Date.parseDate(v, f);
30493 this.setDay(d.format(this.dayFormat));
30494 this.setMonth(d.format(this.monthFormat));
30495 this.setYear(d.format(this.yearFormat));
30502 setDay : function(v)
30504 this.dayField.setValue(v);
30505 this.inputEl.dom.value = this.getValue();
30510 setMonth : function(v)
30512 this.monthField.setValue(v, true);
30513 this.inputEl.dom.value = this.getValue();
30518 setYear : function(v)
30520 this.yearField.setValue(v);
30521 this.inputEl.dom.value = this.getValue();
30526 getDay : function()
30528 return this.dayField.getValue();
30531 getMonth : function()
30533 return this.monthField.getValue();
30536 getYear : function()
30538 return this.yearField.getValue();
30541 getValue : function()
30543 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30545 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30555 this.inputEl.dom.value = '';
30560 validate : function()
30562 var d = this.dayField.validate();
30563 var m = this.monthField.validate();
30564 var y = this.yearField.validate();
30569 (!this.dayAllowBlank && !d) ||
30570 (!this.monthAllowBlank && !m) ||
30571 (!this.yearAllowBlank && !y)
30576 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30585 this.markInvalid();
30590 markValid : function()
30593 var label = this.el.select('label', true).first();
30594 var icon = this.el.select('i.fa-star', true).first();
30600 this.fireEvent('valid', this);
30604 * Mark this field as invalid
30605 * @param {String} msg The validation message
30607 markInvalid : function(msg)
30610 var label = this.el.select('label', true).first();
30611 var icon = this.el.select('i.fa-star', true).first();
30613 if(label && !icon){
30614 this.el.select('.roo-date-split-field-label', true).createChild({
30616 cls : 'text-danger fa fa-lg fa-star',
30617 tooltip : 'This field is required',
30618 style : 'margin-right:5px;'
30622 this.fireEvent('invalid', this, msg);
30625 clearInvalid : function()
30627 var label = this.el.select('label', true).first();
30628 var icon = this.el.select('i.fa-star', true).first();
30634 this.fireEvent('valid', this);
30637 getName: function()
30647 * http://masonry.desandro.com
30649 * The idea is to render all the bricks based on vertical width...
30651 * The original code extends 'outlayer' - we might need to use that....
30657 * @class Roo.bootstrap.LayoutMasonry
30658 * @extends Roo.bootstrap.Component
30659 * Bootstrap Layout Masonry class
30662 * Create a new Element
30663 * @param {Object} config The config object
30666 Roo.bootstrap.LayoutMasonry = function(config){
30668 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30672 Roo.bootstrap.LayoutMasonry.register(this);
30678 * Fire after layout the items
30679 * @param {Roo.bootstrap.LayoutMasonry} this
30680 * @param {Roo.EventObject} e
30687 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30690 * @cfg {Boolean} isLayoutInstant = no animation?
30692 isLayoutInstant : false, // needed?
30695 * @cfg {Number} boxWidth width of the columns
30700 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30705 * @cfg {Number} padWidth padding below box..
30710 * @cfg {Number} gutter gutter width..
30715 * @cfg {Number} maxCols maximum number of columns
30721 * @cfg {Boolean} isAutoInitial defalut true
30723 isAutoInitial : true,
30728 * @cfg {Boolean} isHorizontal defalut false
30730 isHorizontal : false,
30732 currentSize : null,
30738 bricks: null, //CompositeElement
30742 _isLayoutInited : false,
30744 // isAlternative : false, // only use for vertical layout...
30747 * @cfg {Number} alternativePadWidth padding below box..
30749 alternativePadWidth : 50,
30751 selectedBrick : [],
30753 getAutoCreate : function(){
30755 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30759 cls: 'blog-masonary-wrapper ' + this.cls,
30761 cls : 'mas-boxes masonary'
30768 getChildContainer: function( )
30770 if (this.boxesEl) {
30771 return this.boxesEl;
30774 this.boxesEl = this.el.select('.mas-boxes').first();
30776 return this.boxesEl;
30780 initEvents : function()
30784 if(this.isAutoInitial){
30785 Roo.log('hook children rendered');
30786 this.on('childrenrendered', function() {
30787 Roo.log('children rendered');
30793 initial : function()
30795 this.selectedBrick = [];
30797 this.currentSize = this.el.getBox(true);
30799 Roo.EventManager.onWindowResize(this.resize, this);
30801 if(!this.isAutoInitial){
30809 //this.layout.defer(500,this);
30813 resize : function()
30815 var cs = this.el.getBox(true);
30818 this.currentSize.width == cs.width &&
30819 this.currentSize.x == cs.x &&
30820 this.currentSize.height == cs.height &&
30821 this.currentSize.y == cs.y
30823 Roo.log("no change in with or X or Y");
30827 this.currentSize = cs;
30833 layout : function()
30835 this._resetLayout();
30837 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30839 this.layoutItems( isInstant );
30841 this._isLayoutInited = true;
30843 this.fireEvent('layout', this);
30847 _resetLayout : function()
30849 if(this.isHorizontal){
30850 this.horizontalMeasureColumns();
30854 this.verticalMeasureColumns();
30858 verticalMeasureColumns : function()
30860 this.getContainerWidth();
30862 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30863 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30867 var boxWidth = this.boxWidth + this.padWidth;
30869 if(this.containerWidth < this.boxWidth){
30870 boxWidth = this.containerWidth
30873 var containerWidth = this.containerWidth;
30875 var cols = Math.floor(containerWidth / boxWidth);
30877 this.cols = Math.max( cols, 1 );
30879 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30881 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30883 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30885 this.colWidth = boxWidth + avail - this.padWidth;
30887 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30888 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30891 horizontalMeasureColumns : function()
30893 this.getContainerWidth();
30895 var boxWidth = this.boxWidth;
30897 if(this.containerWidth < boxWidth){
30898 boxWidth = this.containerWidth;
30901 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30903 this.el.setHeight(boxWidth);
30907 getContainerWidth : function()
30909 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30912 layoutItems : function( isInstant )
30914 Roo.log(this.bricks);
30916 var items = Roo.apply([], this.bricks);
30918 if(this.isHorizontal){
30919 this._horizontalLayoutItems( items , isInstant );
30923 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30924 // this._verticalAlternativeLayoutItems( items , isInstant );
30928 this._verticalLayoutItems( items , isInstant );
30932 _verticalLayoutItems : function ( items , isInstant)
30934 if ( !items || !items.length ) {
30939 ['xs', 'xs', 'xs', 'tall'],
30940 ['xs', 'xs', 'tall'],
30941 ['xs', 'xs', 'sm'],
30942 ['xs', 'xs', 'xs'],
30948 ['sm', 'xs', 'xs'],
30952 ['tall', 'xs', 'xs', 'xs'],
30953 ['tall', 'xs', 'xs'],
30965 Roo.each(items, function(item, k){
30967 switch (item.size) {
30968 // these layouts take up a full box,
30979 boxes.push([item]);
31002 var filterPattern = function(box, length)
31010 var pattern = box.slice(0, length);
31014 Roo.each(pattern, function(i){
31015 format.push(i.size);
31018 Roo.each(standard, function(s){
31020 if(String(s) != String(format)){
31029 if(!match && length == 1){
31034 filterPattern(box, length - 1);
31038 queue.push(pattern);
31040 box = box.slice(length, box.length);
31042 filterPattern(box, 4);
31048 Roo.each(boxes, function(box, k){
31054 if(box.length == 1){
31059 filterPattern(box, 4);
31063 this._processVerticalLayoutQueue( queue, isInstant );
31067 // _verticalAlternativeLayoutItems : function( items , isInstant )
31069 // if ( !items || !items.length ) {
31073 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31077 _horizontalLayoutItems : function ( items , isInstant)
31079 if ( !items || !items.length || items.length < 3) {
31085 var eItems = items.slice(0, 3);
31087 items = items.slice(3, items.length);
31090 ['xs', 'xs', 'xs', 'wide'],
31091 ['xs', 'xs', 'wide'],
31092 ['xs', 'xs', 'sm'],
31093 ['xs', 'xs', 'xs'],
31099 ['sm', 'xs', 'xs'],
31103 ['wide', 'xs', 'xs', 'xs'],
31104 ['wide', 'xs', 'xs'],
31117 Roo.each(items, function(item, k){
31119 switch (item.size) {
31130 boxes.push([item]);
31154 var filterPattern = function(box, length)
31162 var pattern = box.slice(0, length);
31166 Roo.each(pattern, function(i){
31167 format.push(i.size);
31170 Roo.each(standard, function(s){
31172 if(String(s) != String(format)){
31181 if(!match && length == 1){
31186 filterPattern(box, length - 1);
31190 queue.push(pattern);
31192 box = box.slice(length, box.length);
31194 filterPattern(box, 4);
31200 Roo.each(boxes, function(box, k){
31206 if(box.length == 1){
31211 filterPattern(box, 4);
31218 var pos = this.el.getBox(true);
31222 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31224 var hit_end = false;
31226 Roo.each(queue, function(box){
31230 Roo.each(box, function(b){
31232 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31242 Roo.each(box, function(b){
31244 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31247 mx = Math.max(mx, b.x);
31251 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31255 Roo.each(box, function(b){
31257 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31271 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31274 /** Sets position of item in DOM
31275 * @param {Element} item
31276 * @param {Number} x - horizontal position
31277 * @param {Number} y - vertical position
31278 * @param {Boolean} isInstant - disables transitions
31280 _processVerticalLayoutQueue : function( queue, isInstant )
31282 var pos = this.el.getBox(true);
31287 for (var i = 0; i < this.cols; i++){
31291 Roo.each(queue, function(box, k){
31293 var col = k % this.cols;
31295 Roo.each(box, function(b,kk){
31297 b.el.position('absolute');
31299 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31300 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31302 if(b.size == 'md-left' || b.size == 'md-right'){
31303 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31304 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31307 b.el.setWidth(width);
31308 b.el.setHeight(height);
31310 b.el.select('iframe',true).setSize(width,height);
31314 for (var i = 0; i < this.cols; i++){
31316 if(maxY[i] < maxY[col]){
31321 col = Math.min(col, i);
31325 x = pos.x + col * (this.colWidth + this.padWidth);
31329 var positions = [];
31331 switch (box.length){
31333 positions = this.getVerticalOneBoxColPositions(x, y, box);
31336 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31339 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31342 positions = this.getVerticalFourBoxColPositions(x, y, box);
31348 Roo.each(box, function(b,kk){
31350 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31352 var sz = b.el.getSize();
31354 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31362 for (var i = 0; i < this.cols; i++){
31363 mY = Math.max(mY, maxY[i]);
31366 this.el.setHeight(mY - pos.y);
31370 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31372 // var pos = this.el.getBox(true);
31375 // var maxX = pos.right;
31377 // var maxHeight = 0;
31379 // Roo.each(items, function(item, k){
31383 // item.el.position('absolute');
31385 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31387 // item.el.setWidth(width);
31389 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31391 // item.el.setHeight(height);
31394 // item.el.setXY([x, y], isInstant ? false : true);
31396 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31399 // y = y + height + this.alternativePadWidth;
31401 // maxHeight = maxHeight + height + this.alternativePadWidth;
31405 // this.el.setHeight(maxHeight);
31409 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31411 var pos = this.el.getBox(true);
31416 var maxX = pos.right;
31418 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31420 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31422 Roo.each(queue, function(box, k){
31424 Roo.each(box, function(b, kk){
31426 b.el.position('absolute');
31428 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31429 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31431 if(b.size == 'md-left' || b.size == 'md-right'){
31432 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31433 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31436 b.el.setWidth(width);
31437 b.el.setHeight(height);
31445 var positions = [];
31447 switch (box.length){
31449 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31452 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31455 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31458 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31464 Roo.each(box, function(b,kk){
31466 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31468 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31476 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31478 Roo.each(eItems, function(b,k){
31480 b.size = (k == 0) ? 'sm' : 'xs';
31481 b.x = (k == 0) ? 2 : 1;
31482 b.y = (k == 0) ? 2 : 1;
31484 b.el.position('absolute');
31486 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31488 b.el.setWidth(width);
31490 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31492 b.el.setHeight(height);
31496 var positions = [];
31499 x : maxX - this.unitWidth * 2 - this.gutter,
31504 x : maxX - this.unitWidth,
31505 y : minY + (this.unitWidth + this.gutter) * 2
31509 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31513 Roo.each(eItems, function(b,k){
31515 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31521 getVerticalOneBoxColPositions : function(x, y, box)
31525 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31527 if(box[0].size == 'md-left'){
31531 if(box[0].size == 'md-right'){
31536 x : x + (this.unitWidth + this.gutter) * rand,
31543 getVerticalTwoBoxColPositions : function(x, y, box)
31547 if(box[0].size == 'xs'){
31551 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31555 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31569 x : x + (this.unitWidth + this.gutter) * 2,
31570 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31577 getVerticalThreeBoxColPositions : function(x, y, box)
31581 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31589 x : x + (this.unitWidth + this.gutter) * 1,
31594 x : x + (this.unitWidth + this.gutter) * 2,
31602 if(box[0].size == 'xs' && box[1].size == 'xs'){
31611 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31615 x : x + (this.unitWidth + this.gutter) * 1,
31629 x : x + (this.unitWidth + this.gutter) * 2,
31634 x : x + (this.unitWidth + this.gutter) * 2,
31635 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31642 getVerticalFourBoxColPositions : function(x, y, box)
31646 if(box[0].size == 'xs'){
31655 y : y + (this.unitHeight + this.gutter) * 1
31660 y : y + (this.unitHeight + this.gutter) * 2
31664 x : x + (this.unitWidth + this.gutter) * 1,
31678 x : x + (this.unitWidth + this.gutter) * 2,
31683 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31684 y : y + (this.unitHeight + this.gutter) * 1
31688 x : x + (this.unitWidth + this.gutter) * 2,
31689 y : y + (this.unitWidth + this.gutter) * 2
31696 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31700 if(box[0].size == 'md-left'){
31702 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31709 if(box[0].size == 'md-right'){
31711 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31712 y : minY + (this.unitWidth + this.gutter) * 1
31718 var rand = Math.floor(Math.random() * (4 - box[0].y));
31721 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31722 y : minY + (this.unitWidth + this.gutter) * rand
31729 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31733 if(box[0].size == 'xs'){
31736 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31741 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31742 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31750 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31755 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31756 y : minY + (this.unitWidth + this.gutter) * 2
31763 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31767 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31770 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31775 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31776 y : minY + (this.unitWidth + this.gutter) * 1
31780 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31781 y : minY + (this.unitWidth + this.gutter) * 2
31788 if(box[0].size == 'xs' && box[1].size == 'xs'){
31791 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31796 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31801 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31802 y : minY + (this.unitWidth + this.gutter) * 1
31810 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31815 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31816 y : minY + (this.unitWidth + this.gutter) * 2
31820 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31821 y : minY + (this.unitWidth + this.gutter) * 2
31828 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31832 if(box[0].size == 'xs'){
31835 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31840 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31845 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),
31850 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31851 y : minY + (this.unitWidth + this.gutter) * 1
31859 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31864 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31865 y : minY + (this.unitWidth + this.gutter) * 2
31869 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31870 y : minY + (this.unitWidth + this.gutter) * 2
31874 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),
31875 y : minY + (this.unitWidth + this.gutter) * 2
31883 * remove a Masonry Brick
31884 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31886 removeBrick : function(brick_id)
31892 for (var i = 0; i<this.bricks.length; i++) {
31893 if (this.bricks[i].id == brick_id) {
31894 this.bricks.splice(i,1);
31895 this.el.dom.removeChild(Roo.get(brick_id).dom);
31902 * adds a Masonry Brick
31903 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31905 addBrick : function(cfg)
31907 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31908 //this.register(cn);
31909 cn.parentId = this.id;
31910 cn.onRender(this.el, null);
31915 * register a Masonry Brick
31916 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31919 register : function(brick)
31921 this.bricks.push(brick);
31922 brick.masonryId = this.id;
31926 * clear all the Masonry Brick
31928 clearAll : function()
31931 //this.getChildContainer().dom.innerHTML = "";
31932 this.el.dom.innerHTML = '';
31935 getSelected : function()
31937 if (!this.selectedBrick) {
31941 return this.selectedBrick;
31945 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31949 * register a Masonry Layout
31950 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31953 register : function(layout)
31955 this.groups[layout.id] = layout;
31958 * fetch a Masonry Layout based on the masonry layout ID
31959 * @param {string} the masonry layout to add
31960 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31963 get: function(layout_id) {
31964 if (typeof(this.groups[layout_id]) == 'undefined') {
31967 return this.groups[layout_id] ;
31979 * http://masonry.desandro.com
31981 * The idea is to render all the bricks based on vertical width...
31983 * The original code extends 'outlayer' - we might need to use that....
31989 * @class Roo.bootstrap.LayoutMasonryAuto
31990 * @extends Roo.bootstrap.Component
31991 * Bootstrap Layout Masonry class
31994 * Create a new Element
31995 * @param {Object} config The config object
31998 Roo.bootstrap.LayoutMasonryAuto = function(config){
31999 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32002 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32005 * @cfg {Boolean} isFitWidth - resize the width..
32007 isFitWidth : false, // options..
32009 * @cfg {Boolean} isOriginLeft = left align?
32011 isOriginLeft : true,
32013 * @cfg {Boolean} isOriginTop = top align?
32015 isOriginTop : false,
32017 * @cfg {Boolean} isLayoutInstant = no animation?
32019 isLayoutInstant : false, // needed?
32021 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32023 isResizingContainer : true,
32025 * @cfg {Number} columnWidth width of the columns
32031 * @cfg {Number} maxCols maximum number of columns
32036 * @cfg {Number} padHeight padding below box..
32042 * @cfg {Boolean} isAutoInitial defalut true
32045 isAutoInitial : true,
32051 initialColumnWidth : 0,
32052 currentSize : null,
32054 colYs : null, // array.
32061 bricks: null, //CompositeElement
32062 cols : 0, // array?
32063 // element : null, // wrapped now this.el
32064 _isLayoutInited : null,
32067 getAutoCreate : function(){
32071 cls: 'blog-masonary-wrapper ' + this.cls,
32073 cls : 'mas-boxes masonary'
32080 getChildContainer: function( )
32082 if (this.boxesEl) {
32083 return this.boxesEl;
32086 this.boxesEl = this.el.select('.mas-boxes').first();
32088 return this.boxesEl;
32092 initEvents : function()
32096 if(this.isAutoInitial){
32097 Roo.log('hook children rendered');
32098 this.on('childrenrendered', function() {
32099 Roo.log('children rendered');
32106 initial : function()
32108 this.reloadItems();
32110 this.currentSize = this.el.getBox(true);
32112 /// was window resize... - let's see if this works..
32113 Roo.EventManager.onWindowResize(this.resize, this);
32115 if(!this.isAutoInitial){
32120 this.layout.defer(500,this);
32123 reloadItems: function()
32125 this.bricks = this.el.select('.masonry-brick', true);
32127 this.bricks.each(function(b) {
32128 //Roo.log(b.getSize());
32129 if (!b.attr('originalwidth')) {
32130 b.attr('originalwidth', b.getSize().width);
32135 Roo.log(this.bricks.elements.length);
32138 resize : function()
32141 var cs = this.el.getBox(true);
32143 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32144 Roo.log("no change in with or X");
32147 this.currentSize = cs;
32151 layout : function()
32154 this._resetLayout();
32155 //this._manageStamps();
32157 // don't animate first layout
32158 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32159 this.layoutItems( isInstant );
32161 // flag for initalized
32162 this._isLayoutInited = true;
32165 layoutItems : function( isInstant )
32167 //var items = this._getItemsForLayout( this.items );
32168 // original code supports filtering layout items.. we just ignore it..
32170 this._layoutItems( this.bricks , isInstant );
32172 this._postLayout();
32174 _layoutItems : function ( items , isInstant)
32176 //this.fireEvent( 'layout', this, items );
32179 if ( !items || !items.elements.length ) {
32180 // no items, emit event with empty array
32185 items.each(function(item) {
32186 Roo.log("layout item");
32188 // get x/y object from method
32189 var position = this._getItemLayoutPosition( item );
32191 position.item = item;
32192 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32193 queue.push( position );
32196 this._processLayoutQueue( queue );
32198 /** Sets position of item in DOM
32199 * @param {Element} item
32200 * @param {Number} x - horizontal position
32201 * @param {Number} y - vertical position
32202 * @param {Boolean} isInstant - disables transitions
32204 _processLayoutQueue : function( queue )
32206 for ( var i=0, len = queue.length; i < len; i++ ) {
32207 var obj = queue[i];
32208 obj.item.position('absolute');
32209 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32215 * Any logic you want to do after each layout,
32216 * i.e. size the container
32218 _postLayout : function()
32220 this.resizeContainer();
32223 resizeContainer : function()
32225 if ( !this.isResizingContainer ) {
32228 var size = this._getContainerSize();
32230 this.el.setSize(size.width,size.height);
32231 this.boxesEl.setSize(size.width,size.height);
32237 _resetLayout : function()
32239 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32240 this.colWidth = this.el.getWidth();
32241 //this.gutter = this.el.getWidth();
32243 this.measureColumns();
32249 this.colYs.push( 0 );
32255 measureColumns : function()
32257 this.getContainerWidth();
32258 // if columnWidth is 0, default to outerWidth of first item
32259 if ( !this.columnWidth ) {
32260 var firstItem = this.bricks.first();
32261 Roo.log(firstItem);
32262 this.columnWidth = this.containerWidth;
32263 if (firstItem && firstItem.attr('originalwidth') ) {
32264 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32266 // columnWidth fall back to item of first element
32267 Roo.log("set column width?");
32268 this.initialColumnWidth = this.columnWidth ;
32270 // if first elem has no width, default to size of container
32275 if (this.initialColumnWidth) {
32276 this.columnWidth = this.initialColumnWidth;
32281 // column width is fixed at the top - however if container width get's smaller we should
32284 // this bit calcs how man columns..
32286 var columnWidth = this.columnWidth += this.gutter;
32288 // calculate columns
32289 var containerWidth = this.containerWidth + this.gutter;
32291 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32292 // fix rounding errors, typically with gutters
32293 var excess = columnWidth - containerWidth % columnWidth;
32296 // if overshoot is less than a pixel, round up, otherwise floor it
32297 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32298 cols = Math[ mathMethod ]( cols );
32299 this.cols = Math.max( cols, 1 );
32300 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32302 // padding positioning..
32303 var totalColWidth = this.cols * this.columnWidth;
32304 var padavail = this.containerWidth - totalColWidth;
32305 // so for 2 columns - we need 3 'pads'
32307 var padNeeded = (1+this.cols) * this.padWidth;
32309 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32311 this.columnWidth += padExtra
32312 //this.padWidth = Math.floor(padavail / ( this.cols));
32314 // adjust colum width so that padding is fixed??
32316 // we have 3 columns ... total = width * 3
32317 // we have X left over... that should be used by
32319 //if (this.expandC) {
32327 getContainerWidth : function()
32329 /* // container is parent if fit width
32330 var container = this.isFitWidth ? this.element.parentNode : this.element;
32331 // check that this.size and size are there
32332 // IE8 triggers resize on body size change, so they might not be
32334 var size = getSize( container ); //FIXME
32335 this.containerWidth = size && size.innerWidth; //FIXME
32338 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32342 _getItemLayoutPosition : function( item ) // what is item?
32344 // we resize the item to our columnWidth..
32346 item.setWidth(this.columnWidth);
32347 item.autoBoxAdjust = false;
32349 var sz = item.getSize();
32351 // how many columns does this brick span
32352 var remainder = this.containerWidth % this.columnWidth;
32354 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32355 // round if off by 1 pixel, otherwise use ceil
32356 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32357 colSpan = Math.min( colSpan, this.cols );
32359 // normally this should be '1' as we dont' currently allow multi width columns..
32361 var colGroup = this._getColGroup( colSpan );
32362 // get the minimum Y value from the columns
32363 var minimumY = Math.min.apply( Math, colGroup );
32364 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32366 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32368 // position the brick
32370 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32371 y: this.currentSize.y + minimumY + this.padHeight
32375 // apply setHeight to necessary columns
32376 var setHeight = minimumY + sz.height + this.padHeight;
32377 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32379 var setSpan = this.cols + 1 - colGroup.length;
32380 for ( var i = 0; i < setSpan; i++ ) {
32381 this.colYs[ shortColIndex + i ] = setHeight ;
32388 * @param {Number} colSpan - number of columns the element spans
32389 * @returns {Array} colGroup
32391 _getColGroup : function( colSpan )
32393 if ( colSpan < 2 ) {
32394 // if brick spans only one column, use all the column Ys
32399 // how many different places could this brick fit horizontally
32400 var groupCount = this.cols + 1 - colSpan;
32401 // for each group potential horizontal position
32402 for ( var i = 0; i < groupCount; i++ ) {
32403 // make an array of colY values for that one group
32404 var groupColYs = this.colYs.slice( i, i + colSpan );
32405 // and get the max value of the array
32406 colGroup[i] = Math.max.apply( Math, groupColYs );
32411 _manageStamp : function( stamp )
32413 var stampSize = stamp.getSize();
32414 var offset = stamp.getBox();
32415 // get the columns that this stamp affects
32416 var firstX = this.isOriginLeft ? offset.x : offset.right;
32417 var lastX = firstX + stampSize.width;
32418 var firstCol = Math.floor( firstX / this.columnWidth );
32419 firstCol = Math.max( 0, firstCol );
32421 var lastCol = Math.floor( lastX / this.columnWidth );
32422 // lastCol should not go over if multiple of columnWidth #425
32423 lastCol -= lastX % this.columnWidth ? 0 : 1;
32424 lastCol = Math.min( this.cols - 1, lastCol );
32426 // set colYs to bottom of the stamp
32427 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32430 for ( var i = firstCol; i <= lastCol; i++ ) {
32431 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32436 _getContainerSize : function()
32438 this.maxY = Math.max.apply( Math, this.colYs );
32443 if ( this.isFitWidth ) {
32444 size.width = this._getContainerFitWidth();
32450 _getContainerFitWidth : function()
32452 var unusedCols = 0;
32453 // count unused columns
32456 if ( this.colYs[i] !== 0 ) {
32461 // fit container to columns that have been used
32462 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32465 needsResizeLayout : function()
32467 var previousWidth = this.containerWidth;
32468 this.getContainerWidth();
32469 return previousWidth !== this.containerWidth;
32484 * @class Roo.bootstrap.MasonryBrick
32485 * @extends Roo.bootstrap.Component
32486 * Bootstrap MasonryBrick class
32489 * Create a new MasonryBrick
32490 * @param {Object} config The config object
32493 Roo.bootstrap.MasonryBrick = function(config){
32495 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32497 Roo.bootstrap.MasonryBrick.register(this);
32503 * When a MasonryBrick is clcik
32504 * @param {Roo.bootstrap.MasonryBrick} this
32505 * @param {Roo.EventObject} e
32511 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32514 * @cfg {String} title
32518 * @cfg {String} html
32522 * @cfg {String} bgimage
32526 * @cfg {String} videourl
32530 * @cfg {String} cls
32534 * @cfg {String} href
32538 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32543 * @cfg {String} placetitle (center|bottom)
32548 * @cfg {Boolean} isFitContainer defalut true
32550 isFitContainer : true,
32553 * @cfg {Boolean} preventDefault defalut false
32555 preventDefault : false,
32558 * @cfg {Boolean} inverse defalut false
32560 maskInverse : false,
32562 getAutoCreate : function()
32564 if(!this.isFitContainer){
32565 return this.getSplitAutoCreate();
32568 var cls = 'masonry-brick masonry-brick-full';
32570 if(this.href.length){
32571 cls += ' masonry-brick-link';
32574 if(this.bgimage.length){
32575 cls += ' masonry-brick-image';
32578 if(this.maskInverse){
32579 cls += ' mask-inverse';
32582 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32583 cls += ' enable-mask';
32587 cls += ' masonry-' + this.size + '-brick';
32590 if(this.placetitle.length){
32592 switch (this.placetitle) {
32594 cls += ' masonry-center-title';
32597 cls += ' masonry-bottom-title';
32604 if(!this.html.length && !this.bgimage.length){
32605 cls += ' masonry-center-title';
32608 if(!this.html.length && this.bgimage.length){
32609 cls += ' masonry-bottom-title';
32614 cls += ' ' + this.cls;
32618 tag: (this.href.length) ? 'a' : 'div',
32623 cls: 'masonry-brick-mask'
32627 cls: 'masonry-brick-paragraph',
32633 if(this.href.length){
32634 cfg.href = this.href;
32637 var cn = cfg.cn[1].cn;
32639 if(this.title.length){
32642 cls: 'masonry-brick-title',
32647 if(this.html.length){
32650 cls: 'masonry-brick-text',
32655 if (!this.title.length && !this.html.length) {
32656 cfg.cn[1].cls += ' hide';
32659 if(this.bgimage.length){
32662 cls: 'masonry-brick-image-view',
32667 if(this.videourl.length){
32668 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32669 // youtube support only?
32672 cls: 'masonry-brick-image-view',
32675 allowfullscreen : true
32683 getSplitAutoCreate : function()
32685 var cls = 'masonry-brick masonry-brick-split';
32687 if(this.href.length){
32688 cls += ' masonry-brick-link';
32691 if(this.bgimage.length){
32692 cls += ' masonry-brick-image';
32696 cls += ' masonry-' + this.size + '-brick';
32699 switch (this.placetitle) {
32701 cls += ' masonry-center-title';
32704 cls += ' masonry-bottom-title';
32707 if(!this.bgimage.length){
32708 cls += ' masonry-center-title';
32711 if(this.bgimage.length){
32712 cls += ' masonry-bottom-title';
32718 cls += ' ' + this.cls;
32722 tag: (this.href.length) ? 'a' : 'div',
32727 cls: 'masonry-brick-split-head',
32731 cls: 'masonry-brick-paragraph',
32738 cls: 'masonry-brick-split-body',
32744 if(this.href.length){
32745 cfg.href = this.href;
32748 if(this.title.length){
32749 cfg.cn[0].cn[0].cn.push({
32751 cls: 'masonry-brick-title',
32756 if(this.html.length){
32757 cfg.cn[1].cn.push({
32759 cls: 'masonry-brick-text',
32764 if(this.bgimage.length){
32765 cfg.cn[0].cn.push({
32767 cls: 'masonry-brick-image-view',
32772 if(this.videourl.length){
32773 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32774 // youtube support only?
32775 cfg.cn[0].cn.cn.push({
32777 cls: 'masonry-brick-image-view',
32780 allowfullscreen : true
32787 initEvents: function()
32789 switch (this.size) {
32822 this.el.on('touchstart', this.onTouchStart, this);
32823 this.el.on('touchmove', this.onTouchMove, this);
32824 this.el.on('touchend', this.onTouchEnd, this);
32825 this.el.on('contextmenu', this.onContextMenu, this);
32827 this.el.on('mouseenter' ,this.enter, this);
32828 this.el.on('mouseleave', this.leave, this);
32829 this.el.on('click', this.onClick, this);
32832 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32833 this.parent().bricks.push(this);
32838 onClick: function(e, el)
32840 var time = this.endTimer - this.startTimer;
32841 // Roo.log(e.preventDefault());
32844 e.preventDefault();
32849 if(!this.preventDefault){
32853 e.preventDefault();
32855 if (this.activeClass != '') {
32856 this.selectBrick();
32859 this.fireEvent('click', this, e);
32862 enter: function(e, el)
32864 e.preventDefault();
32866 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32870 if(this.bgimage.length && this.html.length){
32871 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32875 leave: function(e, el)
32877 e.preventDefault();
32879 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32883 if(this.bgimage.length && this.html.length){
32884 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32888 onTouchStart: function(e, el)
32890 // e.preventDefault();
32892 this.touchmoved = false;
32894 if(!this.isFitContainer){
32898 if(!this.bgimage.length || !this.html.length){
32902 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32904 this.timer = new Date().getTime();
32908 onTouchMove: function(e, el)
32910 this.touchmoved = true;
32913 onContextMenu : function(e,el)
32915 e.preventDefault();
32916 e.stopPropagation();
32920 onTouchEnd: function(e, el)
32922 // e.preventDefault();
32924 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32931 if(!this.bgimage.length || !this.html.length){
32933 if(this.href.length){
32934 window.location.href = this.href;
32940 if(!this.isFitContainer){
32944 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32946 window.location.href = this.href;
32949 //selection on single brick only
32950 selectBrick : function() {
32952 if (!this.parentId) {
32956 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32957 var index = m.selectedBrick.indexOf(this.id);
32960 m.selectedBrick.splice(index,1);
32961 this.el.removeClass(this.activeClass);
32965 for(var i = 0; i < m.selectedBrick.length; i++) {
32966 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32967 b.el.removeClass(b.activeClass);
32970 m.selectedBrick = [];
32972 m.selectedBrick.push(this.id);
32973 this.el.addClass(this.activeClass);
32977 isSelected : function(){
32978 return this.el.hasClass(this.activeClass);
32983 Roo.apply(Roo.bootstrap.MasonryBrick, {
32986 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32988 * register a Masonry Brick
32989 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32992 register : function(brick)
32994 //this.groups[brick.id] = brick;
32995 this.groups.add(brick.id, brick);
32998 * fetch a masonry brick based on the masonry brick ID
32999 * @param {string} the masonry brick to add
33000 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33003 get: function(brick_id)
33005 // if (typeof(this.groups[brick_id]) == 'undefined') {
33008 // return this.groups[brick_id] ;
33010 if(this.groups.key(brick_id)) {
33011 return this.groups.key(brick_id);
33029 * @class Roo.bootstrap.Brick
33030 * @extends Roo.bootstrap.Component
33031 * Bootstrap Brick class
33034 * Create a new Brick
33035 * @param {Object} config The config object
33038 Roo.bootstrap.Brick = function(config){
33039 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33045 * When a Brick is click
33046 * @param {Roo.bootstrap.Brick} this
33047 * @param {Roo.EventObject} e
33053 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33056 * @cfg {String} title
33060 * @cfg {String} html
33064 * @cfg {String} bgimage
33068 * @cfg {String} cls
33072 * @cfg {String} href
33076 * @cfg {String} video
33080 * @cfg {Boolean} square
33084 getAutoCreate : function()
33086 var cls = 'roo-brick';
33088 if(this.href.length){
33089 cls += ' roo-brick-link';
33092 if(this.bgimage.length){
33093 cls += ' roo-brick-image';
33096 if(!this.html.length && !this.bgimage.length){
33097 cls += ' roo-brick-center-title';
33100 if(!this.html.length && this.bgimage.length){
33101 cls += ' roo-brick-bottom-title';
33105 cls += ' ' + this.cls;
33109 tag: (this.href.length) ? 'a' : 'div',
33114 cls: 'roo-brick-paragraph',
33120 if(this.href.length){
33121 cfg.href = this.href;
33124 var cn = cfg.cn[0].cn;
33126 if(this.title.length){
33129 cls: 'roo-brick-title',
33134 if(this.html.length){
33137 cls: 'roo-brick-text',
33144 if(this.bgimage.length){
33147 cls: 'roo-brick-image-view',
33155 initEvents: function()
33157 if(this.title.length || this.html.length){
33158 this.el.on('mouseenter' ,this.enter, this);
33159 this.el.on('mouseleave', this.leave, this);
33162 Roo.EventManager.onWindowResize(this.resize, this);
33164 if(this.bgimage.length){
33165 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33166 this.imageEl.on('load', this.onImageLoad, this);
33173 onImageLoad : function()
33178 resize : function()
33180 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33182 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33184 if(this.bgimage.length){
33185 var image = this.el.select('.roo-brick-image-view', true).first();
33187 image.setWidth(paragraph.getWidth());
33190 image.setHeight(paragraph.getWidth());
33193 this.el.setHeight(image.getHeight());
33194 paragraph.setHeight(image.getHeight());
33200 enter: function(e, el)
33202 e.preventDefault();
33204 if(this.bgimage.length){
33205 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33206 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33210 leave: function(e, el)
33212 e.preventDefault();
33214 if(this.bgimage.length){
33215 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33216 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33231 * @class Roo.bootstrap.NumberField
33232 * @extends Roo.bootstrap.Input
33233 * Bootstrap NumberField class
33239 * Create a new NumberField
33240 * @param {Object} config The config object
33243 Roo.bootstrap.NumberField = function(config){
33244 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33247 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33250 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33252 allowDecimals : true,
33254 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33256 decimalSeparator : ".",
33258 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33260 decimalPrecision : 2,
33262 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33264 allowNegative : true,
33267 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33271 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33273 minValue : Number.NEGATIVE_INFINITY,
33275 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33277 maxValue : Number.MAX_VALUE,
33279 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33281 minText : "The minimum value for this field is {0}",
33283 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33285 maxText : "The maximum value for this field is {0}",
33287 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33288 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33290 nanText : "{0} is not a valid number",
33292 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33294 thousandsDelimiter : false,
33296 * @cfg {String} valueAlign alignment of value
33298 valueAlign : "left",
33300 getAutoCreate : function()
33302 var hiddenInput = {
33306 cls: 'hidden-number-input'
33310 hiddenInput.name = this.name;
33315 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33317 this.name = hiddenInput.name;
33319 if(cfg.cn.length > 0) {
33320 cfg.cn.push(hiddenInput);
33327 initEvents : function()
33329 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33331 var allowed = "0123456789";
33333 if(this.allowDecimals){
33334 allowed += this.decimalSeparator;
33337 if(this.allowNegative){
33341 if(this.thousandsDelimiter) {
33345 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33347 var keyPress = function(e){
33349 var k = e.getKey();
33351 var c = e.getCharCode();
33354 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33355 allowed.indexOf(String.fromCharCode(c)) === -1
33361 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33365 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33370 this.el.on("keypress", keyPress, this);
33373 validateValue : function(value)
33376 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33380 var num = this.parseValue(value);
33383 this.markInvalid(String.format(this.nanText, value));
33387 if(num < this.minValue){
33388 this.markInvalid(String.format(this.minText, this.minValue));
33392 if(num > this.maxValue){
33393 this.markInvalid(String.format(this.maxText, this.maxValue));
33400 getValue : function()
33402 var v = this.hiddenEl().getValue();
33404 return this.fixPrecision(this.parseValue(v));
33407 parseValue : function(value)
33409 if(this.thousandsDelimiter) {
33411 r = new RegExp(",", "g");
33412 value = value.replace(r, "");
33415 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33416 return isNaN(value) ? '' : value;
33419 fixPrecision : function(value)
33421 if(this.thousandsDelimiter) {
33423 r = new RegExp(",", "g");
33424 value = value.replace(r, "");
33427 var nan = isNaN(value);
33429 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33430 return nan ? '' : value;
33432 return parseFloat(value).toFixed(this.decimalPrecision);
33435 setValue : function(v)
33437 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33443 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33445 this.inputEl().dom.value = (v == '') ? '' :
33446 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33448 if(!this.allowZero && v === '0') {
33449 this.hiddenEl().dom.value = '';
33450 this.inputEl().dom.value = '';
33457 decimalPrecisionFcn : function(v)
33459 return Math.floor(v);
33462 beforeBlur : function()
33464 var v = this.parseValue(this.getRawValue());
33466 if(v || v === 0 || v === ''){
33471 hiddenEl : function()
33473 return this.el.select('input.hidden-number-input',true).first();
33485 * @class Roo.bootstrap.DocumentSlider
33486 * @extends Roo.bootstrap.Component
33487 * Bootstrap DocumentSlider class
33490 * Create a new DocumentViewer
33491 * @param {Object} config The config object
33494 Roo.bootstrap.DocumentSlider = function(config){
33495 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33502 * Fire after initEvent
33503 * @param {Roo.bootstrap.DocumentSlider} this
33508 * Fire after update
33509 * @param {Roo.bootstrap.DocumentSlider} this
33515 * @param {Roo.bootstrap.DocumentSlider} this
33521 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33527 getAutoCreate : function()
33531 cls : 'roo-document-slider',
33535 cls : 'roo-document-slider-header',
33539 cls : 'roo-document-slider-header-title'
33545 cls : 'roo-document-slider-body',
33549 cls : 'roo-document-slider-prev',
33553 cls : 'fa fa-chevron-left'
33559 cls : 'roo-document-slider-thumb',
33563 cls : 'roo-document-slider-image'
33569 cls : 'roo-document-slider-next',
33573 cls : 'fa fa-chevron-right'
33585 initEvents : function()
33587 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33588 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33590 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33591 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33593 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33594 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33596 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33597 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33599 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33600 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33602 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33603 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33605 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33606 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33608 this.thumbEl.on('click', this.onClick, this);
33610 this.prevIndicator.on('click', this.prev, this);
33612 this.nextIndicator.on('click', this.next, this);
33616 initial : function()
33618 if(this.files.length){
33619 this.indicator = 1;
33623 this.fireEvent('initial', this);
33626 update : function()
33628 this.imageEl.attr('src', this.files[this.indicator - 1]);
33630 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33632 this.prevIndicator.show();
33634 if(this.indicator == 1){
33635 this.prevIndicator.hide();
33638 this.nextIndicator.show();
33640 if(this.indicator == this.files.length){
33641 this.nextIndicator.hide();
33644 this.thumbEl.scrollTo('top');
33646 this.fireEvent('update', this);
33649 onClick : function(e)
33651 e.preventDefault();
33653 this.fireEvent('click', this);
33658 e.preventDefault();
33660 this.indicator = Math.max(1, this.indicator - 1);
33667 e.preventDefault();
33669 this.indicator = Math.min(this.files.length, this.indicator + 1);
33683 * @class Roo.bootstrap.RadioSet
33684 * @extends Roo.bootstrap.Input
33685 * Bootstrap RadioSet class
33686 * @cfg {String} indicatorpos (left|right) default left
33687 * @cfg {Boolean} inline (true|false) inline the element (default true)
33688 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33690 * Create a new RadioSet
33691 * @param {Object} config The config object
33694 Roo.bootstrap.RadioSet = function(config){
33696 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33700 Roo.bootstrap.RadioSet.register(this);
33705 * Fires when the element is checked or unchecked.
33706 * @param {Roo.bootstrap.RadioSet} this This radio
33707 * @param {Roo.bootstrap.Radio} item The checked item
33712 * Fires when the element is click.
33713 * @param {Roo.bootstrap.RadioSet} this This radio set
33714 * @param {Roo.bootstrap.Radio} item The checked item
33715 * @param {Roo.EventObject} e The event object
33722 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33730 indicatorpos : 'left',
33732 getAutoCreate : function()
33736 cls : 'roo-radio-set-label',
33740 html : this.fieldLabel
33745 if(this.indicatorpos == 'left'){
33748 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33749 tooltip : 'This field is required'
33754 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33755 tooltip : 'This field is required'
33761 cls : 'roo-radio-set-items'
33764 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33766 if (align === 'left' && this.fieldLabel.length) {
33769 cls : "roo-radio-set-right",
33775 if(this.labelWidth > 12){
33776 label.style = "width: " + this.labelWidth + 'px';
33779 if(this.labelWidth < 13 && this.labelmd == 0){
33780 this.labelmd = this.labelWidth;
33783 if(this.labellg > 0){
33784 label.cls += ' col-lg-' + this.labellg;
33785 items.cls += ' col-lg-' + (12 - this.labellg);
33788 if(this.labelmd > 0){
33789 label.cls += ' col-md-' + this.labelmd;
33790 items.cls += ' col-md-' + (12 - this.labelmd);
33793 if(this.labelsm > 0){
33794 label.cls += ' col-sm-' + this.labelsm;
33795 items.cls += ' col-sm-' + (12 - this.labelsm);
33798 if(this.labelxs > 0){
33799 label.cls += ' col-xs-' + this.labelxs;
33800 items.cls += ' col-xs-' + (12 - this.labelxs);
33806 cls : 'roo-radio-set',
33810 cls : 'roo-radio-set-input',
33813 value : this.value ? this.value : ''
33820 if(this.weight.length){
33821 cfg.cls += ' roo-radio-' + this.weight;
33825 cfg.cls += ' roo-radio-set-inline';
33829 ['xs','sm','md','lg'].map(function(size){
33830 if (settings[size]) {
33831 cfg.cls += ' col-' + size + '-' + settings[size];
33839 initEvents : function()
33841 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33842 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33844 if(!this.fieldLabel.length){
33845 this.labelEl.hide();
33848 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33849 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33851 this.indicator = this.indicatorEl();
33853 if(this.indicator){
33854 this.indicator.addClass('invisible');
33857 this.originalValue = this.getValue();
33861 inputEl: function ()
33863 return this.el.select('.roo-radio-set-input', true).first();
33866 getChildContainer : function()
33868 return this.itemsEl;
33871 register : function(item)
33873 this.radioes.push(item);
33877 validate : function()
33879 if(this.getVisibilityEl().hasClass('hidden')){
33885 Roo.each(this.radioes, function(i){
33894 if(this.allowBlank) {
33898 if(this.disabled || valid){
33903 this.markInvalid();
33908 markValid : function()
33910 if(this.labelEl.isVisible(true)){
33911 this.indicatorEl().removeClass('visible');
33912 this.indicatorEl().addClass('invisible');
33915 this.el.removeClass([this.invalidClass, this.validClass]);
33916 this.el.addClass(this.validClass);
33918 this.fireEvent('valid', this);
33921 markInvalid : function(msg)
33923 if(this.allowBlank || this.disabled){
33927 if(this.labelEl.isVisible(true)){
33928 this.indicatorEl().removeClass('invisible');
33929 this.indicatorEl().addClass('visible');
33932 this.el.removeClass([this.invalidClass, this.validClass]);
33933 this.el.addClass(this.invalidClass);
33935 this.fireEvent('invalid', this, msg);
33939 setValue : function(v, suppressEvent)
33941 if(this.value === v){
33948 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33951 Roo.each(this.radioes, function(i){
33953 i.el.removeClass('checked');
33956 Roo.each(this.radioes, function(i){
33958 if(i.value === v || i.value.toString() === v.toString()){
33960 i.el.addClass('checked');
33962 if(suppressEvent !== true){
33963 this.fireEvent('check', this, i);
33974 clearInvalid : function(){
33976 if(!this.el || this.preventMark){
33980 this.el.removeClass([this.invalidClass]);
33982 this.fireEvent('valid', this);
33987 Roo.apply(Roo.bootstrap.RadioSet, {
33991 register : function(set)
33993 this.groups[set.name] = set;
33996 get: function(name)
33998 if (typeof(this.groups[name]) == 'undefined') {
34002 return this.groups[name] ;
34008 * Ext JS Library 1.1.1
34009 * Copyright(c) 2006-2007, Ext JS, LLC.
34011 * Originally Released Under LGPL - original licence link has changed is not relivant.
34014 * <script type="text/javascript">
34019 * @class Roo.bootstrap.SplitBar
34020 * @extends Roo.util.Observable
34021 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34025 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34026 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34027 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34028 split.minSize = 100;
34029 split.maxSize = 600;
34030 split.animate = true;
34031 split.on('moved', splitterMoved);
34034 * Create a new SplitBar
34035 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34036 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34037 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34038 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34039 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34040 position of the SplitBar).
34042 Roo.bootstrap.SplitBar = function(cfg){
34047 // dragElement : elm
34048 // resizingElement: el,
34050 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34051 // placement : Roo.bootstrap.SplitBar.LEFT ,
34052 // existingProxy ???
34055 this.el = Roo.get(cfg.dragElement, true);
34056 this.el.dom.unselectable = "on";
34058 this.resizingEl = Roo.get(cfg.resizingElement, true);
34062 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34063 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34066 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34069 * The minimum size of the resizing element. (Defaults to 0)
34075 * The maximum size of the resizing element. (Defaults to 2000)
34078 this.maxSize = 2000;
34081 * Whether to animate the transition to the new size
34084 this.animate = false;
34087 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34090 this.useShim = false;
34095 if(!cfg.existingProxy){
34097 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34099 this.proxy = Roo.get(cfg.existingProxy).dom;
34102 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34105 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34108 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34111 this.dragSpecs = {};
34114 * @private The adapter to use to positon and resize elements
34116 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34117 this.adapter.init(this);
34119 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34121 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34122 this.el.addClass("roo-splitbar-h");
34125 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34126 this.el.addClass("roo-splitbar-v");
34132 * Fires when the splitter is moved (alias for {@link #event-moved})
34133 * @param {Roo.bootstrap.SplitBar} this
34134 * @param {Number} newSize the new width or height
34139 * Fires when the splitter is moved
34140 * @param {Roo.bootstrap.SplitBar} this
34141 * @param {Number} newSize the new width or height
34145 * @event beforeresize
34146 * Fires before the splitter is dragged
34147 * @param {Roo.bootstrap.SplitBar} this
34149 "beforeresize" : true,
34151 "beforeapply" : true
34154 Roo.util.Observable.call(this);
34157 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34158 onStartProxyDrag : function(x, y){
34159 this.fireEvent("beforeresize", this);
34161 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34163 o.enableDisplayMode("block");
34164 // all splitbars share the same overlay
34165 Roo.bootstrap.SplitBar.prototype.overlay = o;
34167 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34168 this.overlay.show();
34169 Roo.get(this.proxy).setDisplayed("block");
34170 var size = this.adapter.getElementSize(this);
34171 this.activeMinSize = this.getMinimumSize();;
34172 this.activeMaxSize = this.getMaximumSize();;
34173 var c1 = size - this.activeMinSize;
34174 var c2 = Math.max(this.activeMaxSize - size, 0);
34175 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34176 this.dd.resetConstraints();
34177 this.dd.setXConstraint(
34178 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34179 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34181 this.dd.setYConstraint(0, 0);
34183 this.dd.resetConstraints();
34184 this.dd.setXConstraint(0, 0);
34185 this.dd.setYConstraint(
34186 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34187 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34190 this.dragSpecs.startSize = size;
34191 this.dragSpecs.startPoint = [x, y];
34192 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34196 * @private Called after the drag operation by the DDProxy
34198 onEndProxyDrag : function(e){
34199 Roo.get(this.proxy).setDisplayed(false);
34200 var endPoint = Roo.lib.Event.getXY(e);
34202 this.overlay.hide();
34205 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34206 newSize = this.dragSpecs.startSize +
34207 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34208 endPoint[0] - this.dragSpecs.startPoint[0] :
34209 this.dragSpecs.startPoint[0] - endPoint[0]
34212 newSize = this.dragSpecs.startSize +
34213 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34214 endPoint[1] - this.dragSpecs.startPoint[1] :
34215 this.dragSpecs.startPoint[1] - endPoint[1]
34218 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34219 if(newSize != this.dragSpecs.startSize){
34220 if(this.fireEvent('beforeapply', this, newSize) !== false){
34221 this.adapter.setElementSize(this, newSize);
34222 this.fireEvent("moved", this, newSize);
34223 this.fireEvent("resize", this, newSize);
34229 * Get the adapter this SplitBar uses
34230 * @return The adapter object
34232 getAdapter : function(){
34233 return this.adapter;
34237 * Set the adapter this SplitBar uses
34238 * @param {Object} adapter A SplitBar adapter object
34240 setAdapter : function(adapter){
34241 this.adapter = adapter;
34242 this.adapter.init(this);
34246 * Gets the minimum size for the resizing element
34247 * @return {Number} The minimum size
34249 getMinimumSize : function(){
34250 return this.minSize;
34254 * Sets the minimum size for the resizing element
34255 * @param {Number} minSize The minimum size
34257 setMinimumSize : function(minSize){
34258 this.minSize = minSize;
34262 * Gets the maximum size for the resizing element
34263 * @return {Number} The maximum size
34265 getMaximumSize : function(){
34266 return this.maxSize;
34270 * Sets the maximum size for the resizing element
34271 * @param {Number} maxSize The maximum size
34273 setMaximumSize : function(maxSize){
34274 this.maxSize = maxSize;
34278 * Sets the initialize size for the resizing element
34279 * @param {Number} size The initial size
34281 setCurrentSize : function(size){
34282 var oldAnimate = this.animate;
34283 this.animate = false;
34284 this.adapter.setElementSize(this, size);
34285 this.animate = oldAnimate;
34289 * Destroy this splitbar.
34290 * @param {Boolean} removeEl True to remove the element
34292 destroy : function(removeEl){
34294 this.shim.remove();
34297 this.proxy.parentNode.removeChild(this.proxy);
34305 * @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.
34307 Roo.bootstrap.SplitBar.createProxy = function(dir){
34308 var proxy = new Roo.Element(document.createElement("div"));
34309 proxy.unselectable();
34310 var cls = 'roo-splitbar-proxy';
34311 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34312 document.body.appendChild(proxy.dom);
34317 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34318 * Default Adapter. It assumes the splitter and resizing element are not positioned
34319 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34321 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34324 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34325 // do nothing for now
34326 init : function(s){
34330 * Called before drag operations to get the current size of the resizing element.
34331 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34333 getElementSize : function(s){
34334 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34335 return s.resizingEl.getWidth();
34337 return s.resizingEl.getHeight();
34342 * Called after drag operations to set the size of the resizing element.
34343 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34344 * @param {Number} newSize The new size to set
34345 * @param {Function} onComplete A function to be invoked when resizing is complete
34347 setElementSize : function(s, newSize, onComplete){
34348 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34350 s.resizingEl.setWidth(newSize);
34352 onComplete(s, newSize);
34355 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34360 s.resizingEl.setHeight(newSize);
34362 onComplete(s, newSize);
34365 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34372 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34373 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34374 * Adapter that moves the splitter element to align with the resized sizing element.
34375 * Used with an absolute positioned SplitBar.
34376 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34377 * document.body, make sure you assign an id to the body element.
34379 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34380 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34381 this.container = Roo.get(container);
34384 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34385 init : function(s){
34386 this.basic.init(s);
34389 getElementSize : function(s){
34390 return this.basic.getElementSize(s);
34393 setElementSize : function(s, newSize, onComplete){
34394 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34397 moveSplitter : function(s){
34398 var yes = Roo.bootstrap.SplitBar;
34399 switch(s.placement){
34401 s.el.setX(s.resizingEl.getRight());
34404 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34407 s.el.setY(s.resizingEl.getBottom());
34410 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34417 * Orientation constant - Create a vertical SplitBar
34421 Roo.bootstrap.SplitBar.VERTICAL = 1;
34424 * Orientation constant - Create a horizontal SplitBar
34428 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34431 * Placement constant - The resizing element is to the left of the splitter element
34435 Roo.bootstrap.SplitBar.LEFT = 1;
34438 * Placement constant - The resizing element is to the right of the splitter element
34442 Roo.bootstrap.SplitBar.RIGHT = 2;
34445 * Placement constant - The resizing element is positioned above the splitter element
34449 Roo.bootstrap.SplitBar.TOP = 3;
34452 * Placement constant - The resizing element is positioned under splitter element
34456 Roo.bootstrap.SplitBar.BOTTOM = 4;
34457 Roo.namespace("Roo.bootstrap.layout");/*
34459 * Ext JS Library 1.1.1
34460 * Copyright(c) 2006-2007, Ext JS, LLC.
34462 * Originally Released Under LGPL - original licence link has changed is not relivant.
34465 * <script type="text/javascript">
34469 * @class Roo.bootstrap.layout.Manager
34470 * @extends Roo.bootstrap.Component
34471 * Base class for layout managers.
34473 Roo.bootstrap.layout.Manager = function(config)
34475 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34481 /** false to disable window resize monitoring @type Boolean */
34482 this.monitorWindowResize = true;
34487 * Fires when a layout is performed.
34488 * @param {Roo.LayoutManager} this
34492 * @event regionresized
34493 * Fires when the user resizes a region.
34494 * @param {Roo.LayoutRegion} region The resized region
34495 * @param {Number} newSize The new size (width for east/west, height for north/south)
34497 "regionresized" : true,
34499 * @event regioncollapsed
34500 * Fires when a region is collapsed.
34501 * @param {Roo.LayoutRegion} region The collapsed region
34503 "regioncollapsed" : true,
34505 * @event regionexpanded
34506 * Fires when a region is expanded.
34507 * @param {Roo.LayoutRegion} region The expanded region
34509 "regionexpanded" : true
34511 this.updating = false;
34514 this.el = Roo.get(config.el);
34520 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34525 monitorWindowResize : true,
34531 onRender : function(ct, position)
34534 this.el = Roo.get(ct);
34537 //this.fireEvent('render',this);
34541 initEvents: function()
34545 // ie scrollbar fix
34546 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34547 document.body.scroll = "no";
34548 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34549 this.el.position('relative');
34551 this.id = this.el.id;
34552 this.el.addClass("roo-layout-container");
34553 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34554 if(this.el.dom != document.body ) {
34555 this.el.on('resize', this.layout,this);
34556 this.el.on('show', this.layout,this);
34562 * Returns true if this layout is currently being updated
34563 * @return {Boolean}
34565 isUpdating : function(){
34566 return this.updating;
34570 * Suspend the LayoutManager from doing auto-layouts while
34571 * making multiple add or remove calls
34573 beginUpdate : function(){
34574 this.updating = true;
34578 * Restore auto-layouts and optionally disable the manager from performing a layout
34579 * @param {Boolean} noLayout true to disable a layout update
34581 endUpdate : function(noLayout){
34582 this.updating = false;
34588 layout: function(){
34592 onRegionResized : function(region, newSize){
34593 this.fireEvent("regionresized", region, newSize);
34597 onRegionCollapsed : function(region){
34598 this.fireEvent("regioncollapsed", region);
34601 onRegionExpanded : function(region){
34602 this.fireEvent("regionexpanded", region);
34606 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34607 * performs box-model adjustments.
34608 * @return {Object} The size as an object {width: (the width), height: (the height)}
34610 getViewSize : function()
34613 if(this.el.dom != document.body){
34614 size = this.el.getSize();
34616 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34618 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34619 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34624 * Returns the Element this layout is bound to.
34625 * @return {Roo.Element}
34627 getEl : function(){
34632 * Returns the specified region.
34633 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34634 * @return {Roo.LayoutRegion}
34636 getRegion : function(target){
34637 return this.regions[target.toLowerCase()];
34640 onWindowResize : function(){
34641 if(this.monitorWindowResize){
34648 * Ext JS Library 1.1.1
34649 * Copyright(c) 2006-2007, Ext JS, LLC.
34651 * Originally Released Under LGPL - original licence link has changed is not relivant.
34654 * <script type="text/javascript">
34657 * @class Roo.bootstrap.layout.Border
34658 * @extends Roo.bootstrap.layout.Manager
34659 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34660 * please see: examples/bootstrap/nested.html<br><br>
34662 <b>The container the layout is rendered into can be either the body element or any other element.
34663 If it is not the body element, the container needs to either be an absolute positioned element,
34664 or you will need to add "position:relative" to the css of the container. You will also need to specify
34665 the container size if it is not the body element.</b>
34668 * Create a new Border
34669 * @param {Object} config Configuration options
34671 Roo.bootstrap.layout.Border = function(config){
34672 config = config || {};
34673 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34677 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34678 if(config[region]){
34679 config[region].region = region;
34680 this.addRegion(config[region]);
34686 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34688 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34690 * Creates and adds a new region if it doesn't already exist.
34691 * @param {String} target The target region key (north, south, east, west or center).
34692 * @param {Object} config The regions config object
34693 * @return {BorderLayoutRegion} The new region
34695 addRegion : function(config)
34697 if(!this.regions[config.region]){
34698 var r = this.factory(config);
34699 this.bindRegion(r);
34701 return this.regions[config.region];
34705 bindRegion : function(r){
34706 this.regions[r.config.region] = r;
34708 r.on("visibilitychange", this.layout, this);
34709 r.on("paneladded", this.layout, this);
34710 r.on("panelremoved", this.layout, this);
34711 r.on("invalidated", this.layout, this);
34712 r.on("resized", this.onRegionResized, this);
34713 r.on("collapsed", this.onRegionCollapsed, this);
34714 r.on("expanded", this.onRegionExpanded, this);
34718 * Performs a layout update.
34720 layout : function()
34722 if(this.updating) {
34726 // render all the rebions if they have not been done alreayd?
34727 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34728 if(this.regions[region] && !this.regions[region].bodyEl){
34729 this.regions[region].onRender(this.el)
34733 var size = this.getViewSize();
34734 var w = size.width;
34735 var h = size.height;
34740 //var x = 0, y = 0;
34742 var rs = this.regions;
34743 var north = rs["north"];
34744 var south = rs["south"];
34745 var west = rs["west"];
34746 var east = rs["east"];
34747 var center = rs["center"];
34748 //if(this.hideOnLayout){ // not supported anymore
34749 //c.el.setStyle("display", "none");
34751 if(north && north.isVisible()){
34752 var b = north.getBox();
34753 var m = north.getMargins();
34754 b.width = w - (m.left+m.right);
34757 centerY = b.height + b.y + m.bottom;
34758 centerH -= centerY;
34759 north.updateBox(this.safeBox(b));
34761 if(south && south.isVisible()){
34762 var b = south.getBox();
34763 var m = south.getMargins();
34764 b.width = w - (m.left+m.right);
34766 var totalHeight = (b.height + m.top + m.bottom);
34767 b.y = h - totalHeight + m.top;
34768 centerH -= totalHeight;
34769 south.updateBox(this.safeBox(b));
34771 if(west && west.isVisible()){
34772 var b = west.getBox();
34773 var m = west.getMargins();
34774 b.height = centerH - (m.top+m.bottom);
34776 b.y = centerY + m.top;
34777 var totalWidth = (b.width + m.left + m.right);
34778 centerX += totalWidth;
34779 centerW -= totalWidth;
34780 west.updateBox(this.safeBox(b));
34782 if(east && east.isVisible()){
34783 var b = east.getBox();
34784 var m = east.getMargins();
34785 b.height = centerH - (m.top+m.bottom);
34786 var totalWidth = (b.width + m.left + m.right);
34787 b.x = w - totalWidth + m.left;
34788 b.y = centerY + m.top;
34789 centerW -= totalWidth;
34790 east.updateBox(this.safeBox(b));
34793 var m = center.getMargins();
34795 x: centerX + m.left,
34796 y: centerY + m.top,
34797 width: centerW - (m.left+m.right),
34798 height: centerH - (m.top+m.bottom)
34800 //if(this.hideOnLayout){
34801 //center.el.setStyle("display", "block");
34803 center.updateBox(this.safeBox(centerBox));
34806 this.fireEvent("layout", this);
34810 safeBox : function(box){
34811 box.width = Math.max(0, box.width);
34812 box.height = Math.max(0, box.height);
34817 * Adds a ContentPanel (or subclass) to this layout.
34818 * @param {String} target The target region key (north, south, east, west or center).
34819 * @param {Roo.ContentPanel} panel The panel to add
34820 * @return {Roo.ContentPanel} The added panel
34822 add : function(target, panel){
34824 target = target.toLowerCase();
34825 return this.regions[target].add(panel);
34829 * Remove a ContentPanel (or subclass) to this layout.
34830 * @param {String} target The target region key (north, south, east, west or center).
34831 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34832 * @return {Roo.ContentPanel} The removed panel
34834 remove : function(target, panel){
34835 target = target.toLowerCase();
34836 return this.regions[target].remove(panel);
34840 * Searches all regions for a panel with the specified id
34841 * @param {String} panelId
34842 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34844 findPanel : function(panelId){
34845 var rs = this.regions;
34846 for(var target in rs){
34847 if(typeof rs[target] != "function"){
34848 var p = rs[target].getPanel(panelId);
34858 * Searches all regions for a panel with the specified id and activates (shows) it.
34859 * @param {String/ContentPanel} panelId The panels id or the panel itself
34860 * @return {Roo.ContentPanel} The shown panel or null
34862 showPanel : function(panelId) {
34863 var rs = this.regions;
34864 for(var target in rs){
34865 var r = rs[target];
34866 if(typeof r != "function"){
34867 if(r.hasPanel(panelId)){
34868 return r.showPanel(panelId);
34876 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34877 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34880 restoreState : function(provider){
34882 provider = Roo.state.Manager;
34884 var sm = new Roo.LayoutStateManager();
34885 sm.init(this, provider);
34891 * Adds a xtype elements to the layout.
34895 xtype : 'ContentPanel',
34902 xtype : 'NestedLayoutPanel',
34908 items : [ ... list of content panels or nested layout panels.. ]
34912 * @param {Object} cfg Xtype definition of item to add.
34914 addxtype : function(cfg)
34916 // basically accepts a pannel...
34917 // can accept a layout region..!?!?
34918 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34921 // theory? children can only be panels??
34923 //if (!cfg.xtype.match(/Panel$/)) {
34928 if (typeof(cfg.region) == 'undefined') {
34929 Roo.log("Failed to add Panel, region was not set");
34933 var region = cfg.region;
34939 xitems = cfg.items;
34946 case 'Content': // ContentPanel (el, cfg)
34947 case 'Scroll': // ContentPanel (el, cfg)
34949 cfg.autoCreate = true;
34950 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34952 // var el = this.el.createChild();
34953 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34956 this.add(region, ret);
34960 case 'TreePanel': // our new panel!
34961 cfg.el = this.el.createChild();
34962 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34963 this.add(region, ret);
34968 // create a new Layout (which is a Border Layout...
34970 var clayout = cfg.layout;
34971 clayout.el = this.el.createChild();
34972 clayout.items = clayout.items || [];
34976 // replace this exitems with the clayout ones..
34977 xitems = clayout.items;
34979 // force background off if it's in center...
34980 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34981 cfg.background = false;
34983 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34986 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34987 //console.log('adding nested layout panel ' + cfg.toSource());
34988 this.add(region, ret);
34989 nb = {}; /// find first...
34994 // needs grid and region
34996 //var el = this.getRegion(region).el.createChild();
34998 *var el = this.el.createChild();
34999 // create the grid first...
35000 cfg.grid.container = el;
35001 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35004 if (region == 'center' && this.active ) {
35005 cfg.background = false;
35008 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35010 this.add(region, ret);
35012 if (cfg.background) {
35013 // render grid on panel activation (if panel background)
35014 ret.on('activate', function(gp) {
35015 if (!gp.grid.rendered) {
35016 // gp.grid.render(el);
35020 // cfg.grid.render(el);
35026 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35027 // it was the old xcomponent building that caused this before.
35028 // espeically if border is the top element in the tree.
35038 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35040 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35041 this.add(region, ret);
35045 throw "Can not add '" + cfg.xtype + "' to Border";
35051 this.beginUpdate();
35055 Roo.each(xitems, function(i) {
35056 region = nb && i.region ? i.region : false;
35058 var add = ret.addxtype(i);
35061 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35062 if (!i.background) {
35063 abn[region] = nb[region] ;
35070 // make the last non-background panel active..
35071 //if (nb) { Roo.log(abn); }
35074 for(var r in abn) {
35075 region = this.getRegion(r);
35077 // tried using nb[r], but it does not work..
35079 region.showPanel(abn[r]);
35090 factory : function(cfg)
35093 var validRegions = Roo.bootstrap.layout.Border.regions;
35095 var target = cfg.region;
35098 var r = Roo.bootstrap.layout;
35102 return new r.North(cfg);
35104 return new r.South(cfg);
35106 return new r.East(cfg);
35108 return new r.West(cfg);
35110 return new r.Center(cfg);
35112 throw 'Layout region "'+target+'" not supported.';
35119 * Ext JS Library 1.1.1
35120 * Copyright(c) 2006-2007, Ext JS, LLC.
35122 * Originally Released Under LGPL - original licence link has changed is not relivant.
35125 * <script type="text/javascript">
35129 * @class Roo.bootstrap.layout.Basic
35130 * @extends Roo.util.Observable
35131 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35132 * and does not have a titlebar, tabs or any other features. All it does is size and position
35133 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35134 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35135 * @cfg {string} region the region that it inhabits..
35136 * @cfg {bool} skipConfig skip config?
35140 Roo.bootstrap.layout.Basic = function(config){
35142 this.mgr = config.mgr;
35144 this.position = config.region;
35146 var skipConfig = config.skipConfig;
35150 * @scope Roo.BasicLayoutRegion
35154 * @event beforeremove
35155 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35156 * @param {Roo.LayoutRegion} this
35157 * @param {Roo.ContentPanel} panel The panel
35158 * @param {Object} e The cancel event object
35160 "beforeremove" : true,
35162 * @event invalidated
35163 * Fires when the layout for this region is changed.
35164 * @param {Roo.LayoutRegion} this
35166 "invalidated" : true,
35168 * @event visibilitychange
35169 * Fires when this region is shown or hidden
35170 * @param {Roo.LayoutRegion} this
35171 * @param {Boolean} visibility true or false
35173 "visibilitychange" : true,
35175 * @event paneladded
35176 * Fires when a panel is added.
35177 * @param {Roo.LayoutRegion} this
35178 * @param {Roo.ContentPanel} panel The panel
35180 "paneladded" : true,
35182 * @event panelremoved
35183 * Fires when a panel is removed.
35184 * @param {Roo.LayoutRegion} this
35185 * @param {Roo.ContentPanel} panel The panel
35187 "panelremoved" : true,
35189 * @event beforecollapse
35190 * Fires when this region before collapse.
35191 * @param {Roo.LayoutRegion} this
35193 "beforecollapse" : true,
35196 * Fires when this region is collapsed.
35197 * @param {Roo.LayoutRegion} this
35199 "collapsed" : true,
35202 * Fires when this region is expanded.
35203 * @param {Roo.LayoutRegion} this
35208 * Fires when this region is slid into view.
35209 * @param {Roo.LayoutRegion} this
35211 "slideshow" : true,
35214 * Fires when this region slides out of view.
35215 * @param {Roo.LayoutRegion} this
35217 "slidehide" : true,
35219 * @event panelactivated
35220 * Fires when a panel is activated.
35221 * @param {Roo.LayoutRegion} this
35222 * @param {Roo.ContentPanel} panel The activated panel
35224 "panelactivated" : true,
35227 * Fires when the user resizes this region.
35228 * @param {Roo.LayoutRegion} this
35229 * @param {Number} newSize The new size (width for east/west, height for north/south)
35233 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35234 this.panels = new Roo.util.MixedCollection();
35235 this.panels.getKey = this.getPanelId.createDelegate(this);
35237 this.activePanel = null;
35238 // ensure listeners are added...
35240 if (config.listeners || config.events) {
35241 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35242 listeners : config.listeners || {},
35243 events : config.events || {}
35247 if(skipConfig !== true){
35248 this.applyConfig(config);
35252 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35254 getPanelId : function(p){
35258 applyConfig : function(config){
35259 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35260 this.config = config;
35265 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35266 * the width, for horizontal (north, south) the height.
35267 * @param {Number} newSize The new width or height
35269 resizeTo : function(newSize){
35270 var el = this.el ? this.el :
35271 (this.activePanel ? this.activePanel.getEl() : null);
35273 switch(this.position){
35276 el.setWidth(newSize);
35277 this.fireEvent("resized", this, newSize);
35281 el.setHeight(newSize);
35282 this.fireEvent("resized", this, newSize);
35288 getBox : function(){
35289 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35292 getMargins : function(){
35293 return this.margins;
35296 updateBox : function(box){
35298 var el = this.activePanel.getEl();
35299 el.dom.style.left = box.x + "px";
35300 el.dom.style.top = box.y + "px";
35301 this.activePanel.setSize(box.width, box.height);
35305 * Returns the container element for this region.
35306 * @return {Roo.Element}
35308 getEl : function(){
35309 return this.activePanel;
35313 * Returns true if this region is currently visible.
35314 * @return {Boolean}
35316 isVisible : function(){
35317 return this.activePanel ? true : false;
35320 setActivePanel : function(panel){
35321 panel = this.getPanel(panel);
35322 if(this.activePanel && this.activePanel != panel){
35323 this.activePanel.setActiveState(false);
35324 this.activePanel.getEl().setLeftTop(-10000,-10000);
35326 this.activePanel = panel;
35327 panel.setActiveState(true);
35329 panel.setSize(this.box.width, this.box.height);
35331 this.fireEvent("panelactivated", this, panel);
35332 this.fireEvent("invalidated");
35336 * Show the specified panel.
35337 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35338 * @return {Roo.ContentPanel} The shown panel or null
35340 showPanel : function(panel){
35341 panel = this.getPanel(panel);
35343 this.setActivePanel(panel);
35349 * Get the active panel for this region.
35350 * @return {Roo.ContentPanel} The active panel or null
35352 getActivePanel : function(){
35353 return this.activePanel;
35357 * Add the passed ContentPanel(s)
35358 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35359 * @return {Roo.ContentPanel} The panel added (if only one was added)
35361 add : function(panel){
35362 if(arguments.length > 1){
35363 for(var i = 0, len = arguments.length; i < len; i++) {
35364 this.add(arguments[i]);
35368 if(this.hasPanel(panel)){
35369 this.showPanel(panel);
35372 var el = panel.getEl();
35373 if(el.dom.parentNode != this.mgr.el.dom){
35374 this.mgr.el.dom.appendChild(el.dom);
35376 if(panel.setRegion){
35377 panel.setRegion(this);
35379 this.panels.add(panel);
35380 el.setStyle("position", "absolute");
35381 if(!panel.background){
35382 this.setActivePanel(panel);
35383 if(this.config.initialSize && this.panels.getCount()==1){
35384 this.resizeTo(this.config.initialSize);
35387 this.fireEvent("paneladded", this, panel);
35392 * Returns true if the panel is in this region.
35393 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35394 * @return {Boolean}
35396 hasPanel : function(panel){
35397 if(typeof panel == "object"){ // must be panel obj
35398 panel = panel.getId();
35400 return this.getPanel(panel) ? true : false;
35404 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35405 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35406 * @param {Boolean} preservePanel Overrides the config preservePanel option
35407 * @return {Roo.ContentPanel} The panel that was removed
35409 remove : function(panel, preservePanel){
35410 panel = this.getPanel(panel);
35415 this.fireEvent("beforeremove", this, panel, e);
35416 if(e.cancel === true){
35419 var panelId = panel.getId();
35420 this.panels.removeKey(panelId);
35425 * Returns the panel specified or null if it's not in this region.
35426 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35427 * @return {Roo.ContentPanel}
35429 getPanel : function(id){
35430 if(typeof id == "object"){ // must be panel obj
35433 return this.panels.get(id);
35437 * Returns this regions position (north/south/east/west/center).
35440 getPosition: function(){
35441 return this.position;
35445 * Ext JS Library 1.1.1
35446 * Copyright(c) 2006-2007, Ext JS, LLC.
35448 * Originally Released Under LGPL - original licence link has changed is not relivant.
35451 * <script type="text/javascript">
35455 * @class Roo.bootstrap.layout.Region
35456 * @extends Roo.bootstrap.layout.Basic
35457 * This class represents a region in a layout manager.
35459 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35460 * @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})
35461 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35462 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35463 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35464 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35465 * @cfg {String} title The title for the region (overrides panel titles)
35466 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35467 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35468 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35469 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35470 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35471 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35472 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35473 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35474 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35475 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35477 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35478 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35479 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35480 * @cfg {Number} width For East/West panels
35481 * @cfg {Number} height For North/South panels
35482 * @cfg {Boolean} split To show the splitter
35483 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35485 * @cfg {string} cls Extra CSS classes to add to region
35487 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35488 * @cfg {string} region the region that it inhabits..
35491 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35492 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35494 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35495 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35496 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35498 Roo.bootstrap.layout.Region = function(config)
35500 this.applyConfig(config);
35502 var mgr = config.mgr;
35503 var pos = config.region;
35504 config.skipConfig = true;
35505 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35508 this.onRender(mgr.el);
35511 this.visible = true;
35512 this.collapsed = false;
35513 this.unrendered_panels = [];
35516 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35518 position: '', // set by wrapper (eg. north/south etc..)
35519 unrendered_panels : null, // unrendered panels.
35520 createBody : function(){
35521 /** This region's body element
35522 * @type Roo.Element */
35523 this.bodyEl = this.el.createChild({
35525 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35529 onRender: function(ctr, pos)
35531 var dh = Roo.DomHelper;
35532 /** This region's container element
35533 * @type Roo.Element */
35534 this.el = dh.append(ctr.dom, {
35536 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35538 /** This region's title element
35539 * @type Roo.Element */
35541 this.titleEl = dh.append(this.el.dom,
35544 unselectable: "on",
35545 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35547 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35548 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35551 this.titleEl.enableDisplayMode();
35552 /** This region's title text element
35553 * @type HTMLElement */
35554 this.titleTextEl = this.titleEl.dom.firstChild;
35555 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35557 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35558 this.closeBtn.enableDisplayMode();
35559 this.closeBtn.on("click", this.closeClicked, this);
35560 this.closeBtn.hide();
35562 this.createBody(this.config);
35563 if(this.config.hideWhenEmpty){
35565 this.on("paneladded", this.validateVisibility, this);
35566 this.on("panelremoved", this.validateVisibility, this);
35568 if(this.autoScroll){
35569 this.bodyEl.setStyle("overflow", "auto");
35571 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35573 //if(c.titlebar !== false){
35574 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35575 this.titleEl.hide();
35577 this.titleEl.show();
35578 if(this.config.title){
35579 this.titleTextEl.innerHTML = this.config.title;
35583 if(this.config.collapsed){
35584 this.collapse(true);
35586 if(this.config.hidden){
35590 if (this.unrendered_panels && this.unrendered_panels.length) {
35591 for (var i =0;i< this.unrendered_panels.length; i++) {
35592 this.add(this.unrendered_panels[i]);
35594 this.unrendered_panels = null;
35600 applyConfig : function(c)
35603 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35604 var dh = Roo.DomHelper;
35605 if(c.titlebar !== false){
35606 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35607 this.collapseBtn.on("click", this.collapse, this);
35608 this.collapseBtn.enableDisplayMode();
35610 if(c.showPin === true || this.showPin){
35611 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35612 this.stickBtn.enableDisplayMode();
35613 this.stickBtn.on("click", this.expand, this);
35614 this.stickBtn.hide();
35619 /** This region's collapsed element
35620 * @type Roo.Element */
35623 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35624 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35627 if(c.floatable !== false){
35628 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35629 this.collapsedEl.on("click", this.collapseClick, this);
35632 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35633 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35634 id: "message", unselectable: "on", style:{"float":"left"}});
35635 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35637 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35638 this.expandBtn.on("click", this.expand, this);
35642 if(this.collapseBtn){
35643 this.collapseBtn.setVisible(c.collapsible == true);
35646 this.cmargins = c.cmargins || this.cmargins ||
35647 (this.position == "west" || this.position == "east" ?
35648 {top: 0, left: 2, right:2, bottom: 0} :
35649 {top: 2, left: 0, right:0, bottom: 2});
35651 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35654 this.bottomTabs = c.tabPosition != "top";
35656 this.autoScroll = c.autoScroll || false;
35661 this.duration = c.duration || .30;
35662 this.slideDuration = c.slideDuration || .45;
35667 * Returns true if this region is currently visible.
35668 * @return {Boolean}
35670 isVisible : function(){
35671 return this.visible;
35675 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35676 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35678 //setCollapsedTitle : function(title){
35679 // title = title || " ";
35680 // if(this.collapsedTitleTextEl){
35681 // this.collapsedTitleTextEl.innerHTML = title;
35685 getBox : function(){
35687 // if(!this.collapsed){
35688 b = this.el.getBox(false, true);
35690 // b = this.collapsedEl.getBox(false, true);
35695 getMargins : function(){
35696 return this.margins;
35697 //return this.collapsed ? this.cmargins : this.margins;
35700 highlight : function(){
35701 this.el.addClass("x-layout-panel-dragover");
35704 unhighlight : function(){
35705 this.el.removeClass("x-layout-panel-dragover");
35708 updateBox : function(box)
35710 if (!this.bodyEl) {
35711 return; // not rendered yet..
35715 if(!this.collapsed){
35716 this.el.dom.style.left = box.x + "px";
35717 this.el.dom.style.top = box.y + "px";
35718 this.updateBody(box.width, box.height);
35720 this.collapsedEl.dom.style.left = box.x + "px";
35721 this.collapsedEl.dom.style.top = box.y + "px";
35722 this.collapsedEl.setSize(box.width, box.height);
35725 this.tabs.autoSizeTabs();
35729 updateBody : function(w, h)
35732 this.el.setWidth(w);
35733 w -= this.el.getBorderWidth("rl");
35734 if(this.config.adjustments){
35735 w += this.config.adjustments[0];
35738 if(h !== null && h > 0){
35739 this.el.setHeight(h);
35740 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35741 h -= this.el.getBorderWidth("tb");
35742 if(this.config.adjustments){
35743 h += this.config.adjustments[1];
35745 this.bodyEl.setHeight(h);
35747 h = this.tabs.syncHeight(h);
35750 if(this.panelSize){
35751 w = w !== null ? w : this.panelSize.width;
35752 h = h !== null ? h : this.panelSize.height;
35754 if(this.activePanel){
35755 var el = this.activePanel.getEl();
35756 w = w !== null ? w : el.getWidth();
35757 h = h !== null ? h : el.getHeight();
35758 this.panelSize = {width: w, height: h};
35759 this.activePanel.setSize(w, h);
35761 if(Roo.isIE && this.tabs){
35762 this.tabs.el.repaint();
35767 * Returns the container element for this region.
35768 * @return {Roo.Element}
35770 getEl : function(){
35775 * Hides this region.
35778 //if(!this.collapsed){
35779 this.el.dom.style.left = "-2000px";
35782 // this.collapsedEl.dom.style.left = "-2000px";
35783 // this.collapsedEl.hide();
35785 this.visible = false;
35786 this.fireEvent("visibilitychange", this, false);
35790 * Shows this region if it was previously hidden.
35793 //if(!this.collapsed){
35796 // this.collapsedEl.show();
35798 this.visible = true;
35799 this.fireEvent("visibilitychange", this, true);
35802 closeClicked : function(){
35803 if(this.activePanel){
35804 this.remove(this.activePanel);
35808 collapseClick : function(e){
35810 e.stopPropagation();
35813 e.stopPropagation();
35819 * Collapses this region.
35820 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35823 collapse : function(skipAnim, skipCheck = false){
35824 if(this.collapsed) {
35828 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35830 this.collapsed = true;
35832 this.split.el.hide();
35834 if(this.config.animate && skipAnim !== true){
35835 this.fireEvent("invalidated", this);
35836 this.animateCollapse();
35838 this.el.setLocation(-20000,-20000);
35840 this.collapsedEl.show();
35841 this.fireEvent("collapsed", this);
35842 this.fireEvent("invalidated", this);
35848 animateCollapse : function(){
35853 * Expands this region if it was previously collapsed.
35854 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35855 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35858 expand : function(e, skipAnim){
35860 e.stopPropagation();
35862 if(!this.collapsed || this.el.hasActiveFx()) {
35866 this.afterSlideIn();
35869 this.collapsed = false;
35870 if(this.config.animate && skipAnim !== true){
35871 this.animateExpand();
35875 this.split.el.show();
35877 this.collapsedEl.setLocation(-2000,-2000);
35878 this.collapsedEl.hide();
35879 this.fireEvent("invalidated", this);
35880 this.fireEvent("expanded", this);
35884 animateExpand : function(){
35888 initTabs : function()
35890 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35892 var ts = new Roo.bootstrap.panel.Tabs({
35893 el: this.bodyEl.dom,
35894 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35895 disableTooltips: this.config.disableTabTips,
35896 toolbar : this.config.toolbar
35899 if(this.config.hideTabs){
35900 ts.stripWrap.setDisplayed(false);
35903 ts.resizeTabs = this.config.resizeTabs === true;
35904 ts.minTabWidth = this.config.minTabWidth || 40;
35905 ts.maxTabWidth = this.config.maxTabWidth || 250;
35906 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35907 ts.monitorResize = false;
35908 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35909 ts.bodyEl.addClass('roo-layout-tabs-body');
35910 this.panels.each(this.initPanelAsTab, this);
35913 initPanelAsTab : function(panel){
35914 var ti = this.tabs.addTab(
35918 this.config.closeOnTab && panel.isClosable(),
35921 if(panel.tabTip !== undefined){
35922 ti.setTooltip(panel.tabTip);
35924 ti.on("activate", function(){
35925 this.setActivePanel(panel);
35928 if(this.config.closeOnTab){
35929 ti.on("beforeclose", function(t, e){
35931 this.remove(panel);
35935 panel.tabItem = ti;
35940 updatePanelTitle : function(panel, title)
35942 if(this.activePanel == panel){
35943 this.updateTitle(title);
35946 var ti = this.tabs.getTab(panel.getEl().id);
35948 if(panel.tabTip !== undefined){
35949 ti.setTooltip(panel.tabTip);
35954 updateTitle : function(title){
35955 if(this.titleTextEl && !this.config.title){
35956 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35960 setActivePanel : function(panel)
35962 panel = this.getPanel(panel);
35963 if(this.activePanel && this.activePanel != panel){
35964 if(this.activePanel.setActiveState(false) === false){
35968 this.activePanel = panel;
35969 panel.setActiveState(true);
35970 if(this.panelSize){
35971 panel.setSize(this.panelSize.width, this.panelSize.height);
35974 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35976 this.updateTitle(panel.getTitle());
35978 this.fireEvent("invalidated", this);
35980 this.fireEvent("panelactivated", this, panel);
35984 * Shows the specified panel.
35985 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35986 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35988 showPanel : function(panel)
35990 panel = this.getPanel(panel);
35993 var tab = this.tabs.getTab(panel.getEl().id);
35994 if(tab.isHidden()){
35995 this.tabs.unhideTab(tab.id);
35999 this.setActivePanel(panel);
36006 * Get the active panel for this region.
36007 * @return {Roo.ContentPanel} The active panel or null
36009 getActivePanel : function(){
36010 return this.activePanel;
36013 validateVisibility : function(){
36014 if(this.panels.getCount() < 1){
36015 this.updateTitle(" ");
36016 this.closeBtn.hide();
36019 if(!this.isVisible()){
36026 * Adds the passed ContentPanel(s) to this region.
36027 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36028 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36030 add : function(panel)
36032 if(arguments.length > 1){
36033 for(var i = 0, len = arguments.length; i < len; i++) {
36034 this.add(arguments[i]);
36039 // if we have not been rendered yet, then we can not really do much of this..
36040 if (!this.bodyEl) {
36041 this.unrendered_panels.push(panel);
36048 if(this.hasPanel(panel)){
36049 this.showPanel(panel);
36052 panel.setRegion(this);
36053 this.panels.add(panel);
36054 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36055 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36056 // and hide them... ???
36057 this.bodyEl.dom.appendChild(panel.getEl().dom);
36058 if(panel.background !== true){
36059 this.setActivePanel(panel);
36061 this.fireEvent("paneladded", this, panel);
36068 this.initPanelAsTab(panel);
36072 if(panel.background !== true){
36073 this.tabs.activate(panel.getEl().id);
36075 this.fireEvent("paneladded", this, panel);
36080 * Hides the tab for the specified panel.
36081 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36083 hidePanel : function(panel){
36084 if(this.tabs && (panel = this.getPanel(panel))){
36085 this.tabs.hideTab(panel.getEl().id);
36090 * Unhides the tab for a previously hidden panel.
36091 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36093 unhidePanel : function(panel){
36094 if(this.tabs && (panel = this.getPanel(panel))){
36095 this.tabs.unhideTab(panel.getEl().id);
36099 clearPanels : function(){
36100 while(this.panels.getCount() > 0){
36101 this.remove(this.panels.first());
36106 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36107 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36108 * @param {Boolean} preservePanel Overrides the config preservePanel option
36109 * @return {Roo.ContentPanel} The panel that was removed
36111 remove : function(panel, preservePanel)
36113 panel = this.getPanel(panel);
36118 this.fireEvent("beforeremove", this, panel, e);
36119 if(e.cancel === true){
36122 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36123 var panelId = panel.getId();
36124 this.panels.removeKey(panelId);
36126 document.body.appendChild(panel.getEl().dom);
36129 this.tabs.removeTab(panel.getEl().id);
36130 }else if (!preservePanel){
36131 this.bodyEl.dom.removeChild(panel.getEl().dom);
36133 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36134 var p = this.panels.first();
36135 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36136 tempEl.appendChild(p.getEl().dom);
36137 this.bodyEl.update("");
36138 this.bodyEl.dom.appendChild(p.getEl().dom);
36140 this.updateTitle(p.getTitle());
36142 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36143 this.setActivePanel(p);
36145 panel.setRegion(null);
36146 if(this.activePanel == panel){
36147 this.activePanel = null;
36149 if(this.config.autoDestroy !== false && preservePanel !== true){
36150 try{panel.destroy();}catch(e){}
36152 this.fireEvent("panelremoved", this, panel);
36157 * Returns the TabPanel component used by this region
36158 * @return {Roo.TabPanel}
36160 getTabs : function(){
36164 createTool : function(parentEl, className){
36165 var btn = Roo.DomHelper.append(parentEl, {
36167 cls: "x-layout-tools-button",
36170 cls: "roo-layout-tools-button-inner " + className,
36174 btn.addClassOnOver("roo-layout-tools-button-over");
36179 * Ext JS Library 1.1.1
36180 * Copyright(c) 2006-2007, Ext JS, LLC.
36182 * Originally Released Under LGPL - original licence link has changed is not relivant.
36185 * <script type="text/javascript">
36191 * @class Roo.SplitLayoutRegion
36192 * @extends Roo.LayoutRegion
36193 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36195 Roo.bootstrap.layout.Split = function(config){
36196 this.cursor = config.cursor;
36197 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36200 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36202 splitTip : "Drag to resize.",
36203 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36204 useSplitTips : false,
36206 applyConfig : function(config){
36207 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36210 onRender : function(ctr,pos) {
36212 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36213 if(!this.config.split){
36218 var splitEl = Roo.DomHelper.append(ctr.dom, {
36220 id: this.el.id + "-split",
36221 cls: "roo-layout-split roo-layout-split-"+this.position,
36224 /** The SplitBar for this region
36225 * @type Roo.SplitBar */
36226 // does not exist yet...
36227 Roo.log([this.position, this.orientation]);
36229 this.split = new Roo.bootstrap.SplitBar({
36230 dragElement : splitEl,
36231 resizingElement: this.el,
36232 orientation : this.orientation
36235 this.split.on("moved", this.onSplitMove, this);
36236 this.split.useShim = this.config.useShim === true;
36237 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36238 if(this.useSplitTips){
36239 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36241 //if(config.collapsible){
36242 // this.split.el.on("dblclick", this.collapse, this);
36245 if(typeof this.config.minSize != "undefined"){
36246 this.split.minSize = this.config.minSize;
36248 if(typeof this.config.maxSize != "undefined"){
36249 this.split.maxSize = this.config.maxSize;
36251 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36252 this.hideSplitter();
36257 getHMaxSize : function(){
36258 var cmax = this.config.maxSize || 10000;
36259 var center = this.mgr.getRegion("center");
36260 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36263 getVMaxSize : function(){
36264 var cmax = this.config.maxSize || 10000;
36265 var center = this.mgr.getRegion("center");
36266 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36269 onSplitMove : function(split, newSize){
36270 this.fireEvent("resized", this, newSize);
36274 * Returns the {@link Roo.SplitBar} for this region.
36275 * @return {Roo.SplitBar}
36277 getSplitBar : function(){
36282 this.hideSplitter();
36283 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36286 hideSplitter : function(){
36288 this.split.el.setLocation(-2000,-2000);
36289 this.split.el.hide();
36295 this.split.el.show();
36297 Roo.bootstrap.layout.Split.superclass.show.call(this);
36300 beforeSlide: function(){
36301 if(Roo.isGecko){// firefox overflow auto bug workaround
36302 this.bodyEl.clip();
36304 this.tabs.bodyEl.clip();
36306 if(this.activePanel){
36307 this.activePanel.getEl().clip();
36309 if(this.activePanel.beforeSlide){
36310 this.activePanel.beforeSlide();
36316 afterSlide : function(){
36317 if(Roo.isGecko){// firefox overflow auto bug workaround
36318 this.bodyEl.unclip();
36320 this.tabs.bodyEl.unclip();
36322 if(this.activePanel){
36323 this.activePanel.getEl().unclip();
36324 if(this.activePanel.afterSlide){
36325 this.activePanel.afterSlide();
36331 initAutoHide : function(){
36332 if(this.autoHide !== false){
36333 if(!this.autoHideHd){
36334 var st = new Roo.util.DelayedTask(this.slideIn, this);
36335 this.autoHideHd = {
36336 "mouseout": function(e){
36337 if(!e.within(this.el, true)){
36341 "mouseover" : function(e){
36347 this.el.on(this.autoHideHd);
36351 clearAutoHide : function(){
36352 if(this.autoHide !== false){
36353 this.el.un("mouseout", this.autoHideHd.mouseout);
36354 this.el.un("mouseover", this.autoHideHd.mouseover);
36358 clearMonitor : function(){
36359 Roo.get(document).un("click", this.slideInIf, this);
36362 // these names are backwards but not changed for compat
36363 slideOut : function(){
36364 if(this.isSlid || this.el.hasActiveFx()){
36367 this.isSlid = true;
36368 if(this.collapseBtn){
36369 this.collapseBtn.hide();
36371 this.closeBtnState = this.closeBtn.getStyle('display');
36372 this.closeBtn.hide();
36374 this.stickBtn.show();
36377 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36378 this.beforeSlide();
36379 this.el.setStyle("z-index", 10001);
36380 this.el.slideIn(this.getSlideAnchor(), {
36381 callback: function(){
36383 this.initAutoHide();
36384 Roo.get(document).on("click", this.slideInIf, this);
36385 this.fireEvent("slideshow", this);
36392 afterSlideIn : function(){
36393 this.clearAutoHide();
36394 this.isSlid = false;
36395 this.clearMonitor();
36396 this.el.setStyle("z-index", "");
36397 if(this.collapseBtn){
36398 this.collapseBtn.show();
36400 this.closeBtn.setStyle('display', this.closeBtnState);
36402 this.stickBtn.hide();
36404 this.fireEvent("slidehide", this);
36407 slideIn : function(cb){
36408 if(!this.isSlid || this.el.hasActiveFx()){
36412 this.isSlid = false;
36413 this.beforeSlide();
36414 this.el.slideOut(this.getSlideAnchor(), {
36415 callback: function(){
36416 this.el.setLeftTop(-10000, -10000);
36418 this.afterSlideIn();
36426 slideInIf : function(e){
36427 if(!e.within(this.el)){
36432 animateCollapse : function(){
36433 this.beforeSlide();
36434 this.el.setStyle("z-index", 20000);
36435 var anchor = this.getSlideAnchor();
36436 this.el.slideOut(anchor, {
36437 callback : function(){
36438 this.el.setStyle("z-index", "");
36439 this.collapsedEl.slideIn(anchor, {duration:.3});
36441 this.el.setLocation(-10000,-10000);
36443 this.fireEvent("collapsed", this);
36450 animateExpand : function(){
36451 this.beforeSlide();
36452 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36453 this.el.setStyle("z-index", 20000);
36454 this.collapsedEl.hide({
36457 this.el.slideIn(this.getSlideAnchor(), {
36458 callback : function(){
36459 this.el.setStyle("z-index", "");
36462 this.split.el.show();
36464 this.fireEvent("invalidated", this);
36465 this.fireEvent("expanded", this);
36493 getAnchor : function(){
36494 return this.anchors[this.position];
36497 getCollapseAnchor : function(){
36498 return this.canchors[this.position];
36501 getSlideAnchor : function(){
36502 return this.sanchors[this.position];
36505 getAlignAdj : function(){
36506 var cm = this.cmargins;
36507 switch(this.position){
36523 getExpandAdj : function(){
36524 var c = this.collapsedEl, cm = this.cmargins;
36525 switch(this.position){
36527 return [-(cm.right+c.getWidth()+cm.left), 0];
36530 return [cm.right+c.getWidth()+cm.left, 0];
36533 return [0, -(cm.top+cm.bottom+c.getHeight())];
36536 return [0, cm.top+cm.bottom+c.getHeight()];
36542 * Ext JS Library 1.1.1
36543 * Copyright(c) 2006-2007, Ext JS, LLC.
36545 * Originally Released Under LGPL - original licence link has changed is not relivant.
36548 * <script type="text/javascript">
36551 * These classes are private internal classes
36553 Roo.bootstrap.layout.Center = function(config){
36554 config.region = "center";
36555 Roo.bootstrap.layout.Region.call(this, config);
36556 this.visible = true;
36557 this.minWidth = config.minWidth || 20;
36558 this.minHeight = config.minHeight || 20;
36561 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36563 // center panel can't be hidden
36567 // center panel can't be hidden
36570 getMinWidth: function(){
36571 return this.minWidth;
36574 getMinHeight: function(){
36575 return this.minHeight;
36588 Roo.bootstrap.layout.North = function(config)
36590 config.region = 'north';
36591 config.cursor = 'n-resize';
36593 Roo.bootstrap.layout.Split.call(this, config);
36597 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36598 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36599 this.split.el.addClass("roo-layout-split-v");
36601 var size = config.initialSize || config.height;
36602 if(typeof size != "undefined"){
36603 this.el.setHeight(size);
36606 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36608 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36612 getBox : function(){
36613 if(this.collapsed){
36614 return this.collapsedEl.getBox();
36616 var box = this.el.getBox();
36618 box.height += this.split.el.getHeight();
36623 updateBox : function(box){
36624 if(this.split && !this.collapsed){
36625 box.height -= this.split.el.getHeight();
36626 this.split.el.setLeft(box.x);
36627 this.split.el.setTop(box.y+box.height);
36628 this.split.el.setWidth(box.width);
36630 if(this.collapsed){
36631 this.updateBody(box.width, null);
36633 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36641 Roo.bootstrap.layout.South = function(config){
36642 config.region = 'south';
36643 config.cursor = 's-resize';
36644 Roo.bootstrap.layout.Split.call(this, config);
36646 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36647 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36648 this.split.el.addClass("roo-layout-split-v");
36650 var size = config.initialSize || config.height;
36651 if(typeof size != "undefined"){
36652 this.el.setHeight(size);
36656 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36657 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36658 getBox : function(){
36659 if(this.collapsed){
36660 return this.collapsedEl.getBox();
36662 var box = this.el.getBox();
36664 var sh = this.split.el.getHeight();
36671 updateBox : function(box){
36672 if(this.split && !this.collapsed){
36673 var sh = this.split.el.getHeight();
36676 this.split.el.setLeft(box.x);
36677 this.split.el.setTop(box.y-sh);
36678 this.split.el.setWidth(box.width);
36680 if(this.collapsed){
36681 this.updateBody(box.width, null);
36683 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36687 Roo.bootstrap.layout.East = function(config){
36688 config.region = "east";
36689 config.cursor = "e-resize";
36690 Roo.bootstrap.layout.Split.call(this, config);
36692 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36693 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36694 this.split.el.addClass("roo-layout-split-h");
36696 var size = config.initialSize || config.width;
36697 if(typeof size != "undefined"){
36698 this.el.setWidth(size);
36701 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36702 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36703 getBox : function(){
36704 if(this.collapsed){
36705 return this.collapsedEl.getBox();
36707 var box = this.el.getBox();
36709 var sw = this.split.el.getWidth();
36716 updateBox : function(box){
36717 if(this.split && !this.collapsed){
36718 var sw = this.split.el.getWidth();
36720 this.split.el.setLeft(box.x);
36721 this.split.el.setTop(box.y);
36722 this.split.el.setHeight(box.height);
36725 if(this.collapsed){
36726 this.updateBody(null, box.height);
36728 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36732 Roo.bootstrap.layout.West = function(config){
36733 config.region = "west";
36734 config.cursor = "w-resize";
36736 Roo.bootstrap.layout.Split.call(this, config);
36738 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36739 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36740 this.split.el.addClass("roo-layout-split-h");
36744 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36745 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36747 onRender: function(ctr, pos)
36749 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36750 var size = this.config.initialSize || this.config.width;
36751 if(typeof size != "undefined"){
36752 this.el.setWidth(size);
36756 getBox : function(){
36757 if(this.collapsed){
36758 return this.collapsedEl.getBox();
36760 var box = this.el.getBox();
36762 box.width += this.split.el.getWidth();
36767 updateBox : function(box){
36768 if(this.split && !this.collapsed){
36769 var sw = this.split.el.getWidth();
36771 this.split.el.setLeft(box.x+box.width);
36772 this.split.el.setTop(box.y);
36773 this.split.el.setHeight(box.height);
36775 if(this.collapsed){
36776 this.updateBody(null, box.height);
36778 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36781 Roo.namespace("Roo.bootstrap.panel");/*
36783 * Ext JS Library 1.1.1
36784 * Copyright(c) 2006-2007, Ext JS, LLC.
36786 * Originally Released Under LGPL - original licence link has changed is not relivant.
36789 * <script type="text/javascript">
36792 * @class Roo.ContentPanel
36793 * @extends Roo.util.Observable
36794 * A basic ContentPanel element.
36795 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36796 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36797 * @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
36798 * @cfg {Boolean} closable True if the panel can be closed/removed
36799 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36800 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36801 * @cfg {Toolbar} toolbar A toolbar for this panel
36802 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36803 * @cfg {String} title The title for this panel
36804 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36805 * @cfg {String} url Calls {@link #setUrl} with this value
36806 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36807 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36808 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36809 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36810 * @cfg {Boolean} badges render the badges
36813 * Create a new ContentPanel.
36814 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36815 * @param {String/Object} config A string to set only the title or a config object
36816 * @param {String} content (optional) Set the HTML content for this panel
36817 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36819 Roo.bootstrap.panel.Content = function( config){
36821 this.tpl = config.tpl || false;
36823 var el = config.el;
36824 var content = config.content;
36826 if(config.autoCreate){ // xtype is available if this is called from factory
36829 this.el = Roo.get(el);
36830 if(!this.el && config && config.autoCreate){
36831 if(typeof config.autoCreate == "object"){
36832 if(!config.autoCreate.id){
36833 config.autoCreate.id = config.id||el;
36835 this.el = Roo.DomHelper.append(document.body,
36836 config.autoCreate, true);
36838 var elcfg = { tag: "div",
36839 cls: "roo-layout-inactive-content",
36843 elcfg.html = config.html;
36847 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36850 this.closable = false;
36851 this.loaded = false;
36852 this.active = false;
36855 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36857 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36859 this.wrapEl = this.el; //this.el.wrap();
36861 if (config.toolbar.items) {
36862 ti = config.toolbar.items ;
36863 delete config.toolbar.items ;
36867 this.toolbar.render(this.wrapEl, 'before');
36868 for(var i =0;i < ti.length;i++) {
36869 // Roo.log(['add child', items[i]]);
36870 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36872 this.toolbar.items = nitems;
36873 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36874 delete config.toolbar;
36878 // xtype created footer. - not sure if will work as we normally have to render first..
36879 if (this.footer && !this.footer.el && this.footer.xtype) {
36880 if (!this.wrapEl) {
36881 this.wrapEl = this.el.wrap();
36884 this.footer.container = this.wrapEl.createChild();
36886 this.footer = Roo.factory(this.footer, Roo);
36891 if(typeof config == "string"){
36892 this.title = config;
36894 Roo.apply(this, config);
36898 this.resizeEl = Roo.get(this.resizeEl, true);
36900 this.resizeEl = this.el;
36902 // handle view.xtype
36910 * Fires when this panel is activated.
36911 * @param {Roo.ContentPanel} this
36915 * @event deactivate
36916 * Fires when this panel is activated.
36917 * @param {Roo.ContentPanel} this
36919 "deactivate" : true,
36923 * Fires when this panel is resized if fitToFrame is true.
36924 * @param {Roo.ContentPanel} this
36925 * @param {Number} width The width after any component adjustments
36926 * @param {Number} height The height after any component adjustments
36932 * Fires when this tab is created
36933 * @param {Roo.ContentPanel} this
36944 if(this.autoScroll){
36945 this.resizeEl.setStyle("overflow", "auto");
36947 // fix randome scrolling
36948 //this.el.on('scroll', function() {
36949 // Roo.log('fix random scolling');
36950 // this.scrollTo('top',0);
36953 content = content || this.content;
36955 this.setContent(content);
36957 if(config && config.url){
36958 this.setUrl(this.url, this.params, this.loadOnce);
36963 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36965 if (this.view && typeof(this.view.xtype) != 'undefined') {
36966 this.view.el = this.el.appendChild(document.createElement("div"));
36967 this.view = Roo.factory(this.view);
36968 this.view.render && this.view.render(false, '');
36972 this.fireEvent('render', this);
36975 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36979 setRegion : function(region){
36980 this.region = region;
36981 this.setActiveClass(region && !this.background);
36985 setActiveClass: function(state)
36988 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36989 this.el.setStyle('position','relative');
36991 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36992 this.el.setStyle('position', 'absolute');
36997 * Returns the toolbar for this Panel if one was configured.
36998 * @return {Roo.Toolbar}
37000 getToolbar : function(){
37001 return this.toolbar;
37004 setActiveState : function(active)
37006 this.active = active;
37007 this.setActiveClass(active);
37009 if(this.fireEvent("deactivate", this) === false){
37014 this.fireEvent("activate", this);
37018 * Updates this panel's element
37019 * @param {String} content The new content
37020 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37022 setContent : function(content, loadScripts){
37023 this.el.update(content, loadScripts);
37026 ignoreResize : function(w, h){
37027 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37030 this.lastSize = {width: w, height: h};
37035 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37036 * @return {Roo.UpdateManager} The UpdateManager
37038 getUpdateManager : function(){
37039 return this.el.getUpdateManager();
37042 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37043 * @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:
37046 url: "your-url.php",
37047 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37048 callback: yourFunction,
37049 scope: yourObject, //(optional scope)
37052 text: "Loading...",
37057 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37058 * 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.
37059 * @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}
37060 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37061 * @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.
37062 * @return {Roo.ContentPanel} this
37065 var um = this.el.getUpdateManager();
37066 um.update.apply(um, arguments);
37072 * 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.
37073 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37074 * @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)
37075 * @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)
37076 * @return {Roo.UpdateManager} The UpdateManager
37078 setUrl : function(url, params, loadOnce){
37079 if(this.refreshDelegate){
37080 this.removeListener("activate", this.refreshDelegate);
37082 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37083 this.on("activate", this.refreshDelegate);
37084 return this.el.getUpdateManager();
37087 _handleRefresh : function(url, params, loadOnce){
37088 if(!loadOnce || !this.loaded){
37089 var updater = this.el.getUpdateManager();
37090 updater.update(url, params, this._setLoaded.createDelegate(this));
37094 _setLoaded : function(){
37095 this.loaded = true;
37099 * Returns this panel's id
37102 getId : function(){
37107 * Returns this panel's element - used by regiosn to add.
37108 * @return {Roo.Element}
37110 getEl : function(){
37111 return this.wrapEl || this.el;
37116 adjustForComponents : function(width, height)
37118 //Roo.log('adjustForComponents ');
37119 if(this.resizeEl != this.el){
37120 width -= this.el.getFrameWidth('lr');
37121 height -= this.el.getFrameWidth('tb');
37124 var te = this.toolbar.getEl();
37125 te.setWidth(width);
37126 height -= te.getHeight();
37129 var te = this.footer.getEl();
37130 te.setWidth(width);
37131 height -= te.getHeight();
37135 if(this.adjustments){
37136 width += this.adjustments[0];
37137 height += this.adjustments[1];
37139 return {"width": width, "height": height};
37142 setSize : function(width, height){
37143 if(this.fitToFrame && !this.ignoreResize(width, height)){
37144 if(this.fitContainer && this.resizeEl != this.el){
37145 this.el.setSize(width, height);
37147 var size = this.adjustForComponents(width, height);
37148 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37149 this.fireEvent('resize', this, size.width, size.height);
37154 * Returns this panel's title
37157 getTitle : function(){
37159 if (typeof(this.title) != 'object') {
37164 for (var k in this.title) {
37165 if (!this.title.hasOwnProperty(k)) {
37169 if (k.indexOf('-') >= 0) {
37170 var s = k.split('-');
37171 for (var i = 0; i<s.length; i++) {
37172 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37175 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37182 * Set this panel's title
37183 * @param {String} title
37185 setTitle : function(title){
37186 this.title = title;
37188 this.region.updatePanelTitle(this, title);
37193 * Returns true is this panel was configured to be closable
37194 * @return {Boolean}
37196 isClosable : function(){
37197 return this.closable;
37200 beforeSlide : function(){
37202 this.resizeEl.clip();
37205 afterSlide : function(){
37207 this.resizeEl.unclip();
37211 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37212 * Will fail silently if the {@link #setUrl} method has not been called.
37213 * This does not activate the panel, just updates its content.
37215 refresh : function(){
37216 if(this.refreshDelegate){
37217 this.loaded = false;
37218 this.refreshDelegate();
37223 * Destroys this panel
37225 destroy : function(){
37226 this.el.removeAllListeners();
37227 var tempEl = document.createElement("span");
37228 tempEl.appendChild(this.el.dom);
37229 tempEl.innerHTML = "";
37235 * form - if the content panel contains a form - this is a reference to it.
37236 * @type {Roo.form.Form}
37240 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37241 * This contains a reference to it.
37247 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37257 * @param {Object} cfg Xtype definition of item to add.
37261 getChildContainer: function () {
37262 return this.getEl();
37267 var ret = new Roo.factory(cfg);
37272 if (cfg.xtype.match(/^Form$/)) {
37275 //if (this.footer) {
37276 // el = this.footer.container.insertSibling(false, 'before');
37278 el = this.el.createChild();
37281 this.form = new Roo.form.Form(cfg);
37284 if ( this.form.allItems.length) {
37285 this.form.render(el.dom);
37289 // should only have one of theses..
37290 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37291 // views.. should not be just added - used named prop 'view''
37293 cfg.el = this.el.appendChild(document.createElement("div"));
37296 var ret = new Roo.factory(cfg);
37298 ret.render && ret.render(false, ''); // render blank..
37308 * @class Roo.bootstrap.panel.Grid
37309 * @extends Roo.bootstrap.panel.Content
37311 * Create a new GridPanel.
37312 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37313 * @param {Object} config A the config object
37319 Roo.bootstrap.panel.Grid = function(config)
37323 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37324 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37326 config.el = this.wrapper;
37327 //this.el = this.wrapper;
37329 if (config.container) {
37330 // ctor'ed from a Border/panel.grid
37333 this.wrapper.setStyle("overflow", "hidden");
37334 this.wrapper.addClass('roo-grid-container');
37339 if(config.toolbar){
37340 var tool_el = this.wrapper.createChild();
37341 this.toolbar = Roo.factory(config.toolbar);
37343 if (config.toolbar.items) {
37344 ti = config.toolbar.items ;
37345 delete config.toolbar.items ;
37349 this.toolbar.render(tool_el);
37350 for(var i =0;i < ti.length;i++) {
37351 // Roo.log(['add child', items[i]]);
37352 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37354 this.toolbar.items = nitems;
37356 delete config.toolbar;
37359 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37360 config.grid.scrollBody = true;;
37361 config.grid.monitorWindowResize = false; // turn off autosizing
37362 config.grid.autoHeight = false;
37363 config.grid.autoWidth = false;
37365 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37367 if (config.background) {
37368 // render grid on panel activation (if panel background)
37369 this.on('activate', function(gp) {
37370 if (!gp.grid.rendered) {
37371 gp.grid.render(this.wrapper);
37372 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37377 this.grid.render(this.wrapper);
37378 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37381 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37382 // ??? needed ??? config.el = this.wrapper;
37387 // xtype created footer. - not sure if will work as we normally have to render first..
37388 if (this.footer && !this.footer.el && this.footer.xtype) {
37390 var ctr = this.grid.getView().getFooterPanel(true);
37391 this.footer.dataSource = this.grid.dataSource;
37392 this.footer = Roo.factory(this.footer, Roo);
37393 this.footer.render(ctr);
37403 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37404 getId : function(){
37405 return this.grid.id;
37409 * Returns the grid for this panel
37410 * @return {Roo.bootstrap.Table}
37412 getGrid : function(){
37416 setSize : function(width, height){
37417 if(!this.ignoreResize(width, height)){
37418 var grid = this.grid;
37419 var size = this.adjustForComponents(width, height);
37420 var gridel = grid.getGridEl();
37421 gridel.setSize(size.width, size.height);
37423 var thd = grid.getGridEl().select('thead',true).first();
37424 var tbd = grid.getGridEl().select('tbody', true).first();
37426 tbd.setSize(width, height - thd.getHeight());
37435 beforeSlide : function(){
37436 this.grid.getView().scroller.clip();
37439 afterSlide : function(){
37440 this.grid.getView().scroller.unclip();
37443 destroy : function(){
37444 this.grid.destroy();
37446 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37451 * @class Roo.bootstrap.panel.Nest
37452 * @extends Roo.bootstrap.panel.Content
37454 * Create a new Panel, that can contain a layout.Border.
37457 * @param {Roo.BorderLayout} layout The layout for this panel
37458 * @param {String/Object} config A string to set only the title or a config object
37460 Roo.bootstrap.panel.Nest = function(config)
37462 // construct with only one argument..
37463 /* FIXME - implement nicer consturctors
37464 if (layout.layout) {
37466 layout = config.layout;
37467 delete config.layout;
37469 if (layout.xtype && !layout.getEl) {
37470 // then layout needs constructing..
37471 layout = Roo.factory(layout, Roo);
37475 config.el = config.layout.getEl();
37477 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37479 config.layout.monitorWindowResize = false; // turn off autosizing
37480 this.layout = config.layout;
37481 this.layout.getEl().addClass("roo-layout-nested-layout");
37488 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37490 setSize : function(width, height){
37491 if(!this.ignoreResize(width, height)){
37492 var size = this.adjustForComponents(width, height);
37493 var el = this.layout.getEl();
37494 if (size.height < 1) {
37495 el.setWidth(size.width);
37497 el.setSize(size.width, size.height);
37499 var touch = el.dom.offsetWidth;
37500 this.layout.layout();
37501 // ie requires a double layout on the first pass
37502 if(Roo.isIE && !this.initialized){
37503 this.initialized = true;
37504 this.layout.layout();
37509 // activate all subpanels if not currently active..
37511 setActiveState : function(active){
37512 this.active = active;
37513 this.setActiveClass(active);
37516 this.fireEvent("deactivate", this);
37520 this.fireEvent("activate", this);
37521 // not sure if this should happen before or after..
37522 if (!this.layout) {
37523 return; // should not happen..
37526 for (var r in this.layout.regions) {
37527 reg = this.layout.getRegion(r);
37528 if (reg.getActivePanel()) {
37529 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37530 reg.setActivePanel(reg.getActivePanel());
37533 if (!reg.panels.length) {
37536 reg.showPanel(reg.getPanel(0));
37545 * Returns the nested BorderLayout for this panel
37546 * @return {Roo.BorderLayout}
37548 getLayout : function(){
37549 return this.layout;
37553 * Adds a xtype elements to the layout of the nested panel
37557 xtype : 'ContentPanel',
37564 xtype : 'NestedLayoutPanel',
37570 items : [ ... list of content panels or nested layout panels.. ]
37574 * @param {Object} cfg Xtype definition of item to add.
37576 addxtype : function(cfg) {
37577 return this.layout.addxtype(cfg);
37582 * Ext JS Library 1.1.1
37583 * Copyright(c) 2006-2007, Ext JS, LLC.
37585 * Originally Released Under LGPL - original licence link has changed is not relivant.
37588 * <script type="text/javascript">
37591 * @class Roo.TabPanel
37592 * @extends Roo.util.Observable
37593 * A lightweight tab container.
37597 // basic tabs 1, built from existing content
37598 var tabs = new Roo.TabPanel("tabs1");
37599 tabs.addTab("script", "View Script");
37600 tabs.addTab("markup", "View Markup");
37601 tabs.activate("script");
37603 // more advanced tabs, built from javascript
37604 var jtabs = new Roo.TabPanel("jtabs");
37605 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37607 // set up the UpdateManager
37608 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37609 var updater = tab2.getUpdateManager();
37610 updater.setDefaultUrl("ajax1.htm");
37611 tab2.on('activate', updater.refresh, updater, true);
37613 // Use setUrl for Ajax loading
37614 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37615 tab3.setUrl("ajax2.htm", null, true);
37618 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37621 jtabs.activate("jtabs-1");
37624 * Create a new TabPanel.
37625 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37626 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37628 Roo.bootstrap.panel.Tabs = function(config){
37630 * The container element for this TabPanel.
37631 * @type Roo.Element
37633 this.el = Roo.get(config.el);
37636 if(typeof config == "boolean"){
37637 this.tabPosition = config ? "bottom" : "top";
37639 Roo.apply(this, config);
37643 if(this.tabPosition == "bottom"){
37644 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37645 this.el.addClass("roo-tabs-bottom");
37647 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37648 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37649 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37651 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37653 if(this.tabPosition != "bottom"){
37654 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37655 * @type Roo.Element
37657 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37658 this.el.addClass("roo-tabs-top");
37662 this.bodyEl.setStyle("position", "relative");
37664 this.active = null;
37665 this.activateDelegate = this.activate.createDelegate(this);
37670 * Fires when the active tab changes
37671 * @param {Roo.TabPanel} this
37672 * @param {Roo.TabPanelItem} activePanel The new active tab
37676 * @event beforetabchange
37677 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37678 * @param {Roo.TabPanel} this
37679 * @param {Object} e Set cancel to true on this object to cancel the tab change
37680 * @param {Roo.TabPanelItem} tab The tab being changed to
37682 "beforetabchange" : true
37685 Roo.EventManager.onWindowResize(this.onResize, this);
37686 this.cpad = this.el.getPadding("lr");
37687 this.hiddenCount = 0;
37690 // toolbar on the tabbar support...
37691 if (this.toolbar) {
37692 alert("no toolbar support yet");
37693 this.toolbar = false;
37695 var tcfg = this.toolbar;
37696 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37697 this.toolbar = new Roo.Toolbar(tcfg);
37698 if (Roo.isSafari) {
37699 var tbl = tcfg.container.child('table', true);
37700 tbl.setAttribute('width', '100%');
37708 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37711 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37713 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37715 tabPosition : "top",
37717 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37719 currentTabWidth : 0,
37721 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37725 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37729 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37731 preferredTabWidth : 175,
37733 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37735 resizeTabs : false,
37737 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37739 monitorResize : true,
37741 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37746 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37747 * @param {String} id The id of the div to use <b>or create</b>
37748 * @param {String} text The text for the tab
37749 * @param {String} content (optional) Content to put in the TabPanelItem body
37750 * @param {Boolean} closable (optional) True to create a close icon on the tab
37751 * @return {Roo.TabPanelItem} The created TabPanelItem
37753 addTab : function(id, text, content, closable, tpl)
37755 var item = new Roo.bootstrap.panel.TabItem({
37759 closable : closable,
37762 this.addTabItem(item);
37764 item.setContent(content);
37770 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37771 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37772 * @return {Roo.TabPanelItem}
37774 getTab : function(id){
37775 return this.items[id];
37779 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37780 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37782 hideTab : function(id){
37783 var t = this.items[id];
37786 this.hiddenCount++;
37787 this.autoSizeTabs();
37792 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37793 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37795 unhideTab : function(id){
37796 var t = this.items[id];
37798 t.setHidden(false);
37799 this.hiddenCount--;
37800 this.autoSizeTabs();
37805 * Adds an existing {@link Roo.TabPanelItem}.
37806 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37808 addTabItem : function(item){
37809 this.items[item.id] = item;
37810 this.items.push(item);
37811 // if(this.resizeTabs){
37812 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37813 // this.autoSizeTabs();
37815 // item.autoSize();
37820 * Removes a {@link Roo.TabPanelItem}.
37821 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37823 removeTab : function(id){
37824 var items = this.items;
37825 var tab = items[id];
37826 if(!tab) { return; }
37827 var index = items.indexOf(tab);
37828 if(this.active == tab && items.length > 1){
37829 var newTab = this.getNextAvailable(index);
37834 this.stripEl.dom.removeChild(tab.pnode.dom);
37835 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37836 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37838 items.splice(index, 1);
37839 delete this.items[tab.id];
37840 tab.fireEvent("close", tab);
37841 tab.purgeListeners();
37842 this.autoSizeTabs();
37845 getNextAvailable : function(start){
37846 var items = this.items;
37848 // look for a next tab that will slide over to
37849 // replace the one being removed
37850 while(index < items.length){
37851 var item = items[++index];
37852 if(item && !item.isHidden()){
37856 // if one isn't found select the previous tab (on the left)
37859 var item = items[--index];
37860 if(item && !item.isHidden()){
37868 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37869 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37871 disableTab : function(id){
37872 var tab = this.items[id];
37873 if(tab && this.active != tab){
37879 * Enables a {@link Roo.TabPanelItem} that is disabled.
37880 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37882 enableTab : function(id){
37883 var tab = this.items[id];
37888 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37889 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37890 * @return {Roo.TabPanelItem} The TabPanelItem.
37892 activate : function(id){
37893 var tab = this.items[id];
37897 if(tab == this.active || tab.disabled){
37901 this.fireEvent("beforetabchange", this, e, tab);
37902 if(e.cancel !== true && !tab.disabled){
37904 this.active.hide();
37906 this.active = this.items[id];
37907 this.active.show();
37908 this.fireEvent("tabchange", this, this.active);
37914 * Gets the active {@link Roo.TabPanelItem}.
37915 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37917 getActiveTab : function(){
37918 return this.active;
37922 * Updates the tab body element to fit the height of the container element
37923 * for overflow scrolling
37924 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37926 syncHeight : function(targetHeight){
37927 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37928 var bm = this.bodyEl.getMargins();
37929 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37930 this.bodyEl.setHeight(newHeight);
37934 onResize : function(){
37935 if(this.monitorResize){
37936 this.autoSizeTabs();
37941 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37943 beginUpdate : function(){
37944 this.updating = true;
37948 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37950 endUpdate : function(){
37951 this.updating = false;
37952 this.autoSizeTabs();
37956 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37958 autoSizeTabs : function(){
37959 var count = this.items.length;
37960 var vcount = count - this.hiddenCount;
37961 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37964 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37965 var availWidth = Math.floor(w / vcount);
37966 var b = this.stripBody;
37967 if(b.getWidth() > w){
37968 var tabs = this.items;
37969 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37970 if(availWidth < this.minTabWidth){
37971 /*if(!this.sleft){ // incomplete scrolling code
37972 this.createScrollButtons();
37975 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37978 if(this.currentTabWidth < this.preferredTabWidth){
37979 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37985 * Returns the number of tabs in this TabPanel.
37988 getCount : function(){
37989 return this.items.length;
37993 * Resizes all the tabs to the passed width
37994 * @param {Number} The new width
37996 setTabWidth : function(width){
37997 this.currentTabWidth = width;
37998 for(var i = 0, len = this.items.length; i < len; i++) {
37999 if(!this.items[i].isHidden()) {
38000 this.items[i].setWidth(width);
38006 * Destroys this TabPanel
38007 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38009 destroy : function(removeEl){
38010 Roo.EventManager.removeResizeListener(this.onResize, this);
38011 for(var i = 0, len = this.items.length; i < len; i++){
38012 this.items[i].purgeListeners();
38014 if(removeEl === true){
38015 this.el.update("");
38020 createStrip : function(container)
38022 var strip = document.createElement("nav");
38023 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38024 container.appendChild(strip);
38028 createStripList : function(strip)
38030 // div wrapper for retard IE
38031 // returns the "tr" element.
38032 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38033 //'<div class="x-tabs-strip-wrap">'+
38034 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38035 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38036 return strip.firstChild; //.firstChild.firstChild.firstChild;
38038 createBody : function(container)
38040 var body = document.createElement("div");
38041 Roo.id(body, "tab-body");
38042 //Roo.fly(body).addClass("x-tabs-body");
38043 Roo.fly(body).addClass("tab-content");
38044 container.appendChild(body);
38047 createItemBody :function(bodyEl, id){
38048 var body = Roo.getDom(id);
38050 body = document.createElement("div");
38053 //Roo.fly(body).addClass("x-tabs-item-body");
38054 Roo.fly(body).addClass("tab-pane");
38055 bodyEl.insertBefore(body, bodyEl.firstChild);
38059 createStripElements : function(stripEl, text, closable, tpl)
38061 var td = document.createElement("li"); // was td..
38064 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38067 stripEl.appendChild(td);
38069 td.className = "x-tabs-closable";
38070 if(!this.closeTpl){
38071 this.closeTpl = new Roo.Template(
38072 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38073 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38074 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38077 var el = this.closeTpl.overwrite(td, {"text": text});
38078 var close = el.getElementsByTagName("div")[0];
38079 var inner = el.getElementsByTagName("em")[0];
38080 return {"el": el, "close": close, "inner": inner};
38083 // not sure what this is..
38084 // if(!this.tabTpl){
38085 //this.tabTpl = new Roo.Template(
38086 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38087 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38089 // this.tabTpl = new Roo.Template(
38090 // '<a href="#">' +
38091 // '<span unselectable="on"' +
38092 // (this.disableTooltips ? '' : ' title="{text}"') +
38093 // ' >{text}</span></a>'
38099 var template = tpl || this.tabTpl || false;
38103 template = new Roo.Template(
38105 '<span unselectable="on"' +
38106 (this.disableTooltips ? '' : ' title="{text}"') +
38107 ' >{text}</span></a>'
38111 switch (typeof(template)) {
38115 template = new Roo.Template(template);
38121 var el = template.overwrite(td, {"text": text});
38123 var inner = el.getElementsByTagName("span")[0];
38125 return {"el": el, "inner": inner};
38133 * @class Roo.TabPanelItem
38134 * @extends Roo.util.Observable
38135 * Represents an individual item (tab plus body) in a TabPanel.
38136 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38137 * @param {String} id The id of this TabPanelItem
38138 * @param {String} text The text for the tab of this TabPanelItem
38139 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38141 Roo.bootstrap.panel.TabItem = function(config){
38143 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38144 * @type Roo.TabPanel
38146 this.tabPanel = config.panel;
38148 * The id for this TabPanelItem
38151 this.id = config.id;
38153 this.disabled = false;
38155 this.text = config.text;
38157 this.loaded = false;
38158 this.closable = config.closable;
38161 * The body element for this TabPanelItem.
38162 * @type Roo.Element
38164 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38165 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38166 this.bodyEl.setStyle("display", "block");
38167 this.bodyEl.setStyle("zoom", "1");
38168 //this.hideAction();
38170 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38172 this.el = Roo.get(els.el);
38173 this.inner = Roo.get(els.inner, true);
38174 this.textEl = Roo.get(this.el.dom.firstChild, true);
38175 this.pnode = Roo.get(els.el.parentNode, true);
38176 // this.el.on("mousedown", this.onTabMouseDown, this);
38177 this.el.on("click", this.onTabClick, this);
38179 if(config.closable){
38180 var c = Roo.get(els.close, true);
38181 c.dom.title = this.closeText;
38182 c.addClassOnOver("close-over");
38183 c.on("click", this.closeClick, this);
38189 * Fires when this tab becomes the active tab.
38190 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38191 * @param {Roo.TabPanelItem} this
38195 * @event beforeclose
38196 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38197 * @param {Roo.TabPanelItem} this
38198 * @param {Object} e Set cancel to true on this object to cancel the close.
38200 "beforeclose": true,
38203 * Fires when this tab is closed.
38204 * @param {Roo.TabPanelItem} this
38208 * @event deactivate
38209 * Fires when this tab is no longer the active tab.
38210 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38211 * @param {Roo.TabPanelItem} this
38213 "deactivate" : true
38215 this.hidden = false;
38217 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38220 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38222 purgeListeners : function(){
38223 Roo.util.Observable.prototype.purgeListeners.call(this);
38224 this.el.removeAllListeners();
38227 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38230 this.pnode.addClass("active");
38233 this.tabPanel.stripWrap.repaint();
38235 this.fireEvent("activate", this.tabPanel, this);
38239 * Returns true if this tab is the active tab.
38240 * @return {Boolean}
38242 isActive : function(){
38243 return this.tabPanel.getActiveTab() == this;
38247 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38250 this.pnode.removeClass("active");
38252 this.fireEvent("deactivate", this.tabPanel, this);
38255 hideAction : function(){
38256 this.bodyEl.hide();
38257 this.bodyEl.setStyle("position", "absolute");
38258 this.bodyEl.setLeft("-20000px");
38259 this.bodyEl.setTop("-20000px");
38262 showAction : function(){
38263 this.bodyEl.setStyle("position", "relative");
38264 this.bodyEl.setTop("");
38265 this.bodyEl.setLeft("");
38266 this.bodyEl.show();
38270 * Set the tooltip for the tab.
38271 * @param {String} tooltip The tab's tooltip
38273 setTooltip : function(text){
38274 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38275 this.textEl.dom.qtip = text;
38276 this.textEl.dom.removeAttribute('title');
38278 this.textEl.dom.title = text;
38282 onTabClick : function(e){
38283 e.preventDefault();
38284 this.tabPanel.activate(this.id);
38287 onTabMouseDown : function(e){
38288 e.preventDefault();
38289 this.tabPanel.activate(this.id);
38292 getWidth : function(){
38293 return this.inner.getWidth();
38296 setWidth : function(width){
38297 var iwidth = width - this.pnode.getPadding("lr");
38298 this.inner.setWidth(iwidth);
38299 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38300 this.pnode.setWidth(width);
38304 * Show or hide the tab
38305 * @param {Boolean} hidden True to hide or false to show.
38307 setHidden : function(hidden){
38308 this.hidden = hidden;
38309 this.pnode.setStyle("display", hidden ? "none" : "");
38313 * Returns true if this tab is "hidden"
38314 * @return {Boolean}
38316 isHidden : function(){
38317 return this.hidden;
38321 * Returns the text for this tab
38324 getText : function(){
38328 autoSize : function(){
38329 //this.el.beginMeasure();
38330 this.textEl.setWidth(1);
38332 * #2804 [new] Tabs in Roojs
38333 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38335 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38336 //this.el.endMeasure();
38340 * Sets the text for the tab (Note: this also sets the tooltip text)
38341 * @param {String} text The tab's text and tooltip
38343 setText : function(text){
38345 this.textEl.update(text);
38346 this.setTooltip(text);
38347 //if(!this.tabPanel.resizeTabs){
38348 // this.autoSize();
38352 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38354 activate : function(){
38355 this.tabPanel.activate(this.id);
38359 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38361 disable : function(){
38362 if(this.tabPanel.active != this){
38363 this.disabled = true;
38364 this.pnode.addClass("disabled");
38369 * Enables this TabPanelItem if it was previously disabled.
38371 enable : function(){
38372 this.disabled = false;
38373 this.pnode.removeClass("disabled");
38377 * Sets the content for this TabPanelItem.
38378 * @param {String} content The content
38379 * @param {Boolean} loadScripts true to look for and load scripts
38381 setContent : function(content, loadScripts){
38382 this.bodyEl.update(content, loadScripts);
38386 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38387 * @return {Roo.UpdateManager} The UpdateManager
38389 getUpdateManager : function(){
38390 return this.bodyEl.getUpdateManager();
38394 * Set a URL to be used to load the content for this TabPanelItem.
38395 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38396 * @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)
38397 * @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)
38398 * @return {Roo.UpdateManager} The UpdateManager
38400 setUrl : function(url, params, loadOnce){
38401 if(this.refreshDelegate){
38402 this.un('activate', this.refreshDelegate);
38404 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38405 this.on("activate", this.refreshDelegate);
38406 return this.bodyEl.getUpdateManager();
38410 _handleRefresh : function(url, params, loadOnce){
38411 if(!loadOnce || !this.loaded){
38412 var updater = this.bodyEl.getUpdateManager();
38413 updater.update(url, params, this._setLoaded.createDelegate(this));
38418 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38419 * Will fail silently if the setUrl method has not been called.
38420 * This does not activate the panel, just updates its content.
38422 refresh : function(){
38423 if(this.refreshDelegate){
38424 this.loaded = false;
38425 this.refreshDelegate();
38430 _setLoaded : function(){
38431 this.loaded = true;
38435 closeClick : function(e){
38438 this.fireEvent("beforeclose", this, o);
38439 if(o.cancel !== true){
38440 this.tabPanel.removeTab(this.id);
38444 * The text displayed in the tooltip for the close icon.
38447 closeText : "Close this tab"
38450 * This script refer to:
38451 * Title: International Telephone Input
38452 * Author: Jack O'Connor
38453 * Code version: v12.1.12
38454 * Availability: https://github.com/jackocnr/intl-tel-input.git
38457 Roo.bootstrap.PhoneInputData = function() {
38460 "Afghanistan (افغانستان)",
38465 "Albania (Shqipëri)",
38470 "Algeria (الجزائر)",
38495 "Antigua and Barbuda",
38505 "Armenia (Հայաստան)",
38521 "Austria (Österreich)",
38526 "Azerbaijan (Azərbaycan)",
38536 "Bahrain (البحرين)",
38541 "Bangladesh (বাংলাদেশ)",
38551 "Belarus (Беларусь)",
38556 "Belgium (België)",
38586 "Bosnia and Herzegovina (Босна и Херцеговина)",
38601 "British Indian Ocean Territory",
38606 "British Virgin Islands",
38616 "Bulgaria (България)",
38626 "Burundi (Uburundi)",
38631 "Cambodia (កម្ពុជា)",
38636 "Cameroon (Cameroun)",
38645 ["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"]
38648 "Cape Verde (Kabu Verdi)",
38653 "Caribbean Netherlands",
38664 "Central African Republic (République centrafricaine)",
38684 "Christmas Island",
38690 "Cocos (Keeling) Islands",
38701 "Comoros (جزر القمر)",
38706 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38711 "Congo (Republic) (Congo-Brazzaville)",
38731 "Croatia (Hrvatska)",
38752 "Czech Republic (Česká republika)",
38757 "Denmark (Danmark)",
38772 "Dominican Republic (República Dominicana)",
38776 ["809", "829", "849"]
38794 "Equatorial Guinea (Guinea Ecuatorial)",
38814 "Falkland Islands (Islas Malvinas)",
38819 "Faroe Islands (Føroyar)",
38840 "French Guiana (Guyane française)",
38845 "French Polynesia (Polynésie française)",
38860 "Georgia (საქართველო)",
38865 "Germany (Deutschland)",
38885 "Greenland (Kalaallit Nunaat)",
38922 "Guinea-Bissau (Guiné Bissau)",
38947 "Hungary (Magyarország)",
38952 "Iceland (Ísland)",
38972 "Iraq (العراق)",
38988 "Israel (ישראל)",
39015 "Jordan (الأردن)",
39020 "Kazakhstan (Казахстан)",
39041 "Kuwait (الكويت)",
39046 "Kyrgyzstan (Кыргызстан)",
39056 "Latvia (Latvija)",
39061 "Lebanon (لبنان)",
39076 "Libya (ليبيا)",
39086 "Lithuania (Lietuva)",
39101 "Macedonia (FYROM) (Македонија)",
39106 "Madagascar (Madagasikara)",
39136 "Marshall Islands",
39146 "Mauritania (موريتانيا)",
39151 "Mauritius (Moris)",
39172 "Moldova (Republica Moldova)",
39182 "Mongolia (Монгол)",
39187 "Montenegro (Crna Gora)",
39197 "Morocco (المغرب)",
39203 "Mozambique (Moçambique)",
39208 "Myanmar (Burma) (မြန်မာ)",
39213 "Namibia (Namibië)",
39228 "Netherlands (Nederland)",
39233 "New Caledonia (Nouvelle-Calédonie)",
39268 "North Korea (조선 민주주의 인민 공화국)",
39273 "Northern Mariana Islands",
39289 "Pakistan (پاکستان)",
39299 "Palestine (فلسطين)",
39309 "Papua New Guinea",
39351 "Réunion (La Réunion)",
39357 "Romania (România)",
39373 "Saint Barthélemy",
39384 "Saint Kitts and Nevis",
39394 "Saint Martin (Saint-Martin (partie française))",
39400 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39405 "Saint Vincent and the Grenadines",
39420 "São Tomé and Príncipe (São Tomé e Príncipe)",
39425 "Saudi Arabia (المملكة العربية السعودية)",
39430 "Senegal (Sénégal)",
39460 "Slovakia (Slovensko)",
39465 "Slovenia (Slovenija)",
39475 "Somalia (Soomaaliya)",
39485 "South Korea (대한민국)",
39490 "South Sudan (جنوب السودان)",
39500 "Sri Lanka (ශ්රී ලංකාව)",
39505 "Sudan (السودان)",
39515 "Svalbard and Jan Mayen",
39526 "Sweden (Sverige)",
39531 "Switzerland (Schweiz)",
39536 "Syria (سوريا)",
39581 "Trinidad and Tobago",
39586 "Tunisia (تونس)",
39591 "Turkey (Türkiye)",
39601 "Turks and Caicos Islands",
39611 "U.S. Virgin Islands",
39621 "Ukraine (Україна)",
39626 "United Arab Emirates (الإمارات العربية المتحدة)",
39648 "Uzbekistan (Oʻzbekiston)",
39658 "Vatican City (Città del Vaticano)",
39669 "Vietnam (Việt Nam)",
39674 "Wallis and Futuna (Wallis-et-Futuna)",
39679 "Western Sahara (الصحراء الغربية)",
39685 "Yemen (اليمن)",
39709 * This script refer to:
39710 * Title: International Telephone Input
39711 * Author: Jack O'Connor
39712 * Code version: v12.1.12
39713 * Availability: https://github.com/jackocnr/intl-tel-input.git
39717 * @class Roo.bootstrap.PhoneInput
39718 * @extends Roo.bootstrap.TriggerField
39719 * An input with International dial-code selection
39721 * @cfg {String} defaultDialCode default '+852'
39722 * @cfg {Array} preferedCountries default []
39725 * Create a new PhoneInput.
39726 * @param {Object} config Configuration options
39729 Roo.bootstrap.PhoneInput = function(config) {
39730 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39733 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39735 listWidth: undefined,
39737 selectedClass: 'active',
39739 invalidClass : "has-warning",
39741 validClass: 'has-success',
39743 allowed: '0123456789',
39746 * @cfg {String} defaultDialCode The default dial code when initializing the input
39748 defaultDialCode: '+852',
39751 * @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
39753 preferedCountries: false,
39755 getAutoCreate : function()
39757 var data = Roo.bootstrap.PhoneInputData();
39758 var align = this.labelAlign || this.parentLabelAlign();
39761 this.allCountries = [];
39762 this.dialCodeMapping = [];
39764 for (var i = 0; i < data.length; i++) {
39766 this.allCountries[i] = {
39770 priority: c[3] || 0,
39771 areaCodes: c[4] || null
39773 this.dialCodeMapping[c[2]] = {
39776 priority: c[3] || 0,
39777 areaCodes: c[4] || null
39789 cls : 'form-control tel-input',
39790 autocomplete: 'new-password'
39793 var hiddenInput = {
39796 cls: 'hidden-tel-input'
39800 hiddenInput.name = this.name;
39803 if (this.disabled) {
39804 input.disabled = true;
39807 var flag_container = {
39824 cls: this.hasFeedback ? 'has-feedback' : '',
39830 cls: 'dial-code-holder',
39837 cls: 'roo-select2-container input-group',
39844 if (this.fieldLabel.length) {
39847 tooltip: 'This field is required'
39853 cls: 'control-label',
39859 html: this.fieldLabel
39862 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39868 if(this.indicatorpos == 'right') {
39869 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39876 if(align == 'left') {
39884 if(this.labelWidth > 12){
39885 label.style = "width: " + this.labelWidth + 'px';
39887 if(this.labelWidth < 13 && this.labelmd == 0){
39888 this.labelmd = this.labelWidth;
39890 if(this.labellg > 0){
39891 label.cls += ' col-lg-' + this.labellg;
39892 input.cls += ' col-lg-' + (12 - this.labellg);
39894 if(this.labelmd > 0){
39895 label.cls += ' col-md-' + this.labelmd;
39896 container.cls += ' col-md-' + (12 - this.labelmd);
39898 if(this.labelsm > 0){
39899 label.cls += ' col-sm-' + this.labelsm;
39900 container.cls += ' col-sm-' + (12 - this.labelsm);
39902 if(this.labelxs > 0){
39903 label.cls += ' col-xs-' + this.labelxs;
39904 container.cls += ' col-xs-' + (12 - this.labelxs);
39914 var settings = this;
39916 ['xs','sm','md','lg'].map(function(size){
39917 if (settings[size]) {
39918 cfg.cls += ' col-' + size + '-' + settings[size];
39922 this.store = new Roo.data.Store({
39923 proxy : new Roo.data.MemoryProxy({}),
39924 reader : new Roo.data.JsonReader({
39935 'name' : 'dialCode',
39939 'name' : 'priority',
39943 'name' : 'areaCodes',
39950 if(!this.preferedCountries) {
39951 this.preferedCountries = [
39958 var p = this.preferedCountries.reverse();
39961 for (var i = 0; i < p.length; i++) {
39962 for (var j = 0; j < this.allCountries.length; j++) {
39963 if(this.allCountries[j].iso2 == p[i]) {
39964 var t = this.allCountries[j];
39965 this.allCountries.splice(j,1);
39966 this.allCountries.unshift(t);
39972 this.store.proxy.data = {
39974 data: this.allCountries
39980 initEvents : function()
39983 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39985 this.indicator = this.indicatorEl();
39986 this.flag = this.flagEl();
39987 this.dialCodeHolder = this.dialCodeHolderEl();
39989 this.trigger = this.el.select('div.flag-box',true).first();
39990 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39995 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39996 _this.list.setWidth(lw);
39999 this.list.on('mouseover', this.onViewOver, this);
40000 this.list.on('mousemove', this.onViewMove, this);
40001 this.inputEl().on("keyup", this.onKeyUp, this);
40003 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40005 this.view = new Roo.View(this.list, this.tpl, {
40006 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40009 this.view.on('click', this.onViewClick, this);
40010 this.setValue(this.defaultDialCode);
40013 onTriggerClick : function(e)
40015 Roo.log('trigger click');
40020 if(this.isExpanded()){
40022 this.hasFocus = false;
40024 this.store.load({});
40025 this.hasFocus = true;
40030 isExpanded : function()
40032 return this.list.isVisible();
40035 collapse : function()
40037 if(!this.isExpanded()){
40041 Roo.get(document).un('mousedown', this.collapseIf, this);
40042 Roo.get(document).un('mousewheel', this.collapseIf, this);
40043 this.fireEvent('collapse', this);
40047 expand : function()
40051 if(this.isExpanded() || !this.hasFocus){
40055 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40056 this.list.setWidth(lw);
40059 this.restrictHeight();
40061 Roo.get(document).on('mousedown', this.collapseIf, this);
40062 Roo.get(document).on('mousewheel', this.collapseIf, this);
40064 this.fireEvent('expand', this);
40067 restrictHeight : function()
40069 this.list.alignTo(this.inputEl(), this.listAlign);
40070 this.list.alignTo(this.inputEl(), this.listAlign);
40073 onViewOver : function(e, t)
40075 if(this.inKeyMode){
40078 var item = this.view.findItemFromChild(t);
40081 var index = this.view.indexOf(item);
40082 this.select(index, false);
40087 onViewClick : function(view, doFocus, el, e)
40089 var index = this.view.getSelectedIndexes()[0];
40091 var r = this.store.getAt(index);
40094 this.onSelect(r, index);
40096 if(doFocus !== false && !this.blockFocus){
40097 this.inputEl().focus();
40101 onViewMove : function(e, t)
40103 this.inKeyMode = false;
40106 select : function(index, scrollIntoView)
40108 this.selectedIndex = index;
40109 this.view.select(index);
40110 if(scrollIntoView !== false){
40111 var el = this.view.getNode(index);
40113 this.list.scrollChildIntoView(el, false);
40118 createList : function()
40120 this.list = Roo.get(document.body).createChild({
40122 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40123 style: 'display:none'
40126 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40129 collapseIf : function(e)
40131 var in_combo = e.within(this.el);
40132 var in_list = e.within(this.list);
40133 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40135 if (in_combo || in_list || is_list) {
40141 onSelect : function(record, index)
40143 if(this.fireEvent('beforeselect', this, record, index) !== false){
40145 this.setFlagClass(record.data.iso2);
40146 this.setDialCode(record.data.dialCode);
40147 this.hasFocus = false;
40149 this.fireEvent('select', this, record, index);
40153 flagEl : function()
40155 var flag = this.el.select('div.flag',true).first();
40162 dialCodeHolderEl : function()
40164 var d = this.el.select('input.dial-code-holder',true).first();
40171 setDialCode : function(v)
40173 this.dialCodeHolder.dom.value = '+'+v;
40176 setFlagClass : function(n)
40178 this.flag.dom.className = 'flag '+n;
40181 getValue : function()
40183 var v = this.inputEl().getValue();
40184 if(this.dialCodeHolder) {
40185 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40190 setValue : function(v)
40192 var d = this.getDialCode(v);
40194 //invalid dial code
40195 if(v.length == 0 || !d || d.length == 0) {
40197 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40198 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40204 this.setFlagClass(this.dialCodeMapping[d].iso2);
40205 this.setDialCode(d);
40206 this.inputEl().dom.value = v.replace('+'+d,'');
40207 this.hiddenEl().dom.value = this.getValue();
40212 getDialCode : function(v)
40216 if (v.length == 0) {
40217 return this.dialCodeHolder.dom.value;
40221 if (v.charAt(0) != "+") {
40224 var numericChars = "";
40225 for (var i = 1; i < v.length; i++) {
40226 var c = v.charAt(i);
40229 if (this.dialCodeMapping[numericChars]) {
40230 dialCode = v.substr(1, i);
40232 if (numericChars.length == 4) {
40242 this.setValue(this.defaultDialCode);
40246 hiddenEl : function()
40248 return this.el.select('input.hidden-tel-input',true).first();
40251 onKeyUp : function(e){
40253 var k = e.getKey();
40254 var c = e.getCharCode();
40257 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40258 this.allowed.indexOf(String.fromCharCode(c)) === -1
40263 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40266 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40270 this.setValue(this.getValue());
40275 * @class Roo.bootstrap.MoneyField
40276 * @extends Roo.bootstrap.ComboBox
40277 * Bootstrap MoneyField class
40280 * Create a new MoneyField.
40281 * @param {Object} config Configuration options
40284 Roo.bootstrap.MoneyField = function(config) {
40286 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40290 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40293 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40295 allowDecimals : true,
40297 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40299 decimalSeparator : ".",
40301 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40303 decimalPrecision : 0,
40305 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40307 allowNegative : true,
40309 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40313 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40315 minValue : Number.NEGATIVE_INFINITY,
40317 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40319 maxValue : Number.MAX_VALUE,
40321 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40323 minText : "The minimum value for this field is {0}",
40325 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40327 maxText : "The maximum value for this field is {0}",
40329 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40330 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40332 nanText : "{0} is not a valid number",
40334 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40338 * @cfg {String} defaults currency of the MoneyField
40339 * value should be in lkey
40341 defaultCurrency : false,
40343 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40345 thousandsDelimiter : false,
40355 getAutoCreate : function()
40357 var align = this.labelAlign || this.parentLabelAlign();
40369 cls : 'form-control roo-money-amount-input',
40370 autocomplete: 'new-password'
40373 var hiddenInput = {
40377 cls: 'hidden-number-input'
40381 hiddenInput.name = this.name;
40384 if (this.disabled) {
40385 input.disabled = true;
40388 var clg = 12 - this.inputlg;
40389 var cmd = 12 - this.inputmd;
40390 var csm = 12 - this.inputsm;
40391 var cxs = 12 - this.inputxs;
40395 cls : 'row roo-money-field',
40399 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40403 cls: 'roo-select2-container input-group',
40407 cls : 'form-control roo-money-currency-input',
40408 autocomplete: 'new-password',
40410 name : this.currencyName
40414 cls : 'input-group-addon',
40428 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40432 cls: this.hasFeedback ? 'has-feedback' : '',
40443 if (this.fieldLabel.length) {
40446 tooltip: 'This field is required'
40452 cls: 'control-label',
40458 html: this.fieldLabel
40461 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40467 if(this.indicatorpos == 'right') {
40468 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40475 if(align == 'left') {
40483 if(this.labelWidth > 12){
40484 label.style = "width: " + this.labelWidth + 'px';
40486 if(this.labelWidth < 13 && this.labelmd == 0){
40487 this.labelmd = this.labelWidth;
40489 if(this.labellg > 0){
40490 label.cls += ' col-lg-' + this.labellg;
40491 input.cls += ' col-lg-' + (12 - this.labellg);
40493 if(this.labelmd > 0){
40494 label.cls += ' col-md-' + this.labelmd;
40495 container.cls += ' col-md-' + (12 - this.labelmd);
40497 if(this.labelsm > 0){
40498 label.cls += ' col-sm-' + this.labelsm;
40499 container.cls += ' col-sm-' + (12 - this.labelsm);
40501 if(this.labelxs > 0){
40502 label.cls += ' col-xs-' + this.labelxs;
40503 container.cls += ' col-xs-' + (12 - this.labelxs);
40514 var settings = this;
40516 ['xs','sm','md','lg'].map(function(size){
40517 if (settings[size]) {
40518 cfg.cls += ' col-' + size + '-' + settings[size];
40525 initEvents : function()
40527 this.indicator = this.indicatorEl();
40529 this.initCurrencyEvent();
40531 this.initNumberEvent();
40534 initCurrencyEvent : function()
40537 throw "can not find store for combo";
40540 this.store = Roo.factory(this.store, Roo.data);
40541 this.store.parent = this;
40545 this.triggerEl = this.el.select('.input-group-addon', true).first();
40547 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40552 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40553 _this.list.setWidth(lw);
40556 this.list.on('mouseover', this.onViewOver, this);
40557 this.list.on('mousemove', this.onViewMove, this);
40558 this.list.on('scroll', this.onViewScroll, this);
40561 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40564 this.view = new Roo.View(this.list, this.tpl, {
40565 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40568 this.view.on('click', this.onViewClick, this);
40570 this.store.on('beforeload', this.onBeforeLoad, this);
40571 this.store.on('load', this.onLoad, this);
40572 this.store.on('loadexception', this.onLoadException, this);
40574 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40575 "up" : function(e){
40576 this.inKeyMode = true;
40580 "down" : function(e){
40581 if(!this.isExpanded()){
40582 this.onTriggerClick();
40584 this.inKeyMode = true;
40589 "enter" : function(e){
40592 if(this.fireEvent("specialkey", this, e)){
40593 this.onViewClick(false);
40599 "esc" : function(e){
40603 "tab" : function(e){
40606 if(this.fireEvent("specialkey", this, e)){
40607 this.onViewClick(false);
40615 doRelay : function(foo, bar, hname){
40616 if(hname == 'down' || this.scope.isExpanded()){
40617 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40625 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40629 initNumberEvent : function(e)
40631 this.inputEl().on("keydown" , this.fireKey, this);
40632 this.inputEl().on("focus", this.onFocus, this);
40633 this.inputEl().on("blur", this.onBlur, this);
40635 this.inputEl().relayEvent('keyup', this);
40637 if(this.indicator){
40638 this.indicator.addClass('invisible');
40641 this.originalValue = this.getValue();
40643 if(this.validationEvent == 'keyup'){
40644 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40645 this.inputEl().on('keyup', this.filterValidation, this);
40647 else if(this.validationEvent !== false){
40648 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40651 if(this.selectOnFocus){
40652 this.on("focus", this.preFocus, this);
40655 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40656 this.inputEl().on("keypress", this.filterKeys, this);
40658 this.inputEl().relayEvent('keypress', this);
40661 var allowed = "0123456789";
40663 if(this.allowDecimals){
40664 allowed += this.decimalSeparator;
40667 if(this.allowNegative){
40671 if(this.thousandsDelimiter) {
40675 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40677 var keyPress = function(e){
40679 var k = e.getKey();
40681 var c = e.getCharCode();
40684 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40685 allowed.indexOf(String.fromCharCode(c)) === -1
40691 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40695 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40700 this.inputEl().on("keypress", keyPress, this);
40704 onTriggerClick : function(e)
40711 this.loadNext = false;
40713 if(this.isExpanded()){
40718 this.hasFocus = true;
40720 if(this.triggerAction == 'all') {
40721 this.doQuery(this.allQuery, true);
40725 this.doQuery(this.getRawValue());
40728 getCurrency : function()
40730 var v = this.currencyEl().getValue();
40735 restrictHeight : function()
40737 this.list.alignTo(this.currencyEl(), this.listAlign);
40738 this.list.alignTo(this.currencyEl(), this.listAlign);
40741 onViewClick : function(view, doFocus, el, e)
40743 var index = this.view.getSelectedIndexes()[0];
40745 var r = this.store.getAt(index);
40748 this.onSelect(r, index);
40752 onSelect : function(record, index){
40754 if(this.fireEvent('beforeselect', this, record, index) !== false){
40756 this.setFromCurrencyData(index > -1 ? record.data : false);
40760 this.fireEvent('select', this, record, index);
40764 setFromCurrencyData : function(o)
40768 this.lastCurrency = o;
40770 if (this.currencyField) {
40771 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40773 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40776 this.lastSelectionText = currency;
40778 //setting default currency
40779 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40780 this.setCurrency(this.defaultCurrency);
40784 this.setCurrency(currency);
40787 setFromData : function(o)
40791 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40793 this.setFromCurrencyData(c);
40798 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40800 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40803 this.setValue(value);
40807 setCurrency : function(v)
40809 this.currencyValue = v;
40812 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40817 setValue : function(v)
40819 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40825 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40827 this.inputEl().dom.value = (v == '') ? '' :
40828 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40830 if(!this.allowZero && v === '0') {
40831 this.hiddenEl().dom.value = '';
40832 this.inputEl().dom.value = '';
40839 getRawValue : function()
40841 var v = this.inputEl().getValue();
40846 getValue : function()
40848 return this.fixPrecision(this.parseValue(this.getRawValue()));
40851 parseValue : function(value)
40853 if(this.thousandsDelimiter) {
40855 r = new RegExp(",", "g");
40856 value = value.replace(r, "");
40859 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40860 return isNaN(value) ? '' : value;
40864 fixPrecision : function(value)
40866 if(this.thousandsDelimiter) {
40868 r = new RegExp(",", "g");
40869 value = value.replace(r, "");
40872 var nan = isNaN(value);
40874 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40875 return nan ? '' : value;
40877 return parseFloat(value).toFixed(this.decimalPrecision);
40880 decimalPrecisionFcn : function(v)
40882 return Math.floor(v);
40885 validateValue : function(value)
40887 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40891 var num = this.parseValue(value);
40894 this.markInvalid(String.format(this.nanText, value));
40898 if(num < this.minValue){
40899 this.markInvalid(String.format(this.minText, this.minValue));
40903 if(num > this.maxValue){
40904 this.markInvalid(String.format(this.maxText, this.maxValue));
40911 validate : function()
40913 if(this.disabled || this.allowBlank){
40918 var currency = this.getCurrency();
40920 if(this.validateValue(this.getRawValue()) && currency.length){
40925 this.markInvalid();
40929 getName: function()
40934 beforeBlur : function()
40940 var v = this.parseValue(this.getRawValue());
40947 onBlur : function()
40951 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40952 //this.el.removeClass(this.focusClass);
40955 this.hasFocus = false;
40957 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40961 var v = this.getValue();
40963 if(String(v) !== String(this.startValue)){
40964 this.fireEvent('change', this, v, this.startValue);
40967 this.fireEvent("blur", this);
40970 inputEl : function()
40972 return this.el.select('.roo-money-amount-input', true).first();
40975 currencyEl : function()
40977 return this.el.select('.roo-money-currency-input', true).first();
40980 hiddenEl : function()
40982 return this.el.select('input.hidden-number-input',true).first();