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(
2826 Roo.lib.Dom.getViewWidth(true),
2827 Roo.lib.Dom.getViewHeight(true)
2830 if (this.fitwindow) {
2832 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2838 if(this.max_width !== 0) {
2840 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2845 this.height <= Roo.lib.Dom.getViewportHeight(true) - 60 ?
2846 this.height : Roo.lib.Dom.getViewportHeight(true) - 60
2851 if(!this.fit_content) {
2852 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2856 var body_childs = this.bodyEl.dom.childNodes;
2857 // does not seem to give enough space...
2858 var full_height = 60 + this.headerEl.getHeight() + this.footerEl.getHeight();
2859 for(var i = 0; i < body_childs.length; i++) {
2860 full_height += body_childs[i].offsetHeight;
2863 this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2868 setSize : function(w,h)
2881 if (!this.rendered) {
2885 //this.el.setStyle('display', 'block');
2886 this.el.removeClass('hideing');
2887 this.el.addClass('show');
2889 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2892 this.el.addClass('in');
2895 this.el.addClass('in');
2898 // not sure how we can show data in here..
2900 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2903 Roo.get(document.body).addClass("x-body-masked");
2905 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2906 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2907 this.maskEl.addClass('show');
2911 this.fireEvent('show', this);
2913 // set zindex here - otherwise it appears to be ignored...
2914 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2917 this.items.forEach( function(e) {
2918 e.layout ? e.layout() : false;
2926 if(this.fireEvent("beforehide", this) !== false){
2927 this.maskEl.removeClass('show');
2928 Roo.get(document.body).removeClass("x-body-masked");
2929 this.el.removeClass('in');
2930 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2932 if(this.animate){ // why
2933 this.el.addClass('hideing');
2935 if (!this.el.hasClass('hideing')) {
2936 return; // it's been shown again...
2938 this.el.removeClass('show');
2939 this.el.removeClass('hideing');
2943 this.el.removeClass('show');
2945 this.fireEvent('hide', this);
2948 isVisible : function()
2951 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2955 addButton : function(str, cb)
2959 var b = Roo.apply({}, { html : str } );
2960 b.xns = b.xns || Roo.bootstrap;
2961 b.xtype = b.xtype || 'Button';
2962 if (typeof(b.listeners) == 'undefined') {
2963 b.listeners = { click : cb.createDelegate(this) };
2966 var btn = Roo.factory(b);
2968 btn.render(this.el.select('.modal-footer div').first());
2974 setDefaultButton : function(btn)
2976 //this.el.select('.modal-footer').()
2980 resizeTo: function(w,h)
2984 this.dialogEl.setWidth(w);
2985 if (this.diff === false) {
2986 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2989 this.bodyEl.setHeight(h - this.diff);
2991 this.fireEvent('resize', this);
2994 setContentSize : function(w, h)
2998 onButtonClick: function(btn,e)
3001 this.fireEvent('btnclick', btn.name, e);
3004 * Set the title of the Dialog
3005 * @param {String} str new Title
3007 setTitle: function(str) {
3008 this.titleEl.dom.innerHTML = str;
3011 * Set the body of the Dialog
3012 * @param {String} str new Title
3014 setBody: function(str) {
3015 this.bodyEl.dom.innerHTML = str;
3018 * Set the body of the Dialog using the template
3019 * @param {Obj} data - apply this data to the template and replace the body contents.
3021 applyBody: function(obj)
3024 Roo.log("Error - using apply Body without a template");
3027 this.tmpl.overwrite(this.bodyEl, obj);
3033 Roo.apply(Roo.bootstrap.Modal, {
3035 * Button config that displays a single OK button
3044 * Button config that displays Yes and No buttons
3060 * Button config that displays OK and Cancel buttons
3075 * Button config that displays Yes, No and Cancel buttons
3099 * messagebox - can be used as a replace
3103 * @class Roo.MessageBox
3104 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3108 Roo.Msg.alert('Status', 'Changes saved successfully.');
3110 // Prompt for user data:
3111 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3113 // process text value...
3117 // Show a dialog using config options:
3119 title:'Save Changes?',
3120 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3121 buttons: Roo.Msg.YESNOCANCEL,
3128 Roo.bootstrap.MessageBox = function(){
3129 var dlg, opt, mask, waitTimer;
3130 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3131 var buttons, activeTextEl, bwidth;
3135 var handleButton = function(button){
3137 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3141 var handleHide = function(){
3143 dlg.el.removeClass(opt.cls);
3146 // Roo.TaskMgr.stop(waitTimer);
3147 // waitTimer = null;
3152 var updateButtons = function(b){
3155 buttons["ok"].hide();
3156 buttons["cancel"].hide();
3157 buttons["yes"].hide();
3158 buttons["no"].hide();
3159 //dlg.footer.dom.style.display = 'none';
3162 dlg.footerEl.dom.style.display = '';
3163 for(var k in buttons){
3164 if(typeof buttons[k] != "function"){
3167 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3168 width += buttons[k].el.getWidth()+15;
3178 var handleEsc = function(d, k, e){
3179 if(opt && opt.closable !== false){
3189 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3190 * @return {Roo.BasicDialog} The BasicDialog element
3192 getDialog : function(){
3194 dlg = new Roo.bootstrap.Modal( {
3197 //constraintoviewport:false,
3199 //collapsible : false,
3204 //buttonAlign:"center",
3205 closeClick : function(){
3206 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3209 handleButton("cancel");
3214 dlg.on("hide", handleHide);
3216 //dlg.addKeyListener(27, handleEsc);
3218 this.buttons = buttons;
3219 var bt = this.buttonText;
3220 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3221 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3222 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3223 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3225 bodyEl = dlg.bodyEl.createChild({
3227 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3228 '<textarea class="roo-mb-textarea"></textarea>' +
3229 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3231 msgEl = bodyEl.dom.firstChild;
3232 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3233 textboxEl.enableDisplayMode();
3234 textboxEl.addKeyListener([10,13], function(){
3235 if(dlg.isVisible() && opt && opt.buttons){
3238 }else if(opt.buttons.yes){
3239 handleButton("yes");
3243 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3244 textareaEl.enableDisplayMode();
3245 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3246 progressEl.enableDisplayMode();
3248 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3249 var pf = progressEl.dom.firstChild;
3251 pp = Roo.get(pf.firstChild);
3252 pp.setHeight(pf.offsetHeight);
3260 * Updates the message box body text
3261 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3262 * the XHTML-compliant non-breaking space character '&#160;')
3263 * @return {Roo.MessageBox} This message box
3265 updateText : function(text)
3267 if(!dlg.isVisible() && !opt.width){
3268 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3269 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3271 msgEl.innerHTML = text || ' ';
3273 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3274 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3276 Math.min(opt.width || cw , this.maxWidth),
3277 Math.max(opt.minWidth || this.minWidth, bwidth)
3280 activeTextEl.setWidth(w);
3282 if(dlg.isVisible()){
3283 dlg.fixedcenter = false;
3285 // to big, make it scroll. = But as usual stupid IE does not support
3288 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3289 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3290 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3292 bodyEl.dom.style.height = '';
3293 bodyEl.dom.style.overflowY = '';
3296 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3298 bodyEl.dom.style.overflowX = '';
3301 dlg.setContentSize(w, bodyEl.getHeight());
3302 if(dlg.isVisible()){
3303 dlg.fixedcenter = true;
3309 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3310 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3311 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3312 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3313 * @return {Roo.MessageBox} This message box
3315 updateProgress : function(value, text){
3317 this.updateText(text);
3320 if (pp) { // weird bug on my firefox - for some reason this is not defined
3321 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3322 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3328 * Returns true if the message box is currently displayed
3329 * @return {Boolean} True if the message box is visible, else false
3331 isVisible : function(){
3332 return dlg && dlg.isVisible();
3336 * Hides the message box if it is displayed
3339 if(this.isVisible()){
3345 * Displays a new message box, or reinitializes an existing message box, based on the config options
3346 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3347 * The following config object properties are supported:
3349 Property Type Description
3350 ---------- --------------- ------------------------------------------------------------------------------------
3351 animEl String/Element An id or Element from which the message box should animate as it opens and
3352 closes (defaults to undefined)
3353 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3354 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3355 closable Boolean False to hide the top-right close button (defaults to true). Note that
3356 progress and wait dialogs will ignore this property and always hide the
3357 close button as they can only be closed programmatically.
3358 cls String A custom CSS class to apply to the message box element
3359 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3360 displayed (defaults to 75)
3361 fn Function A callback function to execute after closing the dialog. The arguments to the
3362 function will be btn (the name of the button that was clicked, if applicable,
3363 e.g. "ok"), and text (the value of the active text field, if applicable).
3364 Progress and wait dialogs will ignore this option since they do not respond to
3365 user actions and can only be closed programmatically, so any required function
3366 should be called by the same code after it closes the dialog.
3367 icon String A CSS class that provides a background image to be used as an icon for
3368 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3369 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3370 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3371 modal Boolean False to allow user interaction with the page while the message box is
3372 displayed (defaults to true)
3373 msg String A string that will replace the existing message box body text (defaults
3374 to the XHTML-compliant non-breaking space character ' ')
3375 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3376 progress Boolean True to display a progress bar (defaults to false)
3377 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3378 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3379 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3380 title String The title text
3381 value String The string value to set into the active textbox element if displayed
3382 wait Boolean True to display a progress bar (defaults to false)
3383 width Number The width of the dialog in pixels
3390 msg: 'Please enter your address:',
3392 buttons: Roo.MessageBox.OKCANCEL,
3395 animEl: 'addAddressBtn'
3398 * @param {Object} config Configuration options
3399 * @return {Roo.MessageBox} This message box
3401 show : function(options)
3404 // this causes nightmares if you show one dialog after another
3405 // especially on callbacks..
3407 if(this.isVisible()){
3410 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3411 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3412 Roo.log("New Dialog Message:" + options.msg )
3413 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3414 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3417 var d = this.getDialog();
3419 d.setTitle(opt.title || " ");
3420 d.closeEl.setDisplayed(opt.closable !== false);
3421 activeTextEl = textboxEl;
3422 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3427 textareaEl.setHeight(typeof opt.multiline == "number" ?
3428 opt.multiline : this.defaultTextHeight);
3429 activeTextEl = textareaEl;
3438 progressEl.setDisplayed(opt.progress === true);
3439 this.updateProgress(0);
3440 activeTextEl.dom.value = opt.value || "";
3442 dlg.setDefaultButton(activeTextEl);
3444 var bs = opt.buttons;
3448 }else if(bs && bs.yes){
3449 db = buttons["yes"];
3451 dlg.setDefaultButton(db);
3453 bwidth = updateButtons(opt.buttons);
3454 this.updateText(opt.msg);
3456 d.el.addClass(opt.cls);
3458 d.proxyDrag = opt.proxyDrag === true;
3459 d.modal = opt.modal !== false;
3460 d.mask = opt.modal !== false ? mask : false;
3462 // force it to the end of the z-index stack so it gets a cursor in FF
3463 document.body.appendChild(dlg.el.dom);
3464 d.animateTarget = null;
3465 d.show(options.animEl);
3471 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3472 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3473 * and closing the message box when the process is complete.
3474 * @param {String} title The title bar text
3475 * @param {String} msg The message box body text
3476 * @return {Roo.MessageBox} This message box
3478 progress : function(title, msg){
3485 minWidth: this.minProgressWidth,
3492 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3493 * If a callback function is passed it will be called after the user clicks the button, and the
3494 * id of the button that was clicked will be passed as the only parameter to the callback
3495 * (could also be the top-right close button).
3496 * @param {String} title The title bar text
3497 * @param {String} msg The message box body text
3498 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3499 * @param {Object} scope (optional) The scope of the callback function
3500 * @return {Roo.MessageBox} This message box
3502 alert : function(title, msg, fn, scope)
3517 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3518 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3519 * You are responsible for closing the message box when the process is complete.
3520 * @param {String} msg The message box body text
3521 * @param {String} title (optional) The title bar text
3522 * @return {Roo.MessageBox} This message box
3524 wait : function(msg, title){
3535 waitTimer = Roo.TaskMgr.start({
3537 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3545 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3546 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3547 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3548 * @param {String} title The title bar text
3549 * @param {String} msg The message box body text
3550 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3551 * @param {Object} scope (optional) The scope of the callback function
3552 * @return {Roo.MessageBox} This message box
3554 confirm : function(title, msg, fn, scope){
3558 buttons: this.YESNO,
3567 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3568 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3569 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3570 * (could also be the top-right close button) and the text that was entered will be passed as the two
3571 * parameters to the callback.
3572 * @param {String} title The title bar text
3573 * @param {String} msg The message box body text
3574 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3575 * @param {Object} scope (optional) The scope of the callback function
3576 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3577 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3578 * @return {Roo.MessageBox} This message box
3580 prompt : function(title, msg, fn, scope, multiline){
3584 buttons: this.OKCANCEL,
3589 multiline: multiline,
3596 * Button config that displays a single OK button
3601 * Button config that displays Yes and No buttons
3604 YESNO : {yes:true, no:true},
3606 * Button config that displays OK and Cancel buttons
3609 OKCANCEL : {ok:true, cancel:true},
3611 * Button config that displays Yes, No and Cancel buttons
3614 YESNOCANCEL : {yes:true, no:true, cancel:true},
3617 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3620 defaultTextHeight : 75,
3622 * The maximum width in pixels of the message box (defaults to 600)
3627 * The minimum width in pixels of the message box (defaults to 100)
3632 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3633 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3636 minProgressWidth : 250,
3638 * An object containing the default button text strings that can be overriden for localized language support.
3639 * Supported properties are: ok, cancel, yes and no.
3640 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3653 * Shorthand for {@link Roo.MessageBox}
3655 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3656 Roo.Msg = Roo.Msg || Roo.MessageBox;
3665 * @class Roo.bootstrap.Navbar
3666 * @extends Roo.bootstrap.Component
3667 * Bootstrap Navbar class
3670 * Create a new Navbar
3671 * @param {Object} config The config object
3675 Roo.bootstrap.Navbar = function(config){
3676 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3680 * @event beforetoggle
3681 * Fire before toggle the menu
3682 * @param {Roo.EventObject} e
3684 "beforetoggle" : true
3688 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3697 getAutoCreate : function(){
3700 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3704 initEvents :function ()
3706 //Roo.log(this.el.select('.navbar-toggle',true));
3707 this.el.select('.navbar-toggle',true).on('click', function() {
3708 if(this.fireEvent('beforetoggle', this) !== false){
3709 this.el.select('.navbar-collapse',true).toggleClass('in');
3719 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3721 var size = this.el.getSize();
3722 this.maskEl.setSize(size.width, size.height);
3723 this.maskEl.enableDisplayMode("block");
3732 getChildContainer : function()
3734 if (this.el.select('.collapse').getCount()) {
3735 return this.el.select('.collapse',true).first();
3768 * @class Roo.bootstrap.NavSimplebar
3769 * @extends Roo.bootstrap.Navbar
3770 * Bootstrap Sidebar class
3772 * @cfg {Boolean} inverse is inverted color
3774 * @cfg {String} type (nav | pills | tabs)
3775 * @cfg {Boolean} arrangement stacked | justified
3776 * @cfg {String} align (left | right) alignment
3778 * @cfg {Boolean} main (true|false) main nav bar? default false
3779 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3781 * @cfg {String} tag (header|footer|nav|div) default is nav
3787 * Create a new Sidebar
3788 * @param {Object} config The config object
3792 Roo.bootstrap.NavSimplebar = function(config){
3793 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3796 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3812 getAutoCreate : function(){
3816 tag : this.tag || 'div',
3829 this.type = this.type || 'nav';
3830 if (['tabs','pills'].indexOf(this.type)!==-1) {
3831 cfg.cn[0].cls += ' nav-' + this.type
3835 if (this.type!=='nav') {
3836 Roo.log('nav type must be nav/tabs/pills')
3838 cfg.cn[0].cls += ' navbar-nav'
3844 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3845 cfg.cn[0].cls += ' nav-' + this.arrangement;
3849 if (this.align === 'right') {
3850 cfg.cn[0].cls += ' navbar-right';
3854 cfg.cls += ' navbar-inverse';
3881 * @class Roo.bootstrap.NavHeaderbar
3882 * @extends Roo.bootstrap.NavSimplebar
3883 * Bootstrap Sidebar class
3885 * @cfg {String} brand what is brand
3886 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3887 * @cfg {String} brand_href href of the brand
3888 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3889 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3890 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3891 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3894 * Create a new Sidebar
3895 * @param {Object} config The config object
3899 Roo.bootstrap.NavHeaderbar = function(config){
3900 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3904 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3911 desktopCenter : false,
3914 getAutoCreate : function(){
3917 tag: this.nav || 'nav',
3924 if (this.desktopCenter) {
3925 cn.push({cls : 'container', cn : []});
3932 cls: 'navbar-header',
3937 cls: 'navbar-toggle',
3938 'data-toggle': 'collapse',
3943 html: 'Toggle navigation'
3965 cls: 'collapse navbar-collapse',
3969 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3971 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3972 cfg.cls += ' navbar-' + this.position;
3974 // tag can override this..
3976 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3979 if (this.brand !== '') {
3982 href: this.brand_href ? this.brand_href : '#',
3983 cls: 'navbar-brand',
3991 cfg.cls += ' main-nav';
3999 getHeaderChildContainer : function()
4001 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4002 return this.el.select('.navbar-header',true).first();
4005 return this.getChildContainer();
4009 initEvents : function()
4011 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4013 if (this.autohide) {
4018 Roo.get(document).on('scroll',function(e) {
4019 var ns = Roo.get(document).getScroll().top;
4020 var os = prevScroll;
4024 ft.removeClass('slideDown');
4025 ft.addClass('slideUp');
4028 ft.removeClass('slideUp');
4029 ft.addClass('slideDown');
4050 * @class Roo.bootstrap.NavSidebar
4051 * @extends Roo.bootstrap.Navbar
4052 * Bootstrap Sidebar class
4055 * Create a new Sidebar
4056 * @param {Object} config The config object
4060 Roo.bootstrap.NavSidebar = function(config){
4061 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4064 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4066 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4068 getAutoCreate : function(){
4073 cls: 'sidebar sidebar-nav'
4095 * @class Roo.bootstrap.NavGroup
4096 * @extends Roo.bootstrap.Component
4097 * Bootstrap NavGroup class
4098 * @cfg {String} align (left|right)
4099 * @cfg {Boolean} inverse
4100 * @cfg {String} type (nav|pills|tab) default nav
4101 * @cfg {String} navId - reference Id for navbar.
4105 * Create a new nav group
4106 * @param {Object} config The config object
4109 Roo.bootstrap.NavGroup = function(config){
4110 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4113 Roo.bootstrap.NavGroup.register(this);
4117 * Fires when the active item changes
4118 * @param {Roo.bootstrap.NavGroup} this
4119 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4120 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4127 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4138 getAutoCreate : function()
4140 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4147 if (['tabs','pills'].indexOf(this.type)!==-1) {
4148 cfg.cls += ' nav-' + this.type
4150 if (this.type!=='nav') {
4151 Roo.log('nav type must be nav/tabs/pills')
4153 cfg.cls += ' navbar-nav'
4156 if (this.parent() && this.parent().sidebar) {
4159 cls: 'dashboard-menu sidebar-menu'
4165 if (this.form === true) {
4171 if (this.align === 'right') {
4172 cfg.cls += ' navbar-right';
4174 cfg.cls += ' navbar-left';
4178 if (this.align === 'right') {
4179 cfg.cls += ' navbar-right';
4183 cfg.cls += ' navbar-inverse';
4191 * sets the active Navigation item
4192 * @param {Roo.bootstrap.NavItem} the new current navitem
4194 setActiveItem : function(item)
4197 Roo.each(this.navItems, function(v){
4202 v.setActive(false, true);
4209 item.setActive(true, true);
4210 this.fireEvent('changed', this, item, prev);
4215 * gets the active Navigation item
4216 * @return {Roo.bootstrap.NavItem} the current navitem
4218 getActive : function()
4222 Roo.each(this.navItems, function(v){
4233 indexOfNav : function()
4237 Roo.each(this.navItems, function(v,i){
4248 * adds a Navigation item
4249 * @param {Roo.bootstrap.NavItem} the navitem to add
4251 addItem : function(cfg)
4253 var cn = new Roo.bootstrap.NavItem(cfg);
4255 cn.parentId = this.id;
4256 cn.onRender(this.el, null);
4260 * register a Navigation item
4261 * @param {Roo.bootstrap.NavItem} the navitem to add
4263 register : function(item)
4265 this.navItems.push( item);
4266 item.navId = this.navId;
4271 * clear all the Navigation item
4274 clearAll : function()
4277 this.el.dom.innerHTML = '';
4280 getNavItem: function(tabId)
4283 Roo.each(this.navItems, function(e) {
4284 if (e.tabId == tabId) {
4294 setActiveNext : function()
4296 var i = this.indexOfNav(this.getActive());
4297 if (i > this.navItems.length) {
4300 this.setActiveItem(this.navItems[i+1]);
4302 setActivePrev : function()
4304 var i = this.indexOfNav(this.getActive());
4308 this.setActiveItem(this.navItems[i-1]);
4310 clearWasActive : function(except) {
4311 Roo.each(this.navItems, function(e) {
4312 if (e.tabId != except.tabId && e.was_active) {
4313 e.was_active = false;
4320 getWasActive : function ()
4323 Roo.each(this.navItems, function(e) {
4338 Roo.apply(Roo.bootstrap.NavGroup, {
4342 * register a Navigation Group
4343 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4345 register : function(navgrp)
4347 this.groups[navgrp.navId] = navgrp;
4351 * fetch a Navigation Group based on the navigation ID
4352 * @param {string} the navgroup to add
4353 * @returns {Roo.bootstrap.NavGroup} the navgroup
4355 get: function(navId) {
4356 if (typeof(this.groups[navId]) == 'undefined') {
4358 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4360 return this.groups[navId] ;
4375 * @class Roo.bootstrap.NavItem
4376 * @extends Roo.bootstrap.Component
4377 * Bootstrap Navbar.NavItem class
4378 * @cfg {String} href link to
4379 * @cfg {String} html content of button
4380 * @cfg {String} badge text inside badge
4381 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4382 * @cfg {String} glyphicon name of glyphicon
4383 * @cfg {String} icon name of font awesome icon
4384 * @cfg {Boolean} active Is item active
4385 * @cfg {Boolean} disabled Is item disabled
4387 * @cfg {Boolean} preventDefault (true | false) default false
4388 * @cfg {String} tabId the tab that this item activates.
4389 * @cfg {String} tagtype (a|span) render as a href or span?
4390 * @cfg {Boolean} animateRef (true|false) link to element default false
4393 * Create a new Navbar Item
4394 * @param {Object} config The config object
4396 Roo.bootstrap.NavItem = function(config){
4397 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4402 * The raw click event for the entire grid.
4403 * @param {Roo.EventObject} e
4408 * Fires when the active item active state changes
4409 * @param {Roo.bootstrap.NavItem} this
4410 * @param {boolean} state the new state
4416 * Fires when scroll to element
4417 * @param {Roo.bootstrap.NavItem} this
4418 * @param {Object} options
4419 * @param {Roo.EventObject} e
4427 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4435 preventDefault : false,
4442 getAutoCreate : function(){
4451 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4453 if (this.disabled) {
4454 cfg.cls += ' disabled';
4457 if (this.href || this.html || this.glyphicon || this.icon) {
4461 href : this.href || "#",
4462 html: this.html || ''
4467 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4470 if(this.glyphicon) {
4471 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4476 cfg.cn[0].html += " <span class='caret'></span>";
4480 if (this.badge !== '') {
4482 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4490 initEvents: function()
4492 if (typeof (this.menu) != 'undefined') {
4493 this.menu.parentType = this.xtype;
4494 this.menu.triggerEl = this.el;
4495 this.menu = this.addxtype(Roo.apply({}, this.menu));
4498 this.el.select('a',true).on('click', this.onClick, this);
4500 if(this.tagtype == 'span'){
4501 this.el.select('span',true).on('click', this.onClick, this);
4504 // at this point parent should be available..
4505 this.parent().register(this);
4508 onClick : function(e)
4510 if (e.getTarget('.dropdown-menu-item')) {
4511 // did you click on a menu itemm.... - then don't trigger onclick..
4516 this.preventDefault ||
4519 Roo.log("NavItem - prevent Default?");
4523 if (this.disabled) {
4527 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4528 if (tg && tg.transition) {
4529 Roo.log("waiting for the transitionend");
4535 //Roo.log("fire event clicked");
4536 if(this.fireEvent('click', this, e) === false){
4540 if(this.tagtype == 'span'){
4544 //Roo.log(this.href);
4545 var ael = this.el.select('a',true).first();
4548 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4549 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4550 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4551 return; // ignore... - it's a 'hash' to another page.
4553 Roo.log("NavItem - prevent Default?");
4555 this.scrollToElement(e);
4559 var p = this.parent();
4561 if (['tabs','pills'].indexOf(p.type)!==-1) {
4562 if (typeof(p.setActiveItem) !== 'undefined') {
4563 p.setActiveItem(this);
4567 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4568 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4569 // remove the collapsed menu expand...
4570 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4574 isActive: function () {
4577 setActive : function(state, fire, is_was_active)
4579 if (this.active && !state && this.navId) {
4580 this.was_active = true;
4581 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4583 nv.clearWasActive(this);
4587 this.active = state;
4590 this.el.removeClass('active');
4591 } else if (!this.el.hasClass('active')) {
4592 this.el.addClass('active');
4595 this.fireEvent('changed', this, state);
4598 // show a panel if it's registered and related..
4600 if (!this.navId || !this.tabId || !state || is_was_active) {
4604 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4608 var pan = tg.getPanelByName(this.tabId);
4612 // if we can not flip to new panel - go back to old nav highlight..
4613 if (false == tg.showPanel(pan)) {
4614 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4616 var onav = nv.getWasActive();
4618 onav.setActive(true, false, true);
4627 // this should not be here...
4628 setDisabled : function(state)
4630 this.disabled = state;
4632 this.el.removeClass('disabled');
4633 } else if (!this.el.hasClass('disabled')) {
4634 this.el.addClass('disabled');
4640 * Fetch the element to display the tooltip on.
4641 * @return {Roo.Element} defaults to this.el
4643 tooltipEl : function()
4645 return this.el.select('' + this.tagtype + '', true).first();
4648 scrollToElement : function(e)
4650 var c = document.body;
4653 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4655 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4656 c = document.documentElement;
4659 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4665 var o = target.calcOffsetsTo(c);
4672 this.fireEvent('scrollto', this, options, e);
4674 Roo.get(c).scrollTo('top', options.value, true);
4687 * <span> icon </span>
4688 * <span> text </span>
4689 * <span>badge </span>
4693 * @class Roo.bootstrap.NavSidebarItem
4694 * @extends Roo.bootstrap.NavItem
4695 * Bootstrap Navbar.NavSidebarItem class
4696 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4697 * {Boolean} open is the menu open
4698 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4699 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4700 * {String} buttonSize (sm|md|lg)the extra classes for the button
4701 * {Boolean} showArrow show arrow next to the text (default true)
4703 * Create a new Navbar Button
4704 * @param {Object} config The config object
4706 Roo.bootstrap.NavSidebarItem = function(config){
4707 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4712 * The raw click event for the entire grid.
4713 * @param {Roo.EventObject} e
4718 * Fires when the active item active state changes
4719 * @param {Roo.bootstrap.NavSidebarItem} this
4720 * @param {boolean} state the new state
4728 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4730 badgeWeight : 'default',
4736 buttonWeight : 'default',
4742 getAutoCreate : function(){
4747 href : this.href || '#',
4753 if(this.buttonView){
4756 href : this.href || '#',
4757 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4770 cfg.cls += ' active';
4773 if (this.disabled) {
4774 cfg.cls += ' disabled';
4777 cfg.cls += ' open x-open';
4780 if (this.glyphicon || this.icon) {
4781 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4782 a.cn.push({ tag : 'i', cls : c }) ;
4785 if(!this.buttonView){
4788 html : this.html || ''
4795 if (this.badge !== '') {
4796 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4802 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4805 a.cls += ' dropdown-toggle treeview' ;
4811 initEvents : function()
4813 if (typeof (this.menu) != 'undefined') {
4814 this.menu.parentType = this.xtype;
4815 this.menu.triggerEl = this.el;
4816 this.menu = this.addxtype(Roo.apply({}, this.menu));
4819 this.el.on('click', this.onClick, this);
4821 if(this.badge !== ''){
4822 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4827 onClick : function(e)
4834 if(this.preventDefault){
4838 this.fireEvent('click', this);
4841 disable : function()
4843 this.setDisabled(true);
4848 this.setDisabled(false);
4851 setDisabled : function(state)
4853 if(this.disabled == state){
4857 this.disabled = state;
4860 this.el.addClass('disabled');
4864 this.el.removeClass('disabled');
4869 setActive : function(state)
4871 if(this.active == state){
4875 this.active = state;
4878 this.el.addClass('active');
4882 this.el.removeClass('active');
4887 isActive: function ()
4892 setBadge : function(str)
4898 this.badgeEl.dom.innerHTML = str;
4915 * @class Roo.bootstrap.Row
4916 * @extends Roo.bootstrap.Component
4917 * Bootstrap Row class (contains columns...)
4921 * @param {Object} config The config object
4924 Roo.bootstrap.Row = function(config){
4925 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4928 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4930 getAutoCreate : function(){
4949 * @class Roo.bootstrap.Element
4950 * @extends Roo.bootstrap.Component
4951 * Bootstrap Element class
4952 * @cfg {String} html contents of the element
4953 * @cfg {String} tag tag of the element
4954 * @cfg {String} cls class of the element
4955 * @cfg {Boolean} preventDefault (true|false) default false
4956 * @cfg {Boolean} clickable (true|false) default false
4959 * Create a new Element
4960 * @param {Object} config The config object
4963 Roo.bootstrap.Element = function(config){
4964 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4970 * When a element is chick
4971 * @param {Roo.bootstrap.Element} this
4972 * @param {Roo.EventObject} e
4978 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4983 preventDefault: false,
4986 getAutoCreate : function(){
4990 // cls: this.cls, double assign in parent class Component.js :: onRender
4997 initEvents: function()
4999 Roo.bootstrap.Element.superclass.initEvents.call(this);
5002 this.el.on('click', this.onClick, this);
5007 onClick : function(e)
5009 if(this.preventDefault){
5013 this.fireEvent('click', this, e);
5016 getValue : function()
5018 return this.el.dom.innerHTML;
5021 setValue : function(value)
5023 this.el.dom.innerHTML = value;
5038 * @class Roo.bootstrap.Pagination
5039 * @extends Roo.bootstrap.Component
5040 * Bootstrap Pagination class
5041 * @cfg {String} size xs | sm | md | lg
5042 * @cfg {Boolean} inverse false | true
5045 * Create a new Pagination
5046 * @param {Object} config The config object
5049 Roo.bootstrap.Pagination = function(config){
5050 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5053 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5059 getAutoCreate : function(){
5065 cfg.cls += ' inverse';
5071 cfg.cls += " " + this.cls;
5089 * @class Roo.bootstrap.PaginationItem
5090 * @extends Roo.bootstrap.Component
5091 * Bootstrap PaginationItem class
5092 * @cfg {String} html text
5093 * @cfg {String} href the link
5094 * @cfg {Boolean} preventDefault (true | false) default true
5095 * @cfg {Boolean} active (true | false) default false
5096 * @cfg {Boolean} disabled default false
5100 * Create a new PaginationItem
5101 * @param {Object} config The config object
5105 Roo.bootstrap.PaginationItem = function(config){
5106 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5111 * The raw click event for the entire grid.
5112 * @param {Roo.EventObject} e
5118 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5122 preventDefault: true,
5127 getAutoCreate : function(){
5133 href : this.href ? this.href : '#',
5134 html : this.html ? this.html : ''
5144 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5148 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5154 initEvents: function() {
5156 this.el.on('click', this.onClick, this);
5159 onClick : function(e)
5161 Roo.log('PaginationItem on click ');
5162 if(this.preventDefault){
5170 this.fireEvent('click', this, e);
5186 * @class Roo.bootstrap.Slider
5187 * @extends Roo.bootstrap.Component
5188 * Bootstrap Slider class
5191 * Create a new Slider
5192 * @param {Object} config The config object
5195 Roo.bootstrap.Slider = function(config){
5196 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5199 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5201 getAutoCreate : function(){
5205 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5209 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5221 * Ext JS Library 1.1.1
5222 * Copyright(c) 2006-2007, Ext JS, LLC.
5224 * Originally Released Under LGPL - original licence link has changed is not relivant.
5227 * <script type="text/javascript">
5232 * @class Roo.grid.ColumnModel
5233 * @extends Roo.util.Observable
5234 * This is the default implementation of a ColumnModel used by the Grid. It defines
5235 * the columns in the grid.
5238 var colModel = new Roo.grid.ColumnModel([
5239 {header: "Ticker", width: 60, sortable: true, locked: true},
5240 {header: "Company Name", width: 150, sortable: true},
5241 {header: "Market Cap.", width: 100, sortable: true},
5242 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5243 {header: "Employees", width: 100, sortable: true, resizable: false}
5248 * The config options listed for this class are options which may appear in each
5249 * individual column definition.
5250 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5252 * @param {Object} config An Array of column config objects. See this class's
5253 * config objects for details.
5255 Roo.grid.ColumnModel = function(config){
5257 * The config passed into the constructor
5259 this.config = config;
5262 // if no id, create one
5263 // if the column does not have a dataIndex mapping,
5264 // map it to the order it is in the config
5265 for(var i = 0, len = config.length; i < len; i++){
5267 if(typeof c.dataIndex == "undefined"){
5270 if(typeof c.renderer == "string"){
5271 c.renderer = Roo.util.Format[c.renderer];
5273 if(typeof c.id == "undefined"){
5276 if(c.editor && c.editor.xtype){
5277 c.editor = Roo.factory(c.editor, Roo.grid);
5279 if(c.editor && c.editor.isFormField){
5280 c.editor = new Roo.grid.GridEditor(c.editor);
5282 this.lookup[c.id] = c;
5286 * The width of columns which have no width specified (defaults to 100)
5289 this.defaultWidth = 100;
5292 * Default sortable of columns which have no sortable specified (defaults to false)
5295 this.defaultSortable = false;
5299 * @event widthchange
5300 * Fires when the width of a column changes.
5301 * @param {ColumnModel} this
5302 * @param {Number} columnIndex The column index
5303 * @param {Number} newWidth The new width
5305 "widthchange": true,
5307 * @event headerchange
5308 * Fires when the text of a header changes.
5309 * @param {ColumnModel} this
5310 * @param {Number} columnIndex The column index
5311 * @param {Number} newText The new header text
5313 "headerchange": true,
5315 * @event hiddenchange
5316 * Fires when a column is hidden or "unhidden".
5317 * @param {ColumnModel} this
5318 * @param {Number} columnIndex The column index
5319 * @param {Boolean} hidden true if hidden, false otherwise
5321 "hiddenchange": true,
5323 * @event columnmoved
5324 * Fires when a column is moved.
5325 * @param {ColumnModel} this
5326 * @param {Number} oldIndex
5327 * @param {Number} newIndex
5329 "columnmoved" : true,
5331 * @event columlockchange
5332 * Fires when a column's locked state is changed
5333 * @param {ColumnModel} this
5334 * @param {Number} colIndex
5335 * @param {Boolean} locked true if locked
5337 "columnlockchange" : true
5339 Roo.grid.ColumnModel.superclass.constructor.call(this);
5341 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5343 * @cfg {String} header The header text to display in the Grid view.
5346 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5347 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5348 * specified, the column's index is used as an index into the Record's data Array.
5351 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5352 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5355 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5356 * Defaults to the value of the {@link #defaultSortable} property.
5357 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5360 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5363 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5366 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5369 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5372 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5373 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5374 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5375 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5378 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5381 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5384 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5387 * @cfg {String} cursor (Optional)
5390 * @cfg {String} tooltip (Optional)
5393 * @cfg {Number} xs (Optional)
5396 * @cfg {Number} sm (Optional)
5399 * @cfg {Number} md (Optional)
5402 * @cfg {Number} lg (Optional)
5405 * Returns the id of the column at the specified index.
5406 * @param {Number} index The column index
5407 * @return {String} the id
5409 getColumnId : function(index){
5410 return this.config[index].id;
5414 * Returns the column for a specified id.
5415 * @param {String} id The column id
5416 * @return {Object} the column
5418 getColumnById : function(id){
5419 return this.lookup[id];
5424 * Returns the column for a specified dataIndex.
5425 * @param {String} dataIndex The column dataIndex
5426 * @return {Object|Boolean} the column or false if not found
5428 getColumnByDataIndex: function(dataIndex){
5429 var index = this.findColumnIndex(dataIndex);
5430 return index > -1 ? this.config[index] : false;
5434 * Returns the index for a specified column id.
5435 * @param {String} id The column id
5436 * @return {Number} the index, or -1 if not found
5438 getIndexById : function(id){
5439 for(var i = 0, len = this.config.length; i < len; i++){
5440 if(this.config[i].id == id){
5448 * Returns the index for a specified column dataIndex.
5449 * @param {String} dataIndex The column dataIndex
5450 * @return {Number} the index, or -1 if not found
5453 findColumnIndex : function(dataIndex){
5454 for(var i = 0, len = this.config.length; i < len; i++){
5455 if(this.config[i].dataIndex == dataIndex){
5463 moveColumn : function(oldIndex, newIndex){
5464 var c = this.config[oldIndex];
5465 this.config.splice(oldIndex, 1);
5466 this.config.splice(newIndex, 0, c);
5467 this.dataMap = null;
5468 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5471 isLocked : function(colIndex){
5472 return this.config[colIndex].locked === true;
5475 setLocked : function(colIndex, value, suppressEvent){
5476 if(this.isLocked(colIndex) == value){
5479 this.config[colIndex].locked = value;
5481 this.fireEvent("columnlockchange", this, colIndex, value);
5485 getTotalLockedWidth : function(){
5487 for(var i = 0; i < this.config.length; i++){
5488 if(this.isLocked(i) && !this.isHidden(i)){
5489 this.totalWidth += this.getColumnWidth(i);
5495 getLockedCount : function(){
5496 for(var i = 0, len = this.config.length; i < len; i++){
5497 if(!this.isLocked(i)){
5502 return this.config.length;
5506 * Returns the number of columns.
5509 getColumnCount : function(visibleOnly){
5510 if(visibleOnly === true){
5512 for(var i = 0, len = this.config.length; i < len; i++){
5513 if(!this.isHidden(i)){
5519 return this.config.length;
5523 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5524 * @param {Function} fn
5525 * @param {Object} scope (optional)
5526 * @return {Array} result
5528 getColumnsBy : function(fn, scope){
5530 for(var i = 0, len = this.config.length; i < len; i++){
5531 var c = this.config[i];
5532 if(fn.call(scope||this, c, i) === true){
5540 * Returns true if the specified column is sortable.
5541 * @param {Number} col The column index
5544 isSortable : function(col){
5545 if(typeof this.config[col].sortable == "undefined"){
5546 return this.defaultSortable;
5548 return this.config[col].sortable;
5552 * Returns the rendering (formatting) function defined for the column.
5553 * @param {Number} col The column index.
5554 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5556 getRenderer : function(col){
5557 if(!this.config[col].renderer){
5558 return Roo.grid.ColumnModel.defaultRenderer;
5560 return this.config[col].renderer;
5564 * Sets the rendering (formatting) function for a column.
5565 * @param {Number} col The column index
5566 * @param {Function} fn The function to use to process the cell's raw data
5567 * to return HTML markup for the grid view. The render function is called with
5568 * the following parameters:<ul>
5569 * <li>Data value.</li>
5570 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5571 * <li>css A CSS style string to apply to the table cell.</li>
5572 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5573 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5574 * <li>Row index</li>
5575 * <li>Column index</li>
5576 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5578 setRenderer : function(col, fn){
5579 this.config[col].renderer = fn;
5583 * Returns the width for the specified column.
5584 * @param {Number} col The column index
5587 getColumnWidth : function(col){
5588 return this.config[col].width * 1 || this.defaultWidth;
5592 * Sets the width for a column.
5593 * @param {Number} col The column index
5594 * @param {Number} width The new width
5596 setColumnWidth : function(col, width, suppressEvent){
5597 this.config[col].width = width;
5598 this.totalWidth = null;
5600 this.fireEvent("widthchange", this, col, width);
5605 * Returns the total width of all columns.
5606 * @param {Boolean} includeHidden True to include hidden column widths
5609 getTotalWidth : function(includeHidden){
5610 if(!this.totalWidth){
5611 this.totalWidth = 0;
5612 for(var i = 0, len = this.config.length; i < len; i++){
5613 if(includeHidden || !this.isHidden(i)){
5614 this.totalWidth += this.getColumnWidth(i);
5618 return this.totalWidth;
5622 * Returns the header for the specified column.
5623 * @param {Number} col The column index
5626 getColumnHeader : function(col){
5627 return this.config[col].header;
5631 * Sets the header for a column.
5632 * @param {Number} col The column index
5633 * @param {String} header The new header
5635 setColumnHeader : function(col, header){
5636 this.config[col].header = header;
5637 this.fireEvent("headerchange", this, col, header);
5641 * Returns the tooltip for the specified column.
5642 * @param {Number} col The column index
5645 getColumnTooltip : function(col){
5646 return this.config[col].tooltip;
5649 * Sets the tooltip for a column.
5650 * @param {Number} col The column index
5651 * @param {String} tooltip The new tooltip
5653 setColumnTooltip : function(col, tooltip){
5654 this.config[col].tooltip = tooltip;
5658 * Returns the dataIndex for the specified column.
5659 * @param {Number} col The column index
5662 getDataIndex : function(col){
5663 return this.config[col].dataIndex;
5667 * Sets the dataIndex for a column.
5668 * @param {Number} col The column index
5669 * @param {Number} dataIndex The new dataIndex
5671 setDataIndex : function(col, dataIndex){
5672 this.config[col].dataIndex = dataIndex;
5678 * Returns true if the cell is editable.
5679 * @param {Number} colIndex The column index
5680 * @param {Number} rowIndex The row index - this is nto actually used..?
5683 isCellEditable : function(colIndex, rowIndex){
5684 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5688 * Returns the editor defined for the cell/column.
5689 * return false or null to disable editing.
5690 * @param {Number} colIndex The column index
5691 * @param {Number} rowIndex The row index
5694 getCellEditor : function(colIndex, rowIndex){
5695 return this.config[colIndex].editor;
5699 * Sets if a column is editable.
5700 * @param {Number} col The column index
5701 * @param {Boolean} editable True if the column is editable
5703 setEditable : function(col, editable){
5704 this.config[col].editable = editable;
5709 * Returns true if the column is hidden.
5710 * @param {Number} colIndex The column index
5713 isHidden : function(colIndex){
5714 return this.config[colIndex].hidden;
5719 * Returns true if the column width cannot be changed
5721 isFixed : function(colIndex){
5722 return this.config[colIndex].fixed;
5726 * Returns true if the column can be resized
5729 isResizable : function(colIndex){
5730 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5733 * Sets if a column is hidden.
5734 * @param {Number} colIndex The column index
5735 * @param {Boolean} hidden True if the column is hidden
5737 setHidden : function(colIndex, hidden){
5738 this.config[colIndex].hidden = hidden;
5739 this.totalWidth = null;
5740 this.fireEvent("hiddenchange", this, colIndex, hidden);
5744 * Sets the editor for a column.
5745 * @param {Number} col The column index
5746 * @param {Object} editor The editor object
5748 setEditor : function(col, editor){
5749 this.config[col].editor = editor;
5753 Roo.grid.ColumnModel.defaultRenderer = function(value)
5755 if(typeof value == "object") {
5758 if(typeof value == "string" && value.length < 1){
5762 return String.format("{0}", value);
5765 // Alias for backwards compatibility
5766 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5769 * Ext JS Library 1.1.1
5770 * Copyright(c) 2006-2007, Ext JS, LLC.
5772 * Originally Released Under LGPL - original licence link has changed is not relivant.
5775 * <script type="text/javascript">
5779 * @class Roo.LoadMask
5780 * A simple utility class for generically masking elements while loading data. If the element being masked has
5781 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5782 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5783 * element's UpdateManager load indicator and will be destroyed after the initial load.
5785 * Create a new LoadMask
5786 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5787 * @param {Object} config The config object
5789 Roo.LoadMask = function(el, config){
5790 this.el = Roo.get(el);
5791 Roo.apply(this, config);
5793 this.store.on('beforeload', this.onBeforeLoad, this);
5794 this.store.on('load', this.onLoad, this);
5795 this.store.on('loadexception', this.onLoadException, this);
5796 this.removeMask = false;
5798 var um = this.el.getUpdateManager();
5799 um.showLoadIndicator = false; // disable the default indicator
5800 um.on('beforeupdate', this.onBeforeLoad, this);
5801 um.on('update', this.onLoad, this);
5802 um.on('failure', this.onLoad, this);
5803 this.removeMask = true;
5807 Roo.LoadMask.prototype = {
5809 * @cfg {Boolean} removeMask
5810 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5811 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5815 * The text to display in a centered loading message box (defaults to 'Loading...')
5819 * @cfg {String} msgCls
5820 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5822 msgCls : 'x-mask-loading',
5825 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5831 * Disables the mask to prevent it from being displayed
5833 disable : function(){
5834 this.disabled = true;
5838 * Enables the mask so that it can be displayed
5840 enable : function(){
5841 this.disabled = false;
5844 onLoadException : function()
5848 if (typeof(arguments[3]) != 'undefined') {
5849 Roo.MessageBox.alert("Error loading",arguments[3]);
5853 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5854 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5861 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5866 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5870 onBeforeLoad : function(){
5872 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5877 destroy : function(){
5879 this.store.un('beforeload', this.onBeforeLoad, this);
5880 this.store.un('load', this.onLoad, this);
5881 this.store.un('loadexception', this.onLoadException, this);
5883 var um = this.el.getUpdateManager();
5884 um.un('beforeupdate', this.onBeforeLoad, this);
5885 um.un('update', this.onLoad, this);
5886 um.un('failure', this.onLoad, this);
5897 * @class Roo.bootstrap.Table
5898 * @extends Roo.bootstrap.Component
5899 * Bootstrap Table class
5900 * @cfg {String} cls table class
5901 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5902 * @cfg {String} bgcolor Specifies the background color for a table
5903 * @cfg {Number} border Specifies whether the table cells should have borders or not
5904 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5905 * @cfg {Number} cellspacing Specifies the space between cells
5906 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5907 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5908 * @cfg {String} sortable Specifies that the table should be sortable
5909 * @cfg {String} summary Specifies a summary of the content of a table
5910 * @cfg {Number} width Specifies the width of a table
5911 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5913 * @cfg {boolean} striped Should the rows be alternative striped
5914 * @cfg {boolean} bordered Add borders to the table
5915 * @cfg {boolean} hover Add hover highlighting
5916 * @cfg {boolean} condensed Format condensed
5917 * @cfg {boolean} responsive Format condensed
5918 * @cfg {Boolean} loadMask (true|false) default false
5919 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5920 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5921 * @cfg {Boolean} rowSelection (true|false) default false
5922 * @cfg {Boolean} cellSelection (true|false) default false
5923 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5924 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5925 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5926 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5930 * Create a new Table
5931 * @param {Object} config The config object
5934 Roo.bootstrap.Table = function(config){
5935 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5940 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5941 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5942 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5943 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5945 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5947 this.sm.grid = this;
5948 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5949 this.sm = this.selModel;
5950 this.sm.xmodule = this.xmodule || false;
5953 if (this.cm && typeof(this.cm.config) == 'undefined') {
5954 this.colModel = new Roo.grid.ColumnModel(this.cm);
5955 this.cm = this.colModel;
5956 this.cm.xmodule = this.xmodule || false;
5959 this.store= Roo.factory(this.store, Roo.data);
5960 this.ds = this.store;
5961 this.ds.xmodule = this.xmodule || false;
5964 if (this.footer && this.store) {
5965 this.footer.dataSource = this.ds;
5966 this.footer = Roo.factory(this.footer);
5973 * Fires when a cell is clicked
5974 * @param {Roo.bootstrap.Table} this
5975 * @param {Roo.Element} el
5976 * @param {Number} rowIndex
5977 * @param {Number} columnIndex
5978 * @param {Roo.EventObject} e
5982 * @event celldblclick
5983 * Fires when a cell is double clicked
5984 * @param {Roo.bootstrap.Table} this
5985 * @param {Roo.Element} el
5986 * @param {Number} rowIndex
5987 * @param {Number} columnIndex
5988 * @param {Roo.EventObject} e
5990 "celldblclick" : true,
5993 * Fires when a row is clicked
5994 * @param {Roo.bootstrap.Table} this
5995 * @param {Roo.Element} el
5996 * @param {Number} rowIndex
5997 * @param {Roo.EventObject} e
6001 * @event rowdblclick
6002 * Fires when a row is double clicked
6003 * @param {Roo.bootstrap.Table} this
6004 * @param {Roo.Element} el
6005 * @param {Number} rowIndex
6006 * @param {Roo.EventObject} e
6008 "rowdblclick" : true,
6011 * Fires when a mouseover occur
6012 * @param {Roo.bootstrap.Table} this
6013 * @param {Roo.Element} el
6014 * @param {Number} rowIndex
6015 * @param {Number} columnIndex
6016 * @param {Roo.EventObject} e
6021 * Fires when a mouseout occur
6022 * @param {Roo.bootstrap.Table} this
6023 * @param {Roo.Element} el
6024 * @param {Number} rowIndex
6025 * @param {Number} columnIndex
6026 * @param {Roo.EventObject} e
6031 * Fires when a row is rendered, so you can change add a style to it.
6032 * @param {Roo.bootstrap.Table} this
6033 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6037 * @event rowsrendered
6038 * Fires when all the rows have been rendered
6039 * @param {Roo.bootstrap.Table} this
6041 'rowsrendered' : true,
6043 * @event contextmenu
6044 * The raw contextmenu event for the entire grid.
6045 * @param {Roo.EventObject} e
6047 "contextmenu" : true,
6049 * @event rowcontextmenu
6050 * Fires when a row is right clicked
6051 * @param {Roo.bootstrap.Table} this
6052 * @param {Number} rowIndex
6053 * @param {Roo.EventObject} e
6055 "rowcontextmenu" : true,
6057 * @event cellcontextmenu
6058 * Fires when a cell is right clicked
6059 * @param {Roo.bootstrap.Table} this
6060 * @param {Number} rowIndex
6061 * @param {Number} cellIndex
6062 * @param {Roo.EventObject} e
6064 "cellcontextmenu" : true,
6066 * @event headercontextmenu
6067 * Fires when a header is right clicked
6068 * @param {Roo.bootstrap.Table} this
6069 * @param {Number} columnIndex
6070 * @param {Roo.EventObject} e
6072 "headercontextmenu" : true
6076 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6102 rowSelection : false,
6103 cellSelection : false,
6106 // Roo.Element - the tbody
6108 // Roo.Element - thead element
6111 container: false, // used by gridpanel...
6117 auto_hide_footer : false,
6119 getAutoCreate : function()
6121 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6128 if (this.scrollBody) {
6129 cfg.cls += ' table-body-fixed';
6132 cfg.cls += ' table-striped';
6136 cfg.cls += ' table-hover';
6138 if (this.bordered) {
6139 cfg.cls += ' table-bordered';
6141 if (this.condensed) {
6142 cfg.cls += ' table-condensed';
6144 if (this.responsive) {
6145 cfg.cls += ' table-responsive';
6149 cfg.cls+= ' ' +this.cls;
6152 // this lot should be simplifed...
6165 ].forEach(function(k) {
6173 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6176 if(this.store || this.cm){
6177 if(this.headerShow){
6178 cfg.cn.push(this.renderHeader());
6181 cfg.cn.push(this.renderBody());
6183 if(this.footerShow){
6184 cfg.cn.push(this.renderFooter());
6186 // where does this come from?
6187 //cfg.cls+= ' TableGrid';
6190 return { cn : [ cfg ] };
6193 initEvents : function()
6195 if(!this.store || !this.cm){
6198 if (this.selModel) {
6199 this.selModel.initEvents();
6203 //Roo.log('initEvents with ds!!!!');
6205 this.mainBody = this.el.select('tbody', true).first();
6206 this.mainHead = this.el.select('thead', true).first();
6207 this.mainFoot = this.el.select('tfoot', true).first();
6213 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6214 e.on('click', _this.sort, _this);
6217 this.mainBody.on("click", this.onClick, this);
6218 this.mainBody.on("dblclick", this.onDblClick, this);
6220 // why is this done????? = it breaks dialogs??
6221 //this.parent().el.setStyle('position', 'relative');
6225 this.footer.parentId = this.id;
6226 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6229 this.el.select('tfoot tr td').first().addClass('hide');
6234 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6237 this.store.on('load', this.onLoad, this);
6238 this.store.on('beforeload', this.onBeforeLoad, this);
6239 this.store.on('update', this.onUpdate, this);
6240 this.store.on('add', this.onAdd, this);
6241 this.store.on("clear", this.clear, this);
6243 this.el.on("contextmenu", this.onContextMenu, this);
6245 this.mainBody.on('scroll', this.onBodyScroll, this);
6247 this.cm.on("headerchange", this.onHeaderChange, this);
6249 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6253 onContextMenu : function(e, t)
6255 this.processEvent("contextmenu", e);
6258 processEvent : function(name, e)
6260 if (name != 'touchstart' ) {
6261 this.fireEvent(name, e);
6264 var t = e.getTarget();
6266 var cell = Roo.get(t);
6272 if(cell.findParent('tfoot', false, true)){
6276 if(cell.findParent('thead', false, true)){
6278 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6279 cell = Roo.get(t).findParent('th', false, true);
6281 Roo.log("failed to find th in thead?");
6282 Roo.log(e.getTarget());
6287 var cellIndex = cell.dom.cellIndex;
6289 var ename = name == 'touchstart' ? 'click' : name;
6290 this.fireEvent("header" + ename, this, cellIndex, e);
6295 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6296 cell = Roo.get(t).findParent('td', false, true);
6298 Roo.log("failed to find th in tbody?");
6299 Roo.log(e.getTarget());
6304 var row = cell.findParent('tr', false, true);
6305 var cellIndex = cell.dom.cellIndex;
6306 var rowIndex = row.dom.rowIndex - 1;
6310 this.fireEvent("row" + name, this, rowIndex, e);
6314 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6320 onMouseover : function(e, el)
6322 var cell = Roo.get(el);
6328 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6329 cell = cell.findParent('td', false, true);
6332 var row = cell.findParent('tr', false, true);
6333 var cellIndex = cell.dom.cellIndex;
6334 var rowIndex = row.dom.rowIndex - 1; // start from 0
6336 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6340 onMouseout : function(e, el)
6342 var cell = Roo.get(el);
6348 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6349 cell = cell.findParent('td', false, true);
6352 var row = cell.findParent('tr', false, true);
6353 var cellIndex = cell.dom.cellIndex;
6354 var rowIndex = row.dom.rowIndex - 1; // start from 0
6356 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6360 onClick : function(e, el)
6362 var cell = Roo.get(el);
6364 if(!cell || (!this.cellSelection && !this.rowSelection)){
6368 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6369 cell = cell.findParent('td', false, true);
6372 if(!cell || typeof(cell) == 'undefined'){
6376 var row = cell.findParent('tr', false, true);
6378 if(!row || typeof(row) == 'undefined'){
6382 var cellIndex = cell.dom.cellIndex;
6383 var rowIndex = this.getRowIndex(row);
6385 // why??? - should these not be based on SelectionModel?
6386 if(this.cellSelection){
6387 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6390 if(this.rowSelection){
6391 this.fireEvent('rowclick', this, row, rowIndex, e);
6397 onDblClick : function(e,el)
6399 var cell = Roo.get(el);
6401 if(!cell || (!this.cellSelection && !this.rowSelection)){
6405 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406 cell = cell.findParent('td', false, true);
6409 if(!cell || typeof(cell) == 'undefined'){
6413 var row = cell.findParent('tr', false, true);
6415 if(!row || typeof(row) == 'undefined'){
6419 var cellIndex = cell.dom.cellIndex;
6420 var rowIndex = this.getRowIndex(row);
6422 if(this.cellSelection){
6423 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6426 if(this.rowSelection){
6427 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6431 sort : function(e,el)
6433 var col = Roo.get(el);
6435 if(!col.hasClass('sortable')){
6439 var sort = col.attr('sort');
6442 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6446 this.store.sortInfo = {field : sort, direction : dir};
6449 Roo.log("calling footer first");
6450 this.footer.onClick('first');
6453 this.store.load({ params : { start : 0 } });
6457 renderHeader : function()
6465 this.totalWidth = 0;
6467 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6469 var config = cm.config[i];
6473 cls : 'x-hcol-' + i,
6475 html: cm.getColumnHeader(i)
6480 if(typeof(config.sortable) != 'undefined' && config.sortable){
6482 c.html = '<i class="glyphicon"></i>' + c.html;
6485 if(typeof(config.lgHeader) != 'undefined'){
6486 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6489 if(typeof(config.mdHeader) != 'undefined'){
6490 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6493 if(typeof(config.smHeader) != 'undefined'){
6494 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6497 if(typeof(config.xsHeader) != 'undefined'){
6498 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6505 if(typeof(config.tooltip) != 'undefined'){
6506 c.tooltip = config.tooltip;
6509 if(typeof(config.colspan) != 'undefined'){
6510 c.colspan = config.colspan;
6513 if(typeof(config.hidden) != 'undefined' && config.hidden){
6514 c.style += ' display:none;';
6517 if(typeof(config.dataIndex) != 'undefined'){
6518 c.sort = config.dataIndex;
6523 if(typeof(config.align) != 'undefined' && config.align.length){
6524 c.style += ' text-align:' + config.align + ';';
6527 if(typeof(config.width) != 'undefined'){
6528 c.style += ' width:' + config.width + 'px;';
6529 this.totalWidth += config.width;
6531 this.totalWidth += 100; // assume minimum of 100 per column?
6534 if(typeof(config.cls) != 'undefined'){
6535 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6538 ['xs','sm','md','lg'].map(function(size){
6540 if(typeof(config[size]) == 'undefined'){
6544 if (!config[size]) { // 0 = hidden
6545 c.cls += ' hidden-' + size;
6549 c.cls += ' col-' + size + '-' + config[size];
6559 renderBody : function()
6569 colspan : this.cm.getColumnCount()
6579 renderFooter : function()
6589 colspan : this.cm.getColumnCount()
6603 // Roo.log('ds onload');
6608 var ds = this.store;
6610 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6611 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6612 if (_this.store.sortInfo) {
6614 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6615 e.select('i', true).addClass(['glyphicon-arrow-up']);
6618 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6619 e.select('i', true).addClass(['glyphicon-arrow-down']);
6624 var tbody = this.mainBody;
6626 if(ds.getCount() > 0){
6627 ds.data.each(function(d,rowIndex){
6628 var row = this.renderRow(cm, ds, rowIndex);
6630 tbody.createChild(row);
6634 if(row.cellObjects.length){
6635 Roo.each(row.cellObjects, function(r){
6636 _this.renderCellObject(r);
6643 var tfoot = this.el.select('tfoot', true).first();
6645 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6647 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6649 var total = this.ds.getTotalCount();
6651 if(this.footer.pageSize < total){
6652 this.mainFoot.show();
6656 Roo.each(this.el.select('tbody td', true).elements, function(e){
6657 e.on('mouseover', _this.onMouseover, _this);
6660 Roo.each(this.el.select('tbody td', true).elements, function(e){
6661 e.on('mouseout', _this.onMouseout, _this);
6663 this.fireEvent('rowsrendered', this);
6669 onUpdate : function(ds,record)
6671 this.refreshRow(record);
6675 onRemove : function(ds, record, index, isUpdate){
6676 if(isUpdate !== true){
6677 this.fireEvent("beforerowremoved", this, index, record);
6679 var bt = this.mainBody.dom;
6681 var rows = this.el.select('tbody > tr', true).elements;
6683 if(typeof(rows[index]) != 'undefined'){
6684 bt.removeChild(rows[index].dom);
6687 // if(bt.rows[index]){
6688 // bt.removeChild(bt.rows[index]);
6691 if(isUpdate !== true){
6692 //this.stripeRows(index);
6693 //this.syncRowHeights(index, index);
6695 this.fireEvent("rowremoved", this, index, record);
6699 onAdd : function(ds, records, rowIndex)
6701 //Roo.log('on Add called');
6702 // - note this does not handle multiple adding very well..
6703 var bt = this.mainBody.dom;
6704 for (var i =0 ; i < records.length;i++) {
6705 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6706 //Roo.log(records[i]);
6707 //Roo.log(this.store.getAt(rowIndex+i));
6708 this.insertRow(this.store, rowIndex + i, false);
6715 refreshRow : function(record){
6716 var ds = this.store, index;
6717 if(typeof record == 'number'){
6719 record = ds.getAt(index);
6721 index = ds.indexOf(record);
6723 this.insertRow(ds, index, true);
6725 this.onRemove(ds, record, index+1, true);
6727 //this.syncRowHeights(index, index);
6729 this.fireEvent("rowupdated", this, index, record);
6732 insertRow : function(dm, rowIndex, isUpdate){
6735 this.fireEvent("beforerowsinserted", this, rowIndex);
6737 //var s = this.getScrollState();
6738 var row = this.renderRow(this.cm, this.store, rowIndex);
6739 // insert before rowIndex..
6740 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6744 if(row.cellObjects.length){
6745 Roo.each(row.cellObjects, function(r){
6746 _this.renderCellObject(r);
6751 this.fireEvent("rowsinserted", this, rowIndex);
6752 //this.syncRowHeights(firstRow, lastRow);
6753 //this.stripeRows(firstRow);
6760 getRowDom : function(rowIndex)
6762 var rows = this.el.select('tbody > tr', true).elements;
6764 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6767 // returns the object tree for a tr..
6770 renderRow : function(cm, ds, rowIndex)
6772 var d = ds.getAt(rowIndex);
6776 cls : 'x-row-' + rowIndex,
6780 var cellObjects = [];
6782 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6783 var config = cm.config[i];
6785 var renderer = cm.getRenderer(i);
6789 if(typeof(renderer) !== 'undefined'){
6790 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6792 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6793 // and are rendered into the cells after the row is rendered - using the id for the element.
6795 if(typeof(value) === 'object'){
6805 rowIndex : rowIndex,
6810 this.fireEvent('rowclass', this, rowcfg);
6814 cls : rowcfg.rowClass + ' x-col-' + i,
6816 html: (typeof(value) === 'object') ? '' : value
6823 if(typeof(config.colspan) != 'undefined'){
6824 td.colspan = config.colspan;
6827 if(typeof(config.hidden) != 'undefined' && config.hidden){
6828 td.style += ' display:none;';
6831 if(typeof(config.align) != 'undefined' && config.align.length){
6832 td.style += ' text-align:' + config.align + ';';
6834 if(typeof(config.valign) != 'undefined' && config.valign.length){
6835 td.style += ' vertical-align:' + config.valign + ';';
6838 if(typeof(config.width) != 'undefined'){
6839 td.style += ' width:' + config.width + 'px;';
6842 if(typeof(config.cursor) != 'undefined'){
6843 td.style += ' cursor:' + config.cursor + ';';
6846 if(typeof(config.cls) != 'undefined'){
6847 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6850 ['xs','sm','md','lg'].map(function(size){
6852 if(typeof(config[size]) == 'undefined'){
6856 if (!config[size]) { // 0 = hidden
6857 td.cls += ' hidden-' + size;
6861 td.cls += ' col-' + size + '-' + config[size];
6869 row.cellObjects = cellObjects;
6877 onBeforeLoad : function()
6886 this.el.select('tbody', true).first().dom.innerHTML = '';
6889 * Show or hide a row.
6890 * @param {Number} rowIndex to show or hide
6891 * @param {Boolean} state hide
6893 setRowVisibility : function(rowIndex, state)
6895 var bt = this.mainBody.dom;
6897 var rows = this.el.select('tbody > tr', true).elements;
6899 if(typeof(rows[rowIndex]) == 'undefined'){
6902 rows[rowIndex].dom.style.display = state ? '' : 'none';
6906 getSelectionModel : function(){
6908 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6910 return this.selModel;
6913 * Render the Roo.bootstrap object from renderder
6915 renderCellObject : function(r)
6919 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6921 var t = r.cfg.render(r.container);
6924 Roo.each(r.cfg.cn, function(c){
6926 container: t.getChildContainer(),
6929 _this.renderCellObject(child);
6934 getRowIndex : function(row)
6938 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6949 * Returns the grid's underlying element = used by panel.Grid
6950 * @return {Element} The element
6952 getGridEl : function(){
6956 * Forces a resize - used by panel.Grid
6957 * @return {Element} The element
6959 autoSize : function()
6961 //var ctr = Roo.get(this.container.dom.parentElement);
6962 var ctr = Roo.get(this.el.dom);
6964 var thd = this.getGridEl().select('thead',true).first();
6965 var tbd = this.getGridEl().select('tbody', true).first();
6966 var tfd = this.getGridEl().select('tfoot', true).first();
6968 var cw = ctr.getWidth();
6972 tbd.setSize(ctr.getWidth(),
6973 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6975 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6978 cw = Math.max(cw, this.totalWidth);
6979 this.getGridEl().select('tr',true).setWidth(cw);
6980 // resize 'expandable coloumn?
6982 return; // we doe not have a view in this design..
6985 onBodyScroll: function()
6987 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6989 this.mainHead.setStyle({
6990 'position' : 'relative',
6991 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6997 var scrollHeight = this.mainBody.dom.scrollHeight;
6999 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7001 var height = this.mainBody.getHeight();
7003 if(scrollHeight - height == scrollTop) {
7005 var total = this.ds.getTotalCount();
7007 if(this.footer.cursor + this.footer.pageSize < total){
7009 this.footer.ds.load({
7011 start : this.footer.cursor + this.footer.pageSize,
7012 limit : this.footer.pageSize
7022 onHeaderChange : function()
7024 var header = this.renderHeader();
7025 var table = this.el.select('table', true).first();
7027 this.mainHead.remove();
7028 this.mainHead = table.createChild(header, this.mainBody, false);
7031 onHiddenChange : function(colModel, colIndex, hidden)
7033 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7034 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7036 this.CSS.updateRule(thSelector, "display", "");
7037 this.CSS.updateRule(tdSelector, "display", "");
7040 this.CSS.updateRule(thSelector, "display", "none");
7041 this.CSS.updateRule(tdSelector, "display", "none");
7044 this.onHeaderChange();
7061 * @class Roo.bootstrap.TableCell
7062 * @extends Roo.bootstrap.Component
7063 * Bootstrap TableCell class
7064 * @cfg {String} html cell contain text
7065 * @cfg {String} cls cell class
7066 * @cfg {String} tag cell tag (td|th) default td
7067 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7068 * @cfg {String} align Aligns the content in a cell
7069 * @cfg {String} axis Categorizes cells
7070 * @cfg {String} bgcolor Specifies the background color of a cell
7071 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7072 * @cfg {Number} colspan Specifies the number of columns a cell should span
7073 * @cfg {String} headers Specifies one or more header cells a cell is related to
7074 * @cfg {Number} height Sets the height of a cell
7075 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7076 * @cfg {Number} rowspan Sets the number of rows a cell should span
7077 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7078 * @cfg {String} valign Vertical aligns the content in a cell
7079 * @cfg {Number} width Specifies the width of a cell
7082 * Create a new TableCell
7083 * @param {Object} config The config object
7086 Roo.bootstrap.TableCell = function(config){
7087 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7090 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7110 getAutoCreate : function(){
7111 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7131 cfg.align=this.align
7137 cfg.bgcolor=this.bgcolor
7140 cfg.charoff=this.charoff
7143 cfg.colspan=this.colspan
7146 cfg.headers=this.headers
7149 cfg.height=this.height
7152 cfg.nowrap=this.nowrap
7155 cfg.rowspan=this.rowspan
7158 cfg.scope=this.scope
7161 cfg.valign=this.valign
7164 cfg.width=this.width
7183 * @class Roo.bootstrap.TableRow
7184 * @extends Roo.bootstrap.Component
7185 * Bootstrap TableRow class
7186 * @cfg {String} cls row class
7187 * @cfg {String} align Aligns the content in a table row
7188 * @cfg {String} bgcolor Specifies a background color for a table row
7189 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7190 * @cfg {String} valign Vertical aligns the content in a table row
7193 * Create a new TableRow
7194 * @param {Object} config The config object
7197 Roo.bootstrap.TableRow = function(config){
7198 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7201 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7209 getAutoCreate : function(){
7210 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7220 cfg.align = this.align;
7223 cfg.bgcolor = this.bgcolor;
7226 cfg.charoff = this.charoff;
7229 cfg.valign = this.valign;
7247 * @class Roo.bootstrap.TableBody
7248 * @extends Roo.bootstrap.Component
7249 * Bootstrap TableBody class
7250 * @cfg {String} cls element class
7251 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7252 * @cfg {String} align Aligns the content inside the element
7253 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7254 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7257 * Create a new TableBody
7258 * @param {Object} config The config object
7261 Roo.bootstrap.TableBody = function(config){
7262 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7265 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7273 getAutoCreate : function(){
7274 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7288 cfg.align = this.align;
7291 cfg.charoff = this.charoff;
7294 cfg.valign = this.valign;
7301 // initEvents : function()
7308 // this.store = Roo.factory(this.store, Roo.data);
7309 // this.store.on('load', this.onLoad, this);
7311 // this.store.load();
7315 // onLoad: function ()
7317 // this.fireEvent('load', this);
7327 * Ext JS Library 1.1.1
7328 * Copyright(c) 2006-2007, Ext JS, LLC.
7330 * Originally Released Under LGPL - original licence link has changed is not relivant.
7333 * <script type="text/javascript">
7336 // as we use this in bootstrap.
7337 Roo.namespace('Roo.form');
7339 * @class Roo.form.Action
7340 * Internal Class used to handle form actions
7342 * @param {Roo.form.BasicForm} el The form element or its id
7343 * @param {Object} config Configuration options
7348 // define the action interface
7349 Roo.form.Action = function(form, options){
7351 this.options = options || {};
7354 * Client Validation Failed
7357 Roo.form.Action.CLIENT_INVALID = 'client';
7359 * Server Validation Failed
7362 Roo.form.Action.SERVER_INVALID = 'server';
7364 * Connect to Server Failed
7367 Roo.form.Action.CONNECT_FAILURE = 'connect';
7369 * Reading Data from Server Failed
7372 Roo.form.Action.LOAD_FAILURE = 'load';
7374 Roo.form.Action.prototype = {
7376 failureType : undefined,
7377 response : undefined,
7381 run : function(options){
7386 success : function(response){
7391 handleResponse : function(response){
7395 // default connection failure
7396 failure : function(response){
7398 this.response = response;
7399 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7400 this.form.afterAction(this, false);
7403 processResponse : function(response){
7404 this.response = response;
7405 if(!response.responseText){
7408 this.result = this.handleResponse(response);
7412 // utility functions used internally
7413 getUrl : function(appendParams){
7414 var url = this.options.url || this.form.url || this.form.el.dom.action;
7416 var p = this.getParams();
7418 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7424 getMethod : function(){
7425 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7428 getParams : function(){
7429 var bp = this.form.baseParams;
7430 var p = this.options.params;
7432 if(typeof p == "object"){
7433 p = Roo.urlEncode(Roo.applyIf(p, bp));
7434 }else if(typeof p == 'string' && bp){
7435 p += '&' + Roo.urlEncode(bp);
7438 p = Roo.urlEncode(bp);
7443 createCallback : function(){
7445 success: this.success,
7446 failure: this.failure,
7448 timeout: (this.form.timeout*1000),
7449 upload: this.form.fileUpload ? this.success : undefined
7454 Roo.form.Action.Submit = function(form, options){
7455 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7458 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7461 haveProgress : false,
7462 uploadComplete : false,
7464 // uploadProgress indicator.
7465 uploadProgress : function()
7467 if (!this.form.progressUrl) {
7471 if (!this.haveProgress) {
7472 Roo.MessageBox.progress("Uploading", "Uploading");
7474 if (this.uploadComplete) {
7475 Roo.MessageBox.hide();
7479 this.haveProgress = true;
7481 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7483 var c = new Roo.data.Connection();
7485 url : this.form.progressUrl,
7490 success : function(req){
7491 //console.log(data);
7495 rdata = Roo.decode(req.responseText)
7497 Roo.log("Invalid data from server..");
7501 if (!rdata || !rdata.success) {
7503 Roo.MessageBox.alert(Roo.encode(rdata));
7506 var data = rdata.data;
7508 if (this.uploadComplete) {
7509 Roo.MessageBox.hide();
7514 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7515 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7518 this.uploadProgress.defer(2000,this);
7521 failure: function(data) {
7522 Roo.log('progress url failed ');
7533 // run get Values on the form, so it syncs any secondary forms.
7534 this.form.getValues();
7536 var o = this.options;
7537 var method = this.getMethod();
7538 var isPost = method == 'POST';
7539 if(o.clientValidation === false || this.form.isValid()){
7541 if (this.form.progressUrl) {
7542 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7543 (new Date() * 1) + '' + Math.random());
7548 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7549 form:this.form.el.dom,
7550 url:this.getUrl(!isPost),
7552 params:isPost ? this.getParams() : null,
7553 isUpload: this.form.fileUpload
7556 this.uploadProgress();
7558 }else if (o.clientValidation !== false){ // client validation failed
7559 this.failureType = Roo.form.Action.CLIENT_INVALID;
7560 this.form.afterAction(this, false);
7564 success : function(response)
7566 this.uploadComplete= true;
7567 if (this.haveProgress) {
7568 Roo.MessageBox.hide();
7572 var result = this.processResponse(response);
7573 if(result === true || result.success){
7574 this.form.afterAction(this, true);
7578 this.form.markInvalid(result.errors);
7579 this.failureType = Roo.form.Action.SERVER_INVALID;
7581 this.form.afterAction(this, false);
7583 failure : function(response)
7585 this.uploadComplete= true;
7586 if (this.haveProgress) {
7587 Roo.MessageBox.hide();
7590 this.response = response;
7591 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7592 this.form.afterAction(this, false);
7595 handleResponse : function(response){
7596 if(this.form.errorReader){
7597 var rs = this.form.errorReader.read(response);
7600 for(var i = 0, len = rs.records.length; i < len; i++) {
7601 var r = rs.records[i];
7605 if(errors.length < 1){
7609 success : rs.success,
7615 ret = Roo.decode(response.responseText);
7619 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7629 Roo.form.Action.Load = function(form, options){
7630 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7631 this.reader = this.form.reader;
7634 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7639 Roo.Ajax.request(Roo.apply(
7640 this.createCallback(), {
7641 method:this.getMethod(),
7642 url:this.getUrl(false),
7643 params:this.getParams()
7647 success : function(response){
7649 var result = this.processResponse(response);
7650 if(result === true || !result.success || !result.data){
7651 this.failureType = Roo.form.Action.LOAD_FAILURE;
7652 this.form.afterAction(this, false);
7655 this.form.clearInvalid();
7656 this.form.setValues(result.data);
7657 this.form.afterAction(this, true);
7660 handleResponse : function(response){
7661 if(this.form.reader){
7662 var rs = this.form.reader.read(response);
7663 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7665 success : rs.success,
7669 return Roo.decode(response.responseText);
7673 Roo.form.Action.ACTION_TYPES = {
7674 'load' : Roo.form.Action.Load,
7675 'submit' : Roo.form.Action.Submit
7684 * @class Roo.bootstrap.Form
7685 * @extends Roo.bootstrap.Component
7686 * Bootstrap Form class
7687 * @cfg {String} method GET | POST (default POST)
7688 * @cfg {String} labelAlign top | left (default top)
7689 * @cfg {String} align left | right - for navbars
7690 * @cfg {Boolean} loadMask load mask when submit (default true)
7695 * @param {Object} config The config object
7699 Roo.bootstrap.Form = function(config){
7701 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7703 Roo.bootstrap.Form.popover.apply();
7707 * @event clientvalidation
7708 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7709 * @param {Form} this
7710 * @param {Boolean} valid true if the form has passed client-side validation
7712 clientvalidation: true,
7714 * @event beforeaction
7715 * Fires before any action is performed. Return false to cancel the action.
7716 * @param {Form} this
7717 * @param {Action} action The action to be performed
7721 * @event actionfailed
7722 * Fires when an action fails.
7723 * @param {Form} this
7724 * @param {Action} action The action that failed
7726 actionfailed : true,
7728 * @event actioncomplete
7729 * Fires when an action is completed.
7730 * @param {Form} this
7731 * @param {Action} action The action that completed
7733 actioncomplete : true
7737 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7740 * @cfg {String} method
7741 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7746 * The URL to use for form actions if one isn't supplied in the action options.
7749 * @cfg {Boolean} fileUpload
7750 * Set to true if this form is a file upload.
7754 * @cfg {Object} baseParams
7755 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7759 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7763 * @cfg {Sting} align (left|right) for navbar forms
7768 activeAction : null,
7771 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7772 * element by passing it or its id or mask the form itself by passing in true.
7775 waitMsgTarget : false,
7780 * @cfg {Boolean} errorMask (true|false) default false
7785 * @cfg {Number} maskOffset Default 100
7790 * @cfg {Boolean} maskBody
7794 getAutoCreate : function(){
7798 method : this.method || 'POST',
7799 id : this.id || Roo.id(),
7802 if (this.parent().xtype.match(/^Nav/)) {
7803 cfg.cls = 'navbar-form navbar-' + this.align;
7807 if (this.labelAlign == 'left' ) {
7808 cfg.cls += ' form-horizontal';
7814 initEvents : function()
7816 this.el.on('submit', this.onSubmit, this);
7817 // this was added as random key presses on the form where triggering form submit.
7818 this.el.on('keypress', function(e) {
7819 if (e.getCharCode() != 13) {
7822 // we might need to allow it for textareas.. and some other items.
7823 // check e.getTarget().
7825 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7829 Roo.log("keypress blocked");
7837 onSubmit : function(e){
7842 * Returns true if client-side validation on the form is successful.
7845 isValid : function(){
7846 var items = this.getItems();
7850 items.each(function(f){
7856 Roo.log('invalid field: ' + f.name);
7860 if(!target && f.el.isVisible(true)){
7866 if(this.errorMask && !valid){
7867 Roo.bootstrap.Form.popover.mask(this, target);
7874 * Returns true if any fields in this form have changed since their original load.
7877 isDirty : function(){
7879 var items = this.getItems();
7880 items.each(function(f){
7890 * Performs a predefined action (submit or load) or custom actions you define on this form.
7891 * @param {String} actionName The name of the action type
7892 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7893 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7894 * accept other config options):
7896 Property Type Description
7897 ---------------- --------------- ----------------------------------------------------------------------------------
7898 url String The url for the action (defaults to the form's url)
7899 method String The form method to use (defaults to the form's method, or POST if not defined)
7900 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7901 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7902 validate the form on the client (defaults to false)
7904 * @return {BasicForm} this
7906 doAction : function(action, options){
7907 if(typeof action == 'string'){
7908 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7910 if(this.fireEvent('beforeaction', this, action) !== false){
7911 this.beforeAction(action);
7912 action.run.defer(100, action);
7918 beforeAction : function(action){
7919 var o = action.options;
7924 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7926 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7929 // not really supported yet.. ??
7931 //if(this.waitMsgTarget === true){
7932 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7933 //}else if(this.waitMsgTarget){
7934 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7935 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7937 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7943 afterAction : function(action, success){
7944 this.activeAction = null;
7945 var o = action.options;
7950 Roo.get(document.body).unmask();
7956 //if(this.waitMsgTarget === true){
7957 // this.el.unmask();
7958 //}else if(this.waitMsgTarget){
7959 // this.waitMsgTarget.unmask();
7961 // Roo.MessageBox.updateProgress(1);
7962 // Roo.MessageBox.hide();
7969 Roo.callback(o.success, o.scope, [this, action]);
7970 this.fireEvent('actioncomplete', this, action);
7974 // failure condition..
7975 // we have a scenario where updates need confirming.
7976 // eg. if a locking scenario exists..
7977 // we look for { errors : { needs_confirm : true }} in the response.
7979 (typeof(action.result) != 'undefined') &&
7980 (typeof(action.result.errors) != 'undefined') &&
7981 (typeof(action.result.errors.needs_confirm) != 'undefined')
7984 Roo.log("not supported yet");
7987 Roo.MessageBox.confirm(
7988 "Change requires confirmation",
7989 action.result.errorMsg,
7994 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8004 Roo.callback(o.failure, o.scope, [this, action]);
8005 // show an error message if no failed handler is set..
8006 if (!this.hasListener('actionfailed')) {
8007 Roo.log("need to add dialog support");
8009 Roo.MessageBox.alert("Error",
8010 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8011 action.result.errorMsg :
8012 "Saving Failed, please check your entries or try again"
8017 this.fireEvent('actionfailed', this, action);
8022 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8023 * @param {String} id The value to search for
8026 findField : function(id){
8027 var items = this.getItems();
8028 var field = items.get(id);
8030 items.each(function(f){
8031 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8038 return field || null;
8041 * Mark fields in this form invalid in bulk.
8042 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8043 * @return {BasicForm} this
8045 markInvalid : function(errors){
8046 if(errors instanceof Array){
8047 for(var i = 0, len = errors.length; i < len; i++){
8048 var fieldError = errors[i];
8049 var f = this.findField(fieldError.id);
8051 f.markInvalid(fieldError.msg);
8057 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8058 field.markInvalid(errors[id]);
8062 //Roo.each(this.childForms || [], function (f) {
8063 // f.markInvalid(errors);
8070 * Set values for fields in this form in bulk.
8071 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8072 * @return {BasicForm} this
8074 setValues : function(values){
8075 if(values instanceof Array){ // array of objects
8076 for(var i = 0, len = values.length; i < len; i++){
8078 var f = this.findField(v.id);
8080 f.setValue(v.value);
8081 if(this.trackResetOnLoad){
8082 f.originalValue = f.getValue();
8086 }else{ // object hash
8089 if(typeof values[id] != 'function' && (field = this.findField(id))){
8091 if (field.setFromData &&
8093 field.displayField &&
8094 // combos' with local stores can
8095 // be queried via setValue()
8096 // to set their value..
8097 (field.store && !field.store.isLocal)
8101 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8102 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8103 field.setFromData(sd);
8105 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8107 field.setFromData(values);
8110 field.setValue(values[id]);
8114 if(this.trackResetOnLoad){
8115 field.originalValue = field.getValue();
8121 //Roo.each(this.childForms || [], function (f) {
8122 // f.setValues(values);
8129 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8130 * they are returned as an array.
8131 * @param {Boolean} asString
8134 getValues : function(asString){
8135 //if (this.childForms) {
8136 // copy values from the child forms
8137 // Roo.each(this.childForms, function (f) {
8138 // this.setValues(f.getValues());
8144 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8145 if(asString === true){
8148 return Roo.urlDecode(fs);
8152 * Returns the fields in this form as an object with key/value pairs.
8153 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8156 getFieldValues : function(with_hidden)
8158 var items = this.getItems();
8160 items.each(function(f){
8166 var v = f.getValue();
8168 if (f.inputType =='radio') {
8169 if (typeof(ret[f.getName()]) == 'undefined') {
8170 ret[f.getName()] = ''; // empty..
8173 if (!f.el.dom.checked) {
8181 if(f.xtype == 'MoneyField'){
8182 ret[f.currencyName] = f.getCurrency();
8185 // not sure if this supported any more..
8186 if ((typeof(v) == 'object') && f.getRawValue) {
8187 v = f.getRawValue() ; // dates..
8189 // combo boxes where name != hiddenName...
8190 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8191 ret[f.name] = f.getRawValue();
8193 ret[f.getName()] = v;
8200 * Clears all invalid messages in this form.
8201 * @return {BasicForm} this
8203 clearInvalid : function(){
8204 var items = this.getItems();
8206 items.each(function(f){
8215 * @return {BasicForm} this
8218 var items = this.getItems();
8219 items.each(function(f){
8223 Roo.each(this.childForms || [], function (f) {
8231 getItems : function()
8233 var r=new Roo.util.MixedCollection(false, function(o){
8234 return o.id || (o.id = Roo.id());
8236 var iter = function(el) {
8243 Roo.each(el.items,function(e) {
8252 hideFields : function(items)
8254 Roo.each(items, function(i){
8256 var f = this.findField(i);
8262 if(f.xtype == 'DateField'){
8263 f.setVisible(false);
8272 showFields : function(items)
8274 Roo.each(items, function(i){
8276 var f = this.findField(i);
8282 if(f.xtype == 'DateField'){
8294 Roo.apply(Roo.bootstrap.Form, {
8321 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8322 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8323 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8324 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8327 this.maskEl.top.enableDisplayMode("block");
8328 this.maskEl.left.enableDisplayMode("block");
8329 this.maskEl.bottom.enableDisplayMode("block");
8330 this.maskEl.right.enableDisplayMode("block");
8332 this.toolTip = new Roo.bootstrap.Tooltip({
8333 cls : 'roo-form-error-popover',
8335 'left' : ['r-l', [-2,0], 'right'],
8336 'right' : ['l-r', [2,0], 'left'],
8337 'bottom' : ['tl-bl', [0,2], 'top'],
8338 'top' : [ 'bl-tl', [0,-2], 'bottom']
8342 this.toolTip.render(Roo.get(document.body));
8344 this.toolTip.el.enableDisplayMode("block");
8346 Roo.get(document.body).on('click', function(){
8350 Roo.get(document.body).on('touchstart', function(){
8354 this.isApplied = true
8357 mask : function(form, target)
8361 this.target = target;
8363 if(!this.form.errorMask || !target.el){
8367 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8369 Roo.log(scrollable);
8371 var ot = this.target.el.calcOffsetsTo(scrollable);
8373 var scrollTo = ot[1] - this.form.maskOffset;
8375 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8377 scrollable.scrollTo('top', scrollTo);
8379 var box = this.target.el.getBox();
8381 var zIndex = Roo.bootstrap.Modal.zIndex++;
8384 this.maskEl.top.setStyle('position', 'absolute');
8385 this.maskEl.top.setStyle('z-index', zIndex);
8386 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8387 this.maskEl.top.setLeft(0);
8388 this.maskEl.top.setTop(0);
8389 this.maskEl.top.show();
8391 this.maskEl.left.setStyle('position', 'absolute');
8392 this.maskEl.left.setStyle('z-index', zIndex);
8393 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8394 this.maskEl.left.setLeft(0);
8395 this.maskEl.left.setTop(box.y - this.padding);
8396 this.maskEl.left.show();
8398 this.maskEl.bottom.setStyle('position', 'absolute');
8399 this.maskEl.bottom.setStyle('z-index', zIndex);
8400 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8401 this.maskEl.bottom.setLeft(0);
8402 this.maskEl.bottom.setTop(box.bottom + this.padding);
8403 this.maskEl.bottom.show();
8405 this.maskEl.right.setStyle('position', 'absolute');
8406 this.maskEl.right.setStyle('z-index', zIndex);
8407 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8408 this.maskEl.right.setLeft(box.right + this.padding);
8409 this.maskEl.right.setTop(box.y - this.padding);
8410 this.maskEl.right.show();
8412 this.toolTip.bindEl = this.target.el;
8414 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8416 var tip = this.target.blankText;
8418 if(this.target.getValue() !== '' ) {
8420 if (this.target.invalidText.length) {
8421 tip = this.target.invalidText;
8422 } else if (this.target.regexText.length){
8423 tip = this.target.regexText;
8427 this.toolTip.show(tip);
8429 this.intervalID = window.setInterval(function() {
8430 Roo.bootstrap.Form.popover.unmask();
8433 window.onwheel = function(){ return false;};
8435 (function(){ this.isMasked = true; }).defer(500, this);
8441 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8445 this.maskEl.top.setStyle('position', 'absolute');
8446 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8447 this.maskEl.top.hide();
8449 this.maskEl.left.setStyle('position', 'absolute');
8450 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8451 this.maskEl.left.hide();
8453 this.maskEl.bottom.setStyle('position', 'absolute');
8454 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8455 this.maskEl.bottom.hide();
8457 this.maskEl.right.setStyle('position', 'absolute');
8458 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8459 this.maskEl.right.hide();
8461 this.toolTip.hide();
8463 this.toolTip.el.hide();
8465 window.onwheel = function(){ return true;};
8467 if(this.intervalID){
8468 window.clearInterval(this.intervalID);
8469 this.intervalID = false;
8472 this.isMasked = false;
8482 * Ext JS Library 1.1.1
8483 * Copyright(c) 2006-2007, Ext JS, LLC.
8485 * Originally Released Under LGPL - original licence link has changed is not relivant.
8488 * <script type="text/javascript">
8491 * @class Roo.form.VTypes
8492 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8495 Roo.form.VTypes = function(){
8496 // closure these in so they are only created once.
8497 var alpha = /^[a-zA-Z_]+$/;
8498 var alphanum = /^[a-zA-Z0-9_]+$/;
8499 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8500 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8502 // All these messages and functions are configurable
8505 * The function used to validate email addresses
8506 * @param {String} value The email address
8508 'email' : function(v){
8509 return email.test(v);
8512 * The error text to display when the email validation function returns false
8515 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8517 * The keystroke filter mask to be applied on email input
8520 'emailMask' : /[a-z0-9_\.\-@]/i,
8523 * The function used to validate URLs
8524 * @param {String} value The URL
8526 'url' : function(v){
8530 * The error text to display when the url validation function returns false
8533 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8536 * The function used to validate alpha values
8537 * @param {String} value The value
8539 'alpha' : function(v){
8540 return alpha.test(v);
8543 * The error text to display when the alpha validation function returns false
8546 'alphaText' : 'This field should only contain letters and _',
8548 * The keystroke filter mask to be applied on alpha input
8551 'alphaMask' : /[a-z_]/i,
8554 * The function used to validate alphanumeric values
8555 * @param {String} value The value
8557 'alphanum' : function(v){
8558 return alphanum.test(v);
8561 * The error text to display when the alphanumeric validation function returns false
8564 'alphanumText' : 'This field should only contain letters, numbers and _',
8566 * The keystroke filter mask to be applied on alphanumeric input
8569 'alphanumMask' : /[a-z0-9_]/i
8579 * @class Roo.bootstrap.Input
8580 * @extends Roo.bootstrap.Component
8581 * Bootstrap Input class
8582 * @cfg {Boolean} disabled is it disabled
8583 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8584 * @cfg {String} name name of the input
8585 * @cfg {string} fieldLabel - the label associated
8586 * @cfg {string} placeholder - placeholder to put in text.
8587 * @cfg {string} before - input group add on before
8588 * @cfg {string} after - input group add on after
8589 * @cfg {string} size - (lg|sm) or leave empty..
8590 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8591 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8592 * @cfg {Number} md colspan out of 12 for computer-sized screens
8593 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8594 * @cfg {string} value default value of the input
8595 * @cfg {Number} labelWidth set the width of label
8596 * @cfg {Number} labellg set the width of label (1-12)
8597 * @cfg {Number} labelmd set the width of label (1-12)
8598 * @cfg {Number} labelsm set the width of label (1-12)
8599 * @cfg {Number} labelxs set the width of label (1-12)
8600 * @cfg {String} labelAlign (top|left)
8601 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8602 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8603 * @cfg {String} indicatorpos (left|right) default left
8604 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8605 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8607 * @cfg {String} align (left|center|right) Default left
8608 * @cfg {Boolean} forceFeedback (true|false) Default false
8611 * Create a new Input
8612 * @param {Object} config The config object
8615 Roo.bootstrap.Input = function(config){
8617 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8622 * Fires when this field receives input focus.
8623 * @param {Roo.form.Field} this
8628 * Fires when this field loses input focus.
8629 * @param {Roo.form.Field} this
8634 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8635 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8636 * @param {Roo.form.Field} this
8637 * @param {Roo.EventObject} e The event object
8642 * Fires just before the field blurs if the field value has changed.
8643 * @param {Roo.form.Field} this
8644 * @param {Mixed} newValue The new value
8645 * @param {Mixed} oldValue The original value
8650 * Fires after the field has been marked as invalid.
8651 * @param {Roo.form.Field} this
8652 * @param {String} msg The validation message
8657 * Fires after the field has been validated with no errors.
8658 * @param {Roo.form.Field} this
8663 * Fires after the key up
8664 * @param {Roo.form.Field} this
8665 * @param {Roo.EventObject} e The event Object
8671 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8673 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8674 automatic validation (defaults to "keyup").
8676 validationEvent : "keyup",
8678 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8680 validateOnBlur : true,
8682 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8684 validationDelay : 250,
8686 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8688 focusClass : "x-form-focus", // not needed???
8692 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8694 invalidClass : "has-warning",
8697 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8699 validClass : "has-success",
8702 * @cfg {Boolean} hasFeedback (true|false) default true
8707 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8709 invalidFeedbackClass : "glyphicon-warning-sign",
8712 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8714 validFeedbackClass : "glyphicon-ok",
8717 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8719 selectOnFocus : false,
8722 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8726 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8731 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8733 disableKeyFilter : false,
8736 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8740 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8744 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8746 blankText : "Please complete this mandatory field",
8749 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8753 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8755 maxLength : Number.MAX_VALUE,
8757 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8759 minLengthText : "The minimum length for this field is {0}",
8761 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8763 maxLengthText : "The maximum length for this field is {0}",
8767 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8768 * If available, this function will be called only after the basic validators all return true, and will be passed the
8769 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8773 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8774 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8775 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8779 * @cfg {String} regexText -- Depricated - use Invalid Text
8784 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8790 autocomplete: false,
8809 formatedValue : false,
8810 forceFeedback : false,
8812 indicatorpos : 'left',
8822 parentLabelAlign : function()
8825 while (parent.parent()) {
8826 parent = parent.parent();
8827 if (typeof(parent.labelAlign) !='undefined') {
8828 return parent.labelAlign;
8835 getAutoCreate : function()
8837 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8843 if(this.inputType != 'hidden'){
8844 cfg.cls = 'form-group' //input-group
8850 type : this.inputType,
8852 cls : 'form-control',
8853 placeholder : this.placeholder || '',
8854 autocomplete : this.autocomplete || 'new-password'
8857 if(this.capture.length){
8858 input.capture = this.capture;
8861 if(this.accept.length){
8862 input.accept = this.accept + "/*";
8866 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8869 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8870 input.maxLength = this.maxLength;
8873 if (this.disabled) {
8874 input.disabled=true;
8877 if (this.readOnly) {
8878 input.readonly=true;
8882 input.name = this.name;
8886 input.cls += ' input-' + this.size;
8890 ['xs','sm','md','lg'].map(function(size){
8891 if (settings[size]) {
8892 cfg.cls += ' col-' + size + '-' + settings[size];
8896 var inputblock = input;
8900 cls: 'glyphicon form-control-feedback'
8903 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8906 cls : 'has-feedback',
8914 if (this.before || this.after) {
8917 cls : 'input-group',
8921 if (this.before && typeof(this.before) == 'string') {
8923 inputblock.cn.push({
8925 cls : 'roo-input-before input-group-addon',
8929 if (this.before && typeof(this.before) == 'object') {
8930 this.before = Roo.factory(this.before);
8932 inputblock.cn.push({
8934 cls : 'roo-input-before input-group-' +
8935 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8939 inputblock.cn.push(input);
8941 if (this.after && typeof(this.after) == 'string') {
8942 inputblock.cn.push({
8944 cls : 'roo-input-after input-group-addon',
8948 if (this.after && typeof(this.after) == 'object') {
8949 this.after = Roo.factory(this.after);
8951 inputblock.cn.push({
8953 cls : 'roo-input-after input-group-' +
8954 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8958 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8959 inputblock.cls += ' has-feedback';
8960 inputblock.cn.push(feedback);
8964 if (align ==='left' && this.fieldLabel.length) {
8966 cfg.cls += ' roo-form-group-label-left';
8971 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8972 tooltip : 'This field is required'
8977 cls : 'control-label',
8978 html : this.fieldLabel
8989 var labelCfg = cfg.cn[1];
8990 var contentCfg = cfg.cn[2];
8992 if(this.indicatorpos == 'right'){
8997 cls : 'control-label',
9001 html : this.fieldLabel
9005 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9006 tooltip : 'This field is required'
9019 labelCfg = cfg.cn[0];
9020 contentCfg = cfg.cn[1];
9024 if(this.labelWidth > 12){
9025 labelCfg.style = "width: " + this.labelWidth + 'px';
9028 if(this.labelWidth < 13 && this.labelmd == 0){
9029 this.labelmd = this.labelWidth;
9032 if(this.labellg > 0){
9033 labelCfg.cls += ' col-lg-' + this.labellg;
9034 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9037 if(this.labelmd > 0){
9038 labelCfg.cls += ' col-md-' + this.labelmd;
9039 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9042 if(this.labelsm > 0){
9043 labelCfg.cls += ' col-sm-' + this.labelsm;
9044 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9047 if(this.labelxs > 0){
9048 labelCfg.cls += ' col-xs-' + this.labelxs;
9049 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9053 } else if ( this.fieldLabel.length) {
9058 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9059 tooltip : 'This field is required'
9063 //cls : 'input-group-addon',
9064 html : this.fieldLabel
9072 if(this.indicatorpos == 'right'){
9077 //cls : 'input-group-addon',
9078 html : this.fieldLabel
9083 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9084 tooltip : 'This field is required'
9104 if (this.parentType === 'Navbar' && this.parent().bar) {
9105 cfg.cls += ' navbar-form';
9108 if (this.parentType === 'NavGroup') {
9109 cfg.cls += ' navbar-form';
9117 * return the real input element.
9119 inputEl: function ()
9121 return this.el.select('input.form-control',true).first();
9124 tooltipEl : function()
9126 return this.inputEl();
9129 indicatorEl : function()
9131 var indicator = this.el.select('i.roo-required-indicator',true).first();
9141 setDisabled : function(v)
9143 var i = this.inputEl().dom;
9145 i.removeAttribute('disabled');
9149 i.setAttribute('disabled','true');
9151 initEvents : function()
9154 this.inputEl().on("keydown" , this.fireKey, this);
9155 this.inputEl().on("focus", this.onFocus, this);
9156 this.inputEl().on("blur", this.onBlur, this);
9158 this.inputEl().relayEvent('keyup', this);
9160 this.indicator = this.indicatorEl();
9163 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9166 // reference to original value for reset
9167 this.originalValue = this.getValue();
9168 //Roo.form.TextField.superclass.initEvents.call(this);
9169 if(this.validationEvent == 'keyup'){
9170 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9171 this.inputEl().on('keyup', this.filterValidation, this);
9173 else if(this.validationEvent !== false){
9174 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9177 if(this.selectOnFocus){
9178 this.on("focus", this.preFocus, this);
9181 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9182 this.inputEl().on("keypress", this.filterKeys, this);
9184 this.inputEl().relayEvent('keypress', this);
9187 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9188 this.el.on("click", this.autoSize, this);
9191 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9192 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9195 if (typeof(this.before) == 'object') {
9196 this.before.render(this.el.select('.roo-input-before',true).first());
9198 if (typeof(this.after) == 'object') {
9199 this.after.render(this.el.select('.roo-input-after',true).first());
9202 this.inputEl().on('change', this.onChange, this);
9205 filterValidation : function(e){
9206 if(!e.isNavKeyPress()){
9207 this.validationTask.delay(this.validationDelay);
9211 * Validates the field value
9212 * @return {Boolean} True if the value is valid, else false
9214 validate : function(){
9215 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9216 if(this.disabled || this.validateValue(this.getRawValue())){
9227 * Validates a value according to the field's validation rules and marks the field as invalid
9228 * if the validation fails
9229 * @param {Mixed} value The value to validate
9230 * @return {Boolean} True if the value is valid, else false
9232 validateValue : function(value)
9234 if(this.getVisibilityEl().hasClass('hidden')){
9238 if(value.length < 1) { // if it's blank
9239 if(this.allowBlank){
9245 if(value.length < this.minLength){
9248 if(value.length > this.maxLength){
9252 var vt = Roo.form.VTypes;
9253 if(!vt[this.vtype](value, this)){
9257 if(typeof this.validator == "function"){
9258 var msg = this.validator(value);
9262 if (typeof(msg) == 'string') {
9263 this.invalidText = msg;
9267 if(this.regex && !this.regex.test(value)){
9275 fireKey : function(e){
9276 //Roo.log('field ' + e.getKey());
9277 if(e.isNavKeyPress()){
9278 this.fireEvent("specialkey", this, e);
9281 focus : function (selectText){
9283 this.inputEl().focus();
9284 if(selectText === true){
9285 this.inputEl().dom.select();
9291 onFocus : function(){
9292 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9293 // this.el.addClass(this.focusClass);
9296 this.hasFocus = true;
9297 this.startValue = this.getValue();
9298 this.fireEvent("focus", this);
9302 beforeBlur : Roo.emptyFn,
9306 onBlur : function(){
9308 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9309 //this.el.removeClass(this.focusClass);
9311 this.hasFocus = false;
9312 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9315 var v = this.getValue();
9316 if(String(v) !== String(this.startValue)){
9317 this.fireEvent('change', this, v, this.startValue);
9319 this.fireEvent("blur", this);
9322 onChange : function(e)
9324 var v = this.getValue();
9325 if(String(v) !== String(this.startValue)){
9326 this.fireEvent('change', this, v, this.startValue);
9332 * Resets the current field value to the originally loaded value and clears any validation messages
9335 this.setValue(this.originalValue);
9339 * Returns the name of the field
9340 * @return {Mixed} name The name field
9342 getName: function(){
9346 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9347 * @return {Mixed} value The field value
9349 getValue : function(){
9351 var v = this.inputEl().getValue();
9356 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9357 * @return {Mixed} value The field value
9359 getRawValue : function(){
9360 var v = this.inputEl().getValue();
9366 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9367 * @param {Mixed} value The value to set
9369 setRawValue : function(v){
9370 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9373 selectText : function(start, end){
9374 var v = this.getRawValue();
9376 start = start === undefined ? 0 : start;
9377 end = end === undefined ? v.length : end;
9378 var d = this.inputEl().dom;
9379 if(d.setSelectionRange){
9380 d.setSelectionRange(start, end);
9381 }else if(d.createTextRange){
9382 var range = d.createTextRange();
9383 range.moveStart("character", start);
9384 range.moveEnd("character", v.length-end);
9391 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9392 * @param {Mixed} value The value to set
9394 setValue : function(v){
9397 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9403 processValue : function(value){
9404 if(this.stripCharsRe){
9405 var newValue = value.replace(this.stripCharsRe, '');
9406 if(newValue !== value){
9407 this.setRawValue(newValue);
9414 preFocus : function(){
9416 if(this.selectOnFocus){
9417 this.inputEl().dom.select();
9420 filterKeys : function(e){
9422 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9425 var c = e.getCharCode(), cc = String.fromCharCode(c);
9426 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9429 if(!this.maskRe.test(cc)){
9434 * Clear any invalid styles/messages for this field
9436 clearInvalid : function(){
9438 if(!this.el || this.preventMark){ // not rendered
9443 this.el.removeClass(this.invalidClass);
9445 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9447 var feedback = this.el.select('.form-control-feedback', true).first();
9450 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9456 this.indicator.removeClass('visible');
9457 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9460 this.fireEvent('valid', this);
9464 * Mark this field as valid
9466 markValid : function()
9468 if(!this.el || this.preventMark){ // not rendered...
9472 this.el.removeClass([this.invalidClass, this.validClass]);
9474 var feedback = this.el.select('.form-control-feedback', true).first();
9477 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9481 this.indicator.removeClass('visible');
9482 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9489 if(this.allowBlank && !this.getRawValue().length){
9493 this.el.addClass(this.validClass);
9495 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9497 var feedback = this.el.select('.form-control-feedback', true).first();
9500 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9501 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9506 this.fireEvent('valid', this);
9510 * Mark this field as invalid
9511 * @param {String} msg The validation message
9513 markInvalid : function(msg)
9515 if(!this.el || this.preventMark){ // not rendered
9519 this.el.removeClass([this.invalidClass, this.validClass]);
9521 var feedback = this.el.select('.form-control-feedback', true).first();
9524 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9531 if(this.allowBlank && !this.getRawValue().length){
9536 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9537 this.indicator.addClass('visible');
9540 this.el.addClass(this.invalidClass);
9542 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9544 var feedback = this.el.select('.form-control-feedback', true).first();
9547 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9549 if(this.getValue().length || this.forceFeedback){
9550 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9557 this.fireEvent('invalid', this, msg);
9560 SafariOnKeyDown : function(event)
9562 // this is a workaround for a password hang bug on chrome/ webkit.
9563 if (this.inputEl().dom.type != 'password') {
9567 var isSelectAll = false;
9569 if(this.inputEl().dom.selectionEnd > 0){
9570 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9572 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9573 event.preventDefault();
9578 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9580 event.preventDefault();
9581 // this is very hacky as keydown always get's upper case.
9583 var cc = String.fromCharCode(event.getCharCode());
9584 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9588 adjustWidth : function(tag, w){
9589 tag = tag.toLowerCase();
9590 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9591 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9595 if(tag == 'textarea'){
9598 }else if(Roo.isOpera){
9602 if(tag == 'textarea'){
9610 setFieldLabel : function(v)
9617 var ar = this.el.select('label > span',true);
9619 if (ar.elements.length) {
9620 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9621 this.fieldLabel = v;
9625 var br = this.el.select('label',true);
9627 if(br.elements.length) {
9628 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9629 this.fieldLabel = v;
9633 Roo.log('Cannot Found any of label > span || label in input');
9637 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9638 this.fieldLabel = v;
9653 * @class Roo.bootstrap.TextArea
9654 * @extends Roo.bootstrap.Input
9655 * Bootstrap TextArea class
9656 * @cfg {Number} cols Specifies the visible width of a text area
9657 * @cfg {Number} rows Specifies the visible number of lines in a text area
9658 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9659 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9660 * @cfg {string} html text
9663 * Create a new TextArea
9664 * @param {Object} config The config object
9667 Roo.bootstrap.TextArea = function(config){
9668 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9672 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9682 getAutoCreate : function(){
9684 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9690 if(this.inputType != 'hidden'){
9691 cfg.cls = 'form-group' //input-group
9699 value : this.value || '',
9700 html: this.html || '',
9701 cls : 'form-control',
9702 placeholder : this.placeholder || ''
9706 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9707 input.maxLength = this.maxLength;
9711 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9715 input.cols = this.cols;
9718 if (this.readOnly) {
9719 input.readonly = true;
9723 input.name = this.name;
9727 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9731 ['xs','sm','md','lg'].map(function(size){
9732 if (settings[size]) {
9733 cfg.cls += ' col-' + size + '-' + settings[size];
9737 var inputblock = input;
9739 if(this.hasFeedback && !this.allowBlank){
9743 cls: 'glyphicon form-control-feedback'
9747 cls : 'has-feedback',
9756 if (this.before || this.after) {
9759 cls : 'input-group',
9763 inputblock.cn.push({
9765 cls : 'input-group-addon',
9770 inputblock.cn.push(input);
9772 if(this.hasFeedback && !this.allowBlank){
9773 inputblock.cls += ' has-feedback';
9774 inputblock.cn.push(feedback);
9778 inputblock.cn.push({
9780 cls : 'input-group-addon',
9787 if (align ==='left' && this.fieldLabel.length) {
9792 cls : 'control-label',
9793 html : this.fieldLabel
9804 if(this.labelWidth > 12){
9805 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9808 if(this.labelWidth < 13 && this.labelmd == 0){
9809 this.labelmd = this.labelWidth;
9812 if(this.labellg > 0){
9813 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9814 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9817 if(this.labelmd > 0){
9818 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9819 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9822 if(this.labelsm > 0){
9823 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9824 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9827 if(this.labelxs > 0){
9828 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9829 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9832 } else if ( this.fieldLabel.length) {
9837 //cls : 'input-group-addon',
9838 html : this.fieldLabel
9856 if (this.disabled) {
9857 input.disabled=true;
9864 * return the real textarea element.
9866 inputEl: function ()
9868 return this.el.select('textarea.form-control',true).first();
9872 * Clear any invalid styles/messages for this field
9874 clearInvalid : function()
9877 if(!this.el || this.preventMark){ // not rendered
9881 var label = this.el.select('label', true).first();
9882 var icon = this.el.select('i.fa-star', true).first();
9888 this.el.removeClass(this.invalidClass);
9890 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9892 var feedback = this.el.select('.form-control-feedback', true).first();
9895 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9900 this.fireEvent('valid', this);
9904 * Mark this field as valid
9906 markValid : function()
9908 if(!this.el || this.preventMark){ // not rendered
9912 this.el.removeClass([this.invalidClass, this.validClass]);
9914 var feedback = this.el.select('.form-control-feedback', true).first();
9917 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9920 if(this.disabled || this.allowBlank){
9924 var label = this.el.select('label', true).first();
9925 var icon = this.el.select('i.fa-star', true).first();
9931 this.el.addClass(this.validClass);
9933 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9935 var feedback = this.el.select('.form-control-feedback', true).first();
9938 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9939 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9944 this.fireEvent('valid', this);
9948 * Mark this field as invalid
9949 * @param {String} msg The validation message
9951 markInvalid : function(msg)
9953 if(!this.el || this.preventMark){ // not rendered
9957 this.el.removeClass([this.invalidClass, this.validClass]);
9959 var feedback = this.el.select('.form-control-feedback', true).first();
9962 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9965 if(this.disabled || this.allowBlank){
9969 var label = this.el.select('label', true).first();
9970 var icon = this.el.select('i.fa-star', true).first();
9972 if(!this.getValue().length && label && !icon){
9973 this.el.createChild({
9975 cls : 'text-danger fa fa-lg fa-star',
9976 tooltip : 'This field is required',
9977 style : 'margin-right:5px;'
9981 this.el.addClass(this.invalidClass);
9983 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9985 var feedback = this.el.select('.form-control-feedback', true).first();
9988 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9990 if(this.getValue().length || this.forceFeedback){
9991 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9998 this.fireEvent('invalid', this, msg);
10006 * trigger field - base class for combo..
10011 * @class Roo.bootstrap.TriggerField
10012 * @extends Roo.bootstrap.Input
10013 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10014 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10015 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10016 * for which you can provide a custom implementation. For example:
10018 var trigger = new Roo.bootstrap.TriggerField();
10019 trigger.onTriggerClick = myTriggerFn;
10020 trigger.applyTo('my-field');
10023 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10024 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10025 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10026 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10027 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10030 * Create a new TriggerField.
10031 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10032 * to the base TextField)
10034 Roo.bootstrap.TriggerField = function(config){
10035 this.mimicing = false;
10036 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10039 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10041 * @cfg {String} triggerClass A CSS class to apply to the trigger
10044 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10049 * @cfg {Boolean} removable (true|false) special filter default false
10053 /** @cfg {Boolean} grow @hide */
10054 /** @cfg {Number} growMin @hide */
10055 /** @cfg {Number} growMax @hide */
10061 autoSize: Roo.emptyFn,
10065 deferHeight : true,
10068 actionMode : 'wrap',
10073 getAutoCreate : function(){
10075 var align = this.labelAlign || this.parentLabelAlign();
10080 cls: 'form-group' //input-group
10087 type : this.inputType,
10088 cls : 'form-control',
10089 autocomplete: 'new-password',
10090 placeholder : this.placeholder || ''
10094 input.name = this.name;
10097 input.cls += ' input-' + this.size;
10100 if (this.disabled) {
10101 input.disabled=true;
10104 var inputblock = input;
10106 if(this.hasFeedback && !this.allowBlank){
10110 cls: 'glyphicon form-control-feedback'
10113 if(this.removable && !this.editable && !this.tickable){
10115 cls : 'has-feedback',
10121 cls : 'roo-combo-removable-btn close'
10128 cls : 'has-feedback',
10137 if(this.removable && !this.editable && !this.tickable){
10139 cls : 'roo-removable',
10145 cls : 'roo-combo-removable-btn close'
10152 if (this.before || this.after) {
10155 cls : 'input-group',
10159 inputblock.cn.push({
10161 cls : 'input-group-addon',
10166 inputblock.cn.push(input);
10168 if(this.hasFeedback && !this.allowBlank){
10169 inputblock.cls += ' has-feedback';
10170 inputblock.cn.push(feedback);
10174 inputblock.cn.push({
10176 cls : 'input-group-addon',
10189 cls: 'form-hidden-field'
10203 cls: 'form-hidden-field'
10207 cls: 'roo-select2-choices',
10211 cls: 'roo-select2-search-field',
10224 cls: 'roo-select2-container input-group',
10229 // cls: 'typeahead typeahead-long dropdown-menu',
10230 // style: 'display:none'
10235 if(!this.multiple && this.showToggleBtn){
10241 if (this.caret != false) {
10244 cls: 'fa fa-' + this.caret
10251 cls : 'input-group-addon btn dropdown-toggle',
10256 cls: 'combobox-clear',
10270 combobox.cls += ' roo-select2-container-multi';
10273 if (align ==='left' && this.fieldLabel.length) {
10275 cfg.cls += ' roo-form-group-label-left';
10280 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10281 tooltip : 'This field is required'
10286 cls : 'control-label',
10287 html : this.fieldLabel
10299 var labelCfg = cfg.cn[1];
10300 var contentCfg = cfg.cn[2];
10302 if(this.indicatorpos == 'right'){
10307 cls : 'control-label',
10311 html : this.fieldLabel
10315 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10316 tooltip : 'This field is required'
10329 labelCfg = cfg.cn[0];
10330 contentCfg = cfg.cn[1];
10333 if(this.labelWidth > 12){
10334 labelCfg.style = "width: " + this.labelWidth + 'px';
10337 if(this.labelWidth < 13 && this.labelmd == 0){
10338 this.labelmd = this.labelWidth;
10341 if(this.labellg > 0){
10342 labelCfg.cls += ' col-lg-' + this.labellg;
10343 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10346 if(this.labelmd > 0){
10347 labelCfg.cls += ' col-md-' + this.labelmd;
10348 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10351 if(this.labelsm > 0){
10352 labelCfg.cls += ' col-sm-' + this.labelsm;
10353 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10356 if(this.labelxs > 0){
10357 labelCfg.cls += ' col-xs-' + this.labelxs;
10358 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10361 } else if ( this.fieldLabel.length) {
10362 // Roo.log(" label");
10366 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10367 tooltip : 'This field is required'
10371 //cls : 'input-group-addon',
10372 html : this.fieldLabel
10380 if(this.indicatorpos == 'right'){
10388 html : this.fieldLabel
10392 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10393 tooltip : 'This field is required'
10406 // Roo.log(" no label && no align");
10413 ['xs','sm','md','lg'].map(function(size){
10414 if (settings[size]) {
10415 cfg.cls += ' col-' + size + '-' + settings[size];
10426 onResize : function(w, h){
10427 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10428 // if(typeof w == 'number'){
10429 // var x = w - this.trigger.getWidth();
10430 // this.inputEl().setWidth(this.adjustWidth('input', x));
10431 // this.trigger.setStyle('left', x+'px');
10436 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10439 getResizeEl : function(){
10440 return this.inputEl();
10444 getPositionEl : function(){
10445 return this.inputEl();
10449 alignErrorIcon : function(){
10450 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10454 initEvents : function(){
10458 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10459 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10460 if(!this.multiple && this.showToggleBtn){
10461 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10462 if(this.hideTrigger){
10463 this.trigger.setDisplayed(false);
10465 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10469 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10472 if(this.removable && !this.editable && !this.tickable){
10473 var close = this.closeTriggerEl();
10476 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10477 close.on('click', this.removeBtnClick, this, close);
10481 //this.trigger.addClassOnOver('x-form-trigger-over');
10482 //this.trigger.addClassOnClick('x-form-trigger-click');
10485 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10489 closeTriggerEl : function()
10491 var close = this.el.select('.roo-combo-removable-btn', true).first();
10492 return close ? close : false;
10495 removeBtnClick : function(e, h, el)
10497 e.preventDefault();
10499 if(this.fireEvent("remove", this) !== false){
10501 this.fireEvent("afterremove", this)
10505 createList : function()
10507 this.list = Roo.get(document.body).createChild({
10509 cls: 'typeahead typeahead-long dropdown-menu',
10510 style: 'display:none'
10513 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10518 initTrigger : function(){
10523 onDestroy : function(){
10525 this.trigger.removeAllListeners();
10526 // this.trigger.remove();
10529 // this.wrap.remove();
10531 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10535 onFocus : function(){
10536 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10538 if(!this.mimicing){
10539 this.wrap.addClass('x-trigger-wrap-focus');
10540 this.mimicing = true;
10541 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10542 if(this.monitorTab){
10543 this.el.on("keydown", this.checkTab, this);
10550 checkTab : function(e){
10551 if(e.getKey() == e.TAB){
10552 this.triggerBlur();
10557 onBlur : function(){
10562 mimicBlur : function(e, t){
10564 if(!this.wrap.contains(t) && this.validateBlur()){
10565 this.triggerBlur();
10571 triggerBlur : function(){
10572 this.mimicing = false;
10573 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10574 if(this.monitorTab){
10575 this.el.un("keydown", this.checkTab, this);
10577 //this.wrap.removeClass('x-trigger-wrap-focus');
10578 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10582 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10583 validateBlur : function(e, t){
10588 onDisable : function(){
10589 this.inputEl().dom.disabled = true;
10590 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10592 // this.wrap.addClass('x-item-disabled');
10597 onEnable : function(){
10598 this.inputEl().dom.disabled = false;
10599 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10601 // this.el.removeClass('x-item-disabled');
10606 onShow : function(){
10607 var ae = this.getActionEl();
10610 ae.dom.style.display = '';
10611 ae.dom.style.visibility = 'visible';
10617 onHide : function(){
10618 var ae = this.getActionEl();
10619 ae.dom.style.display = 'none';
10623 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10624 * by an implementing function.
10626 * @param {EventObject} e
10628 onTriggerClick : Roo.emptyFn
10632 * Ext JS Library 1.1.1
10633 * Copyright(c) 2006-2007, Ext JS, LLC.
10635 * Originally Released Under LGPL - original licence link has changed is not relivant.
10638 * <script type="text/javascript">
10643 * @class Roo.data.SortTypes
10645 * Defines the default sorting (casting?) comparison functions used when sorting data.
10647 Roo.data.SortTypes = {
10649 * Default sort that does nothing
10650 * @param {Mixed} s The value being converted
10651 * @return {Mixed} The comparison value
10653 none : function(s){
10658 * The regular expression used to strip tags
10662 stripTagsRE : /<\/?[^>]+>/gi,
10665 * Strips all HTML tags to sort on text only
10666 * @param {Mixed} s The value being converted
10667 * @return {String} The comparison value
10669 asText : function(s){
10670 return String(s).replace(this.stripTagsRE, "");
10674 * Strips all HTML tags to sort on text only - Case insensitive
10675 * @param {Mixed} s The value being converted
10676 * @return {String} The comparison value
10678 asUCText : function(s){
10679 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10683 * Case insensitive string
10684 * @param {Mixed} s The value being converted
10685 * @return {String} The comparison value
10687 asUCString : function(s) {
10688 return String(s).toUpperCase();
10693 * @param {Mixed} s The value being converted
10694 * @return {Number} The comparison value
10696 asDate : function(s) {
10700 if(s instanceof Date){
10701 return s.getTime();
10703 return Date.parse(String(s));
10708 * @param {Mixed} s The value being converted
10709 * @return {Float} The comparison value
10711 asFloat : function(s) {
10712 var val = parseFloat(String(s).replace(/,/g, ""));
10721 * @param {Mixed} s The value being converted
10722 * @return {Number} The comparison value
10724 asInt : function(s) {
10725 var val = parseInt(String(s).replace(/,/g, ""));
10733 * Ext JS Library 1.1.1
10734 * Copyright(c) 2006-2007, Ext JS, LLC.
10736 * Originally Released Under LGPL - original licence link has changed is not relivant.
10739 * <script type="text/javascript">
10743 * @class Roo.data.Record
10744 * Instances of this class encapsulate both record <em>definition</em> information, and record
10745 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10746 * to access Records cached in an {@link Roo.data.Store} object.<br>
10748 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10749 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10752 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10754 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10755 * {@link #create}. The parameters are the same.
10756 * @param {Array} data An associative Array of data values keyed by the field name.
10757 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10758 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10759 * not specified an integer id is generated.
10761 Roo.data.Record = function(data, id){
10762 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10767 * Generate a constructor for a specific record layout.
10768 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10769 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10770 * Each field definition object may contain the following properties: <ul>
10771 * <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,
10772 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10773 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10774 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10775 * is being used, then this is a string containing the javascript expression to reference the data relative to
10776 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10777 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10778 * this may be omitted.</p></li>
10779 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10780 * <ul><li>auto (Default, implies no conversion)</li>
10785 * <li>date</li></ul></p></li>
10786 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10787 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10788 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10789 * by the Reader into an object that will be stored in the Record. It is passed the
10790 * following parameters:<ul>
10791 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10793 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10795 * <br>usage:<br><pre><code>
10796 var TopicRecord = Roo.data.Record.create(
10797 {name: 'title', mapping: 'topic_title'},
10798 {name: 'author', mapping: 'username'},
10799 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10800 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10801 {name: 'lastPoster', mapping: 'user2'},
10802 {name: 'excerpt', mapping: 'post_text'}
10805 var myNewRecord = new TopicRecord({
10806 title: 'Do my job please',
10809 lastPost: new Date(),
10810 lastPoster: 'Animal',
10811 excerpt: 'No way dude!'
10813 myStore.add(myNewRecord);
10818 Roo.data.Record.create = function(o){
10819 var f = function(){
10820 f.superclass.constructor.apply(this, arguments);
10822 Roo.extend(f, Roo.data.Record);
10823 var p = f.prototype;
10824 p.fields = new Roo.util.MixedCollection(false, function(field){
10827 for(var i = 0, len = o.length; i < len; i++){
10828 p.fields.add(new Roo.data.Field(o[i]));
10830 f.getField = function(name){
10831 return p.fields.get(name);
10836 Roo.data.Record.AUTO_ID = 1000;
10837 Roo.data.Record.EDIT = 'edit';
10838 Roo.data.Record.REJECT = 'reject';
10839 Roo.data.Record.COMMIT = 'commit';
10841 Roo.data.Record.prototype = {
10843 * Readonly flag - true if this record has been modified.
10852 join : function(store){
10853 this.store = store;
10857 * Set the named field to the specified value.
10858 * @param {String} name The name of the field to set.
10859 * @param {Object} value The value to set the field to.
10861 set : function(name, value){
10862 if(this.data[name] == value){
10866 if(!this.modified){
10867 this.modified = {};
10869 if(typeof this.modified[name] == 'undefined'){
10870 this.modified[name] = this.data[name];
10872 this.data[name] = value;
10873 if(!this.editing && this.store){
10874 this.store.afterEdit(this);
10879 * Get the value of the named field.
10880 * @param {String} name The name of the field to get the value of.
10881 * @return {Object} The value of the field.
10883 get : function(name){
10884 return this.data[name];
10888 beginEdit : function(){
10889 this.editing = true;
10890 this.modified = {};
10894 cancelEdit : function(){
10895 this.editing = false;
10896 delete this.modified;
10900 endEdit : function(){
10901 this.editing = false;
10902 if(this.dirty && this.store){
10903 this.store.afterEdit(this);
10908 * Usually called by the {@link Roo.data.Store} which owns the Record.
10909 * Rejects all changes made to the Record since either creation, or the last commit operation.
10910 * Modified fields are reverted to their original values.
10912 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10913 * of reject operations.
10915 reject : function(){
10916 var m = this.modified;
10918 if(typeof m[n] != "function"){
10919 this.data[n] = m[n];
10922 this.dirty = false;
10923 delete this.modified;
10924 this.editing = false;
10926 this.store.afterReject(this);
10931 * Usually called by the {@link Roo.data.Store} which owns the Record.
10932 * Commits all changes made to the Record since either creation, or the last commit operation.
10934 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10935 * of commit operations.
10937 commit : function(){
10938 this.dirty = false;
10939 delete this.modified;
10940 this.editing = false;
10942 this.store.afterCommit(this);
10947 hasError : function(){
10948 return this.error != null;
10952 clearError : function(){
10957 * Creates a copy of this record.
10958 * @param {String} id (optional) A new record id if you don't want to use this record's id
10961 copy : function(newId) {
10962 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10966 * Ext JS Library 1.1.1
10967 * Copyright(c) 2006-2007, Ext JS, LLC.
10969 * Originally Released Under LGPL - original licence link has changed is not relivant.
10972 * <script type="text/javascript">
10978 * @class Roo.data.Store
10979 * @extends Roo.util.Observable
10980 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10981 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10983 * 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
10984 * has no knowledge of the format of the data returned by the Proxy.<br>
10986 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10987 * instances from the data object. These records are cached and made available through accessor functions.
10989 * Creates a new Store.
10990 * @param {Object} config A config object containing the objects needed for the Store to access data,
10991 * and read the data into Records.
10993 Roo.data.Store = function(config){
10994 this.data = new Roo.util.MixedCollection(false);
10995 this.data.getKey = function(o){
10998 this.baseParams = {};
11000 this.paramNames = {
11005 "multisort" : "_multisort"
11008 if(config && config.data){
11009 this.inlineData = config.data;
11010 delete config.data;
11013 Roo.apply(this, config);
11015 if(this.reader){ // reader passed
11016 this.reader = Roo.factory(this.reader, Roo.data);
11017 this.reader.xmodule = this.xmodule || false;
11018 if(!this.recordType){
11019 this.recordType = this.reader.recordType;
11021 if(this.reader.onMetaChange){
11022 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11026 if(this.recordType){
11027 this.fields = this.recordType.prototype.fields;
11029 this.modified = [];
11033 * @event datachanged
11034 * Fires when the data cache has changed, and a widget which is using this Store
11035 * as a Record cache should refresh its view.
11036 * @param {Store} this
11038 datachanged : true,
11040 * @event metachange
11041 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11042 * @param {Store} this
11043 * @param {Object} meta The JSON metadata
11048 * Fires when Records have been added to the Store
11049 * @param {Store} this
11050 * @param {Roo.data.Record[]} records The array of Records added
11051 * @param {Number} index The index at which the record(s) were added
11056 * Fires when a Record has been removed from the Store
11057 * @param {Store} this
11058 * @param {Roo.data.Record} record The Record that was removed
11059 * @param {Number} index The index at which the record was removed
11064 * Fires when a Record has been updated
11065 * @param {Store} this
11066 * @param {Roo.data.Record} record The Record that was updated
11067 * @param {String} operation The update operation being performed. Value may be one of:
11069 Roo.data.Record.EDIT
11070 Roo.data.Record.REJECT
11071 Roo.data.Record.COMMIT
11077 * Fires when the data cache has been cleared.
11078 * @param {Store} this
11082 * @event beforeload
11083 * Fires before a request is made for a new data object. If the beforeload handler returns false
11084 * the load action will be canceled.
11085 * @param {Store} this
11086 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11090 * @event beforeloadadd
11091 * Fires after a new set of Records has been loaded.
11092 * @param {Store} this
11093 * @param {Roo.data.Record[]} records The Records that were loaded
11094 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11096 beforeloadadd : true,
11099 * Fires after a new set of Records has been loaded, before they are added to the store.
11100 * @param {Store} this
11101 * @param {Roo.data.Record[]} records The Records that were loaded
11102 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11103 * @params {Object} return from reader
11107 * @event loadexception
11108 * Fires if an exception occurs in the Proxy during loading.
11109 * Called with the signature of the Proxy's "loadexception" event.
11110 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11113 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11114 * @param {Object} load options
11115 * @param {Object} jsonData from your request (normally this contains the Exception)
11117 loadexception : true
11121 this.proxy = Roo.factory(this.proxy, Roo.data);
11122 this.proxy.xmodule = this.xmodule || false;
11123 this.relayEvents(this.proxy, ["loadexception"]);
11125 this.sortToggle = {};
11126 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11128 Roo.data.Store.superclass.constructor.call(this);
11130 if(this.inlineData){
11131 this.loadData(this.inlineData);
11132 delete this.inlineData;
11136 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11138 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11139 * without a remote query - used by combo/forms at present.
11143 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11146 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11149 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11150 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11153 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11154 * on any HTTP request
11157 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11160 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11164 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11165 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11167 remoteSort : false,
11170 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11171 * loaded or when a record is removed. (defaults to false).
11173 pruneModifiedRecords : false,
11176 lastOptions : null,
11179 * Add Records to the Store and fires the add event.
11180 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11182 add : function(records){
11183 records = [].concat(records);
11184 for(var i = 0, len = records.length; i < len; i++){
11185 records[i].join(this);
11187 var index = this.data.length;
11188 this.data.addAll(records);
11189 this.fireEvent("add", this, records, index);
11193 * Remove a Record from the Store and fires the remove event.
11194 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11196 remove : function(record){
11197 var index = this.data.indexOf(record);
11198 this.data.removeAt(index);
11200 if(this.pruneModifiedRecords){
11201 this.modified.remove(record);
11203 this.fireEvent("remove", this, record, index);
11207 * Remove all Records from the Store and fires the clear event.
11209 removeAll : function(){
11211 if(this.pruneModifiedRecords){
11212 this.modified = [];
11214 this.fireEvent("clear", this);
11218 * Inserts Records to the Store at the given index and fires the add event.
11219 * @param {Number} index The start index at which to insert the passed Records.
11220 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11222 insert : function(index, records){
11223 records = [].concat(records);
11224 for(var i = 0, len = records.length; i < len; i++){
11225 this.data.insert(index, records[i]);
11226 records[i].join(this);
11228 this.fireEvent("add", this, records, index);
11232 * Get the index within the cache of the passed Record.
11233 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11234 * @return {Number} The index of the passed Record. Returns -1 if not found.
11236 indexOf : function(record){
11237 return this.data.indexOf(record);
11241 * Get the index within the cache of the Record with the passed id.
11242 * @param {String} id The id of the Record to find.
11243 * @return {Number} The index of the Record. Returns -1 if not found.
11245 indexOfId : function(id){
11246 return this.data.indexOfKey(id);
11250 * Get the Record with the specified id.
11251 * @param {String} id The id of the Record to find.
11252 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11254 getById : function(id){
11255 return this.data.key(id);
11259 * Get the Record at the specified index.
11260 * @param {Number} index The index of the Record to find.
11261 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11263 getAt : function(index){
11264 return this.data.itemAt(index);
11268 * Returns a range of Records between specified indices.
11269 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11270 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11271 * @return {Roo.data.Record[]} An array of Records
11273 getRange : function(start, end){
11274 return this.data.getRange(start, end);
11278 storeOptions : function(o){
11279 o = Roo.apply({}, o);
11282 this.lastOptions = o;
11286 * Loads the Record cache from the configured Proxy using the configured Reader.
11288 * If using remote paging, then the first load call must specify the <em>start</em>
11289 * and <em>limit</em> properties in the options.params property to establish the initial
11290 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11292 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11293 * and this call will return before the new data has been loaded. Perform any post-processing
11294 * in a callback function, or in a "load" event handler.</strong>
11296 * @param {Object} options An object containing properties which control loading options:<ul>
11297 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11298 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11299 * passed the following arguments:<ul>
11300 * <li>r : Roo.data.Record[]</li>
11301 * <li>options: Options object from the load call</li>
11302 * <li>success: Boolean success indicator</li></ul></li>
11303 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11304 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11307 load : function(options){
11308 options = options || {};
11309 if(this.fireEvent("beforeload", this, options) !== false){
11310 this.storeOptions(options);
11311 var p = Roo.apply(options.params || {}, this.baseParams);
11312 // if meta was not loaded from remote source.. try requesting it.
11313 if (!this.reader.metaFromRemote) {
11314 p._requestMeta = 1;
11316 if(this.sortInfo && this.remoteSort){
11317 var pn = this.paramNames;
11318 p[pn["sort"]] = this.sortInfo.field;
11319 p[pn["dir"]] = this.sortInfo.direction;
11321 if (this.multiSort) {
11322 var pn = this.paramNames;
11323 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11326 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11331 * Reloads the Record cache from the configured Proxy using the configured Reader and
11332 * the options from the last load operation performed.
11333 * @param {Object} options (optional) An object containing properties which may override the options
11334 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11335 * the most recently used options are reused).
11337 reload : function(options){
11338 this.load(Roo.applyIf(options||{}, this.lastOptions));
11342 // Called as a callback by the Reader during a load operation.
11343 loadRecords : function(o, options, success){
11344 if(!o || success === false){
11345 if(success !== false){
11346 this.fireEvent("load", this, [], options, o);
11348 if(options.callback){
11349 options.callback.call(options.scope || this, [], options, false);
11353 // if data returned failure - throw an exception.
11354 if (o.success === false) {
11355 // show a message if no listener is registered.
11356 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11357 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11359 // loadmask wil be hooked into this..
11360 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11363 var r = o.records, t = o.totalRecords || r.length;
11365 this.fireEvent("beforeloadadd", this, r, options, o);
11367 if(!options || options.add !== true){
11368 if(this.pruneModifiedRecords){
11369 this.modified = [];
11371 for(var i = 0, len = r.length; i < len; i++){
11375 this.data = this.snapshot;
11376 delete this.snapshot;
11379 this.data.addAll(r);
11380 this.totalLength = t;
11382 this.fireEvent("datachanged", this);
11384 this.totalLength = Math.max(t, this.data.length+r.length);
11388 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11390 var e = new Roo.data.Record({});
11392 e.set(this.parent.displayField, this.parent.emptyTitle);
11393 e.set(this.parent.valueField, '');
11398 this.fireEvent("load", this, r, options, o);
11399 if(options.callback){
11400 options.callback.call(options.scope || this, r, options, true);
11406 * Loads data from a passed data block. A Reader which understands the format of the data
11407 * must have been configured in the constructor.
11408 * @param {Object} data The data block from which to read the Records. The format of the data expected
11409 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11410 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11412 loadData : function(o, append){
11413 var r = this.reader.readRecords(o);
11414 this.loadRecords(r, {add: append}, true);
11418 * Gets the number of cached records.
11420 * <em>If using paging, this may not be the total size of the dataset. If the data object
11421 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11422 * the data set size</em>
11424 getCount : function(){
11425 return this.data.length || 0;
11429 * Gets the total number of records in the dataset as returned by the server.
11431 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11432 * the dataset size</em>
11434 getTotalCount : function(){
11435 return this.totalLength || 0;
11439 * Returns the sort state of the Store as an object with two properties:
11441 field {String} The name of the field by which the Records are sorted
11442 direction {String} The sort order, "ASC" or "DESC"
11445 getSortState : function(){
11446 return this.sortInfo;
11450 applySort : function(){
11451 if(this.sortInfo && !this.remoteSort){
11452 var s = this.sortInfo, f = s.field;
11453 var st = this.fields.get(f).sortType;
11454 var fn = function(r1, r2){
11455 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11456 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11458 this.data.sort(s.direction, fn);
11459 if(this.snapshot && this.snapshot != this.data){
11460 this.snapshot.sort(s.direction, fn);
11466 * Sets the default sort column and order to be used by the next load operation.
11467 * @param {String} fieldName The name of the field to sort by.
11468 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11470 setDefaultSort : function(field, dir){
11471 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11475 * Sort the Records.
11476 * If remote sorting is used, the sort is performed on the server, and the cache is
11477 * reloaded. If local sorting is used, the cache is sorted internally.
11478 * @param {String} fieldName The name of the field to sort by.
11479 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11481 sort : function(fieldName, dir){
11482 var f = this.fields.get(fieldName);
11484 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11486 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11487 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11492 this.sortToggle[f.name] = dir;
11493 this.sortInfo = {field: f.name, direction: dir};
11494 if(!this.remoteSort){
11496 this.fireEvent("datachanged", this);
11498 this.load(this.lastOptions);
11503 * Calls the specified function for each of the Records in the cache.
11504 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11505 * Returning <em>false</em> aborts and exits the iteration.
11506 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11508 each : function(fn, scope){
11509 this.data.each(fn, scope);
11513 * Gets all records modified since the last commit. Modified records are persisted across load operations
11514 * (e.g., during paging).
11515 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11517 getModifiedRecords : function(){
11518 return this.modified;
11522 createFilterFn : function(property, value, anyMatch){
11523 if(!value.exec){ // not a regex
11524 value = String(value);
11525 if(value.length == 0){
11528 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11530 return function(r){
11531 return value.test(r.data[property]);
11536 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11537 * @param {String} property A field on your records
11538 * @param {Number} start The record index to start at (defaults to 0)
11539 * @param {Number} end The last record index to include (defaults to length - 1)
11540 * @return {Number} The sum
11542 sum : function(property, start, end){
11543 var rs = this.data.items, v = 0;
11544 start = start || 0;
11545 end = (end || end === 0) ? end : rs.length-1;
11547 for(var i = start; i <= end; i++){
11548 v += (rs[i].data[property] || 0);
11554 * Filter the records by a specified property.
11555 * @param {String} field A field on your records
11556 * @param {String/RegExp} value Either a string that the field
11557 * should start with or a RegExp to test against the field
11558 * @param {Boolean} anyMatch True to match any part not just the beginning
11560 filter : function(property, value, anyMatch){
11561 var fn = this.createFilterFn(property, value, anyMatch);
11562 return fn ? this.filterBy(fn) : this.clearFilter();
11566 * Filter by a function. The specified function will be called with each
11567 * record in this data source. If the function returns true the record is included,
11568 * otherwise it is filtered.
11569 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11570 * @param {Object} scope (optional) The scope of the function (defaults to this)
11572 filterBy : function(fn, scope){
11573 this.snapshot = this.snapshot || this.data;
11574 this.data = this.queryBy(fn, scope||this);
11575 this.fireEvent("datachanged", this);
11579 * Query the records by a specified property.
11580 * @param {String} field A field on your records
11581 * @param {String/RegExp} value Either a string that the field
11582 * should start with or a RegExp to test against the field
11583 * @param {Boolean} anyMatch True to match any part not just the beginning
11584 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11586 query : function(property, value, anyMatch){
11587 var fn = this.createFilterFn(property, value, anyMatch);
11588 return fn ? this.queryBy(fn) : this.data.clone();
11592 * Query by a function. The specified function will be called with each
11593 * record in this data source. If the function returns true the record is included
11595 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11596 * @param {Object} scope (optional) The scope of the function (defaults to this)
11597 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11599 queryBy : function(fn, scope){
11600 var data = this.snapshot || this.data;
11601 return data.filterBy(fn, scope||this);
11605 * Collects unique values for a particular dataIndex from this store.
11606 * @param {String} dataIndex The property to collect
11607 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11608 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11609 * @return {Array} An array of the unique values
11611 collect : function(dataIndex, allowNull, bypassFilter){
11612 var d = (bypassFilter === true && this.snapshot) ?
11613 this.snapshot.items : this.data.items;
11614 var v, sv, r = [], l = {};
11615 for(var i = 0, len = d.length; i < len; i++){
11616 v = d[i].data[dataIndex];
11618 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11627 * Revert to a view of the Record cache with no filtering applied.
11628 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11630 clearFilter : function(suppressEvent){
11631 if(this.snapshot && this.snapshot != this.data){
11632 this.data = this.snapshot;
11633 delete this.snapshot;
11634 if(suppressEvent !== true){
11635 this.fireEvent("datachanged", this);
11641 afterEdit : function(record){
11642 if(this.modified.indexOf(record) == -1){
11643 this.modified.push(record);
11645 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11649 afterReject : function(record){
11650 this.modified.remove(record);
11651 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11655 afterCommit : function(record){
11656 this.modified.remove(record);
11657 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11661 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11662 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11664 commitChanges : function(){
11665 var m = this.modified.slice(0);
11666 this.modified = [];
11667 for(var i = 0, len = m.length; i < len; i++){
11673 * Cancel outstanding changes on all changed records.
11675 rejectChanges : function(){
11676 var m = this.modified.slice(0);
11677 this.modified = [];
11678 for(var i = 0, len = m.length; i < len; i++){
11683 onMetaChange : function(meta, rtype, o){
11684 this.recordType = rtype;
11685 this.fields = rtype.prototype.fields;
11686 delete this.snapshot;
11687 this.sortInfo = meta.sortInfo || this.sortInfo;
11688 this.modified = [];
11689 this.fireEvent('metachange', this, this.reader.meta);
11692 moveIndex : function(data, type)
11694 var index = this.indexOf(data);
11696 var newIndex = index + type;
11700 this.insert(newIndex, data);
11705 * Ext JS Library 1.1.1
11706 * Copyright(c) 2006-2007, Ext JS, LLC.
11708 * Originally Released Under LGPL - original licence link has changed is not relivant.
11711 * <script type="text/javascript">
11715 * @class Roo.data.SimpleStore
11716 * @extends Roo.data.Store
11717 * Small helper class to make creating Stores from Array data easier.
11718 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11719 * @cfg {Array} fields An array of field definition objects, or field name strings.
11720 * @cfg {Array} data The multi-dimensional array of data
11722 * @param {Object} config
11724 Roo.data.SimpleStore = function(config){
11725 Roo.data.SimpleStore.superclass.constructor.call(this, {
11727 reader: new Roo.data.ArrayReader({
11730 Roo.data.Record.create(config.fields)
11732 proxy : new Roo.data.MemoryProxy(config.data)
11736 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11738 * Ext JS Library 1.1.1
11739 * Copyright(c) 2006-2007, Ext JS, LLC.
11741 * Originally Released Under LGPL - original licence link has changed is not relivant.
11744 * <script type="text/javascript">
11749 * @extends Roo.data.Store
11750 * @class Roo.data.JsonStore
11751 * Small helper class to make creating Stores for JSON data easier. <br/>
11753 var store = new Roo.data.JsonStore({
11754 url: 'get-images.php',
11756 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11759 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11760 * JsonReader and HttpProxy (unless inline data is provided).</b>
11761 * @cfg {Array} fields An array of field definition objects, or field name strings.
11763 * @param {Object} config
11765 Roo.data.JsonStore = function(c){
11766 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11767 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11768 reader: new Roo.data.JsonReader(c, c.fields)
11771 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11773 * Ext JS Library 1.1.1
11774 * Copyright(c) 2006-2007, Ext JS, LLC.
11776 * Originally Released Under LGPL - original licence link has changed is not relivant.
11779 * <script type="text/javascript">
11783 Roo.data.Field = function(config){
11784 if(typeof config == "string"){
11785 config = {name: config};
11787 Roo.apply(this, config);
11790 this.type = "auto";
11793 var st = Roo.data.SortTypes;
11794 // named sortTypes are supported, here we look them up
11795 if(typeof this.sortType == "string"){
11796 this.sortType = st[this.sortType];
11799 // set default sortType for strings and dates
11800 if(!this.sortType){
11803 this.sortType = st.asUCString;
11806 this.sortType = st.asDate;
11809 this.sortType = st.none;
11814 var stripRe = /[\$,%]/g;
11816 // prebuilt conversion function for this field, instead of
11817 // switching every time we're reading a value
11819 var cv, dateFormat = this.dateFormat;
11824 cv = function(v){ return v; };
11827 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11831 return v !== undefined && v !== null && v !== '' ?
11832 parseInt(String(v).replace(stripRe, ""), 10) : '';
11837 return v !== undefined && v !== null && v !== '' ?
11838 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11843 cv = function(v){ return v === true || v === "true" || v == 1; };
11850 if(v instanceof Date){
11854 if(dateFormat == "timestamp"){
11855 return new Date(v*1000);
11857 return Date.parseDate(v, dateFormat);
11859 var parsed = Date.parse(v);
11860 return parsed ? new Date(parsed) : null;
11869 Roo.data.Field.prototype = {
11877 * Ext JS Library 1.1.1
11878 * Copyright(c) 2006-2007, Ext JS, LLC.
11880 * Originally Released Under LGPL - original licence link has changed is not relivant.
11883 * <script type="text/javascript">
11886 // Base class for reading structured data from a data source. This class is intended to be
11887 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11890 * @class Roo.data.DataReader
11891 * Base class for reading structured data from a data source. This class is intended to be
11892 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11895 Roo.data.DataReader = function(meta, recordType){
11899 this.recordType = recordType instanceof Array ?
11900 Roo.data.Record.create(recordType) : recordType;
11903 Roo.data.DataReader.prototype = {
11905 * Create an empty record
11906 * @param {Object} data (optional) - overlay some values
11907 * @return {Roo.data.Record} record created.
11909 newRow : function(d) {
11911 this.recordType.prototype.fields.each(function(c) {
11913 case 'int' : da[c.name] = 0; break;
11914 case 'date' : da[c.name] = new Date(); break;
11915 case 'float' : da[c.name] = 0.0; break;
11916 case 'boolean' : da[c.name] = false; break;
11917 default : da[c.name] = ""; break;
11921 return new this.recordType(Roo.apply(da, d));
11926 * Ext JS Library 1.1.1
11927 * Copyright(c) 2006-2007, Ext JS, LLC.
11929 * Originally Released Under LGPL - original licence link has changed is not relivant.
11932 * <script type="text/javascript">
11936 * @class Roo.data.DataProxy
11937 * @extends Roo.data.Observable
11938 * This class is an abstract base class for implementations which provide retrieval of
11939 * unformatted data objects.<br>
11941 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11942 * (of the appropriate type which knows how to parse the data object) to provide a block of
11943 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11945 * Custom implementations must implement the load method as described in
11946 * {@link Roo.data.HttpProxy#load}.
11948 Roo.data.DataProxy = function(){
11951 * @event beforeload
11952 * Fires before a network request is made to retrieve a data object.
11953 * @param {Object} This DataProxy object.
11954 * @param {Object} params The params parameter to the load function.
11959 * Fires before the load method's callback is called.
11960 * @param {Object} This DataProxy object.
11961 * @param {Object} o The data object.
11962 * @param {Object} arg The callback argument object passed to the load function.
11966 * @event loadexception
11967 * Fires if an Exception occurs during data retrieval.
11968 * @param {Object} This DataProxy object.
11969 * @param {Object} o The data object.
11970 * @param {Object} arg The callback argument object passed to the load function.
11971 * @param {Object} e The Exception.
11973 loadexception : true
11975 Roo.data.DataProxy.superclass.constructor.call(this);
11978 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11981 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11985 * Ext JS Library 1.1.1
11986 * Copyright(c) 2006-2007, Ext JS, LLC.
11988 * Originally Released Under LGPL - original licence link has changed is not relivant.
11991 * <script type="text/javascript">
11994 * @class Roo.data.MemoryProxy
11995 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11996 * to the Reader when its load method is called.
11998 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12000 Roo.data.MemoryProxy = function(data){
12004 Roo.data.MemoryProxy.superclass.constructor.call(this);
12008 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12011 * Load data from the requested source (in this case an in-memory
12012 * data object passed to the constructor), read the data object into
12013 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12014 * process that block using the passed callback.
12015 * @param {Object} params This parameter is not used by the MemoryProxy class.
12016 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12017 * object into a block of Roo.data.Records.
12018 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12019 * The function must be passed <ul>
12020 * <li>The Record block object</li>
12021 * <li>The "arg" argument from the load function</li>
12022 * <li>A boolean success indicator</li>
12024 * @param {Object} scope The scope in which to call the callback
12025 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12027 load : function(params, reader, callback, scope, arg){
12028 params = params || {};
12031 result = reader.readRecords(this.data);
12033 this.fireEvent("loadexception", this, arg, null, e);
12034 callback.call(scope, null, arg, false);
12037 callback.call(scope, result, arg, true);
12041 update : function(params, records){
12046 * Ext JS Library 1.1.1
12047 * Copyright(c) 2006-2007, Ext JS, LLC.
12049 * Originally Released Under LGPL - original licence link has changed is not relivant.
12052 * <script type="text/javascript">
12055 * @class Roo.data.HttpProxy
12056 * @extends Roo.data.DataProxy
12057 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12058 * configured to reference a certain URL.<br><br>
12060 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12061 * from which the running page was served.<br><br>
12063 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12065 * Be aware that to enable the browser to parse an XML document, the server must set
12066 * the Content-Type header in the HTTP response to "text/xml".
12068 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12069 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12070 * will be used to make the request.
12072 Roo.data.HttpProxy = function(conn){
12073 Roo.data.HttpProxy.superclass.constructor.call(this);
12074 // is conn a conn config or a real conn?
12076 this.useAjax = !conn || !conn.events;
12080 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12081 // thse are take from connection...
12084 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12087 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12088 * extra parameters to each request made by this object. (defaults to undefined)
12091 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12092 * to each request made by this object. (defaults to undefined)
12095 * @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)
12098 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12101 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12107 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12111 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12112 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12113 * a finer-grained basis than the DataProxy events.
12115 getConnection : function(){
12116 return this.useAjax ? Roo.Ajax : this.conn;
12120 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12121 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12122 * process that block using the passed callback.
12123 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12124 * for the request to the remote server.
12125 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12126 * object into a block of Roo.data.Records.
12127 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12128 * The function must be passed <ul>
12129 * <li>The Record block object</li>
12130 * <li>The "arg" argument from the load function</li>
12131 * <li>A boolean success indicator</li>
12133 * @param {Object} scope The scope in which to call the callback
12134 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12136 load : function(params, reader, callback, scope, arg){
12137 if(this.fireEvent("beforeload", this, params) !== false){
12139 params : params || {},
12141 callback : callback,
12146 callback : this.loadResponse,
12150 Roo.applyIf(o, this.conn);
12151 if(this.activeRequest){
12152 Roo.Ajax.abort(this.activeRequest);
12154 this.activeRequest = Roo.Ajax.request(o);
12156 this.conn.request(o);
12159 callback.call(scope||this, null, arg, false);
12164 loadResponse : function(o, success, response){
12165 delete this.activeRequest;
12167 this.fireEvent("loadexception", this, o, response);
12168 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12173 result = o.reader.read(response);
12175 this.fireEvent("loadexception", this, o, response, e);
12176 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12180 this.fireEvent("load", this, o, o.request.arg);
12181 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12185 update : function(dataSet){
12190 updateResponse : function(dataSet){
12195 * Ext JS Library 1.1.1
12196 * Copyright(c) 2006-2007, Ext JS, LLC.
12198 * Originally Released Under LGPL - original licence link has changed is not relivant.
12201 * <script type="text/javascript">
12205 * @class Roo.data.ScriptTagProxy
12206 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12207 * other than the originating domain of the running page.<br><br>
12209 * <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
12210 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12212 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12213 * source code that is used as the source inside a <script> tag.<br><br>
12215 * In order for the browser to process the returned data, the server must wrap the data object
12216 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12217 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12218 * depending on whether the callback name was passed:
12221 boolean scriptTag = false;
12222 String cb = request.getParameter("callback");
12225 response.setContentType("text/javascript");
12227 response.setContentType("application/x-json");
12229 Writer out = response.getWriter();
12231 out.write(cb + "(");
12233 out.print(dataBlock.toJsonString());
12240 * @param {Object} config A configuration object.
12242 Roo.data.ScriptTagProxy = function(config){
12243 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12244 Roo.apply(this, config);
12245 this.head = document.getElementsByTagName("head")[0];
12248 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12250 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12252 * @cfg {String} url The URL from which to request the data object.
12255 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12259 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12260 * the server the name of the callback function set up by the load call to process the returned data object.
12261 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12262 * javascript output which calls this named function passing the data object as its only parameter.
12264 callbackParam : "callback",
12266 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12267 * name to the request.
12272 * Load data from the configured URL, read the data object into
12273 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12274 * process that block using the passed callback.
12275 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12276 * for the request to the remote server.
12277 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12278 * object into a block of Roo.data.Records.
12279 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12280 * The function must be passed <ul>
12281 * <li>The Record block object</li>
12282 * <li>The "arg" argument from the load function</li>
12283 * <li>A boolean success indicator</li>
12285 * @param {Object} scope The scope in which to call the callback
12286 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12288 load : function(params, reader, callback, scope, arg){
12289 if(this.fireEvent("beforeload", this, params) !== false){
12291 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12293 var url = this.url;
12294 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12296 url += "&_dc=" + (new Date().getTime());
12298 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12301 cb : "stcCallback"+transId,
12302 scriptId : "stcScript"+transId,
12306 callback : callback,
12312 window[trans.cb] = function(o){
12313 conn.handleResponse(o, trans);
12316 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12318 if(this.autoAbort !== false){
12322 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12324 var script = document.createElement("script");
12325 script.setAttribute("src", url);
12326 script.setAttribute("type", "text/javascript");
12327 script.setAttribute("id", trans.scriptId);
12328 this.head.appendChild(script);
12330 this.trans = trans;
12332 callback.call(scope||this, null, arg, false);
12337 isLoading : function(){
12338 return this.trans ? true : false;
12342 * Abort the current server request.
12344 abort : function(){
12345 if(this.isLoading()){
12346 this.destroyTrans(this.trans);
12351 destroyTrans : function(trans, isLoaded){
12352 this.head.removeChild(document.getElementById(trans.scriptId));
12353 clearTimeout(trans.timeoutId);
12355 window[trans.cb] = undefined;
12357 delete window[trans.cb];
12360 // if hasn't been loaded, wait for load to remove it to prevent script error
12361 window[trans.cb] = function(){
12362 window[trans.cb] = undefined;
12364 delete window[trans.cb];
12371 handleResponse : function(o, trans){
12372 this.trans = false;
12373 this.destroyTrans(trans, true);
12376 result = trans.reader.readRecords(o);
12378 this.fireEvent("loadexception", this, o, trans.arg, e);
12379 trans.callback.call(trans.scope||window, null, trans.arg, false);
12382 this.fireEvent("load", this, o, trans.arg);
12383 trans.callback.call(trans.scope||window, result, trans.arg, true);
12387 handleFailure : function(trans){
12388 this.trans = false;
12389 this.destroyTrans(trans, false);
12390 this.fireEvent("loadexception", this, null, trans.arg);
12391 trans.callback.call(trans.scope||window, null, trans.arg, false);
12395 * Ext JS Library 1.1.1
12396 * Copyright(c) 2006-2007, Ext JS, LLC.
12398 * Originally Released Under LGPL - original licence link has changed is not relivant.
12401 * <script type="text/javascript">
12405 * @class Roo.data.JsonReader
12406 * @extends Roo.data.DataReader
12407 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12408 * based on mappings in a provided Roo.data.Record constructor.
12410 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12411 * in the reply previously.
12416 var RecordDef = Roo.data.Record.create([
12417 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12418 {name: 'occupation'} // This field will use "occupation" as the mapping.
12420 var myReader = new Roo.data.JsonReader({
12421 totalProperty: "results", // The property which contains the total dataset size (optional)
12422 root: "rows", // The property which contains an Array of row objects
12423 id: "id" // The property within each row object that provides an ID for the record (optional)
12427 * This would consume a JSON file like this:
12429 { 'results': 2, 'rows': [
12430 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12431 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12434 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12435 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12436 * paged from the remote server.
12437 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12438 * @cfg {String} root name of the property which contains the Array of row objects.
12439 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12440 * @cfg {Array} fields Array of field definition objects
12442 * Create a new JsonReader
12443 * @param {Object} meta Metadata configuration options
12444 * @param {Object} recordType Either an Array of field definition objects,
12445 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12447 Roo.data.JsonReader = function(meta, recordType){
12450 // set some defaults:
12451 Roo.applyIf(meta, {
12452 totalProperty: 'total',
12453 successProperty : 'success',
12458 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12460 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12463 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12464 * Used by Store query builder to append _requestMeta to params.
12467 metaFromRemote : false,
12469 * This method is only used by a DataProxy which has retrieved data from a remote server.
12470 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12471 * @return {Object} data A data block which is used by an Roo.data.Store object as
12472 * a cache of Roo.data.Records.
12474 read : function(response){
12475 var json = response.responseText;
12477 var o = /* eval:var:o */ eval("("+json+")");
12479 throw {message: "JsonReader.read: Json object not found"};
12485 this.metaFromRemote = true;
12486 this.meta = o.metaData;
12487 this.recordType = Roo.data.Record.create(o.metaData.fields);
12488 this.onMetaChange(this.meta, this.recordType, o);
12490 return this.readRecords(o);
12493 // private function a store will implement
12494 onMetaChange : function(meta, recordType, o){
12501 simpleAccess: function(obj, subsc) {
12508 getJsonAccessor: function(){
12510 return function(expr) {
12512 return(re.test(expr))
12513 ? new Function("obj", "return obj." + expr)
12518 return Roo.emptyFn;
12523 * Create a data block containing Roo.data.Records from an XML document.
12524 * @param {Object} o An object which contains an Array of row objects in the property specified
12525 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12526 * which contains the total size of the dataset.
12527 * @return {Object} data A data block which is used by an Roo.data.Store object as
12528 * a cache of Roo.data.Records.
12530 readRecords : function(o){
12532 * After any data loads, the raw JSON data is available for further custom processing.
12536 var s = this.meta, Record = this.recordType,
12537 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12539 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12541 if(s.totalProperty) {
12542 this.getTotal = this.getJsonAccessor(s.totalProperty);
12544 if(s.successProperty) {
12545 this.getSuccess = this.getJsonAccessor(s.successProperty);
12547 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12549 var g = this.getJsonAccessor(s.id);
12550 this.getId = function(rec) {
12552 return (r === undefined || r === "") ? null : r;
12555 this.getId = function(){return null;};
12558 for(var jj = 0; jj < fl; jj++){
12560 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12561 this.ef[jj] = this.getJsonAccessor(map);
12565 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12566 if(s.totalProperty){
12567 var vt = parseInt(this.getTotal(o), 10);
12572 if(s.successProperty){
12573 var vs = this.getSuccess(o);
12574 if(vs === false || vs === 'false'){
12579 for(var i = 0; i < c; i++){
12582 var id = this.getId(n);
12583 for(var j = 0; j < fl; j++){
12585 var v = this.ef[j](n);
12587 Roo.log('missing convert for ' + f.name);
12591 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12593 var record = new Record(values, id);
12595 records[i] = record;
12601 totalRecords : totalRecords
12606 * Ext JS Library 1.1.1
12607 * Copyright(c) 2006-2007, Ext JS, LLC.
12609 * Originally Released Under LGPL - original licence link has changed is not relivant.
12612 * <script type="text/javascript">
12616 * @class Roo.data.ArrayReader
12617 * @extends Roo.data.DataReader
12618 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12619 * Each element of that Array represents a row of data fields. The
12620 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12621 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12625 var RecordDef = Roo.data.Record.create([
12626 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12627 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12629 var myReader = new Roo.data.ArrayReader({
12630 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12634 * This would consume an Array like this:
12636 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12638 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12640 * Create a new JsonReader
12641 * @param {Object} meta Metadata configuration options.
12642 * @param {Object} recordType Either an Array of field definition objects
12643 * as specified to {@link Roo.data.Record#create},
12644 * or an {@link Roo.data.Record} object
12645 * created using {@link Roo.data.Record#create}.
12647 Roo.data.ArrayReader = function(meta, recordType){
12648 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12651 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12653 * Create a data block containing Roo.data.Records from an XML document.
12654 * @param {Object} o An Array of row objects which represents the dataset.
12655 * @return {Object} data A data block which is used by an Roo.data.Store object as
12656 * a cache of Roo.data.Records.
12658 readRecords : function(o){
12659 var sid = this.meta ? this.meta.id : null;
12660 var recordType = this.recordType, fields = recordType.prototype.fields;
12663 for(var i = 0; i < root.length; i++){
12666 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12667 for(var j = 0, jlen = fields.length; j < jlen; j++){
12668 var f = fields.items[j];
12669 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12670 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12672 values[f.name] = v;
12674 var record = new recordType(values, id);
12676 records[records.length] = record;
12680 totalRecords : records.length
12689 * @class Roo.bootstrap.ComboBox
12690 * @extends Roo.bootstrap.TriggerField
12691 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12692 * @cfg {Boolean} append (true|false) default false
12693 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12694 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12695 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12696 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12697 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12698 * @cfg {Boolean} animate default true
12699 * @cfg {Boolean} emptyResultText only for touch device
12700 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12701 * @cfg {String} emptyTitle default ''
12703 * Create a new ComboBox.
12704 * @param {Object} config Configuration options
12706 Roo.bootstrap.ComboBox = function(config){
12707 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12711 * Fires when the dropdown list is expanded
12712 * @param {Roo.bootstrap.ComboBox} combo This combo box
12717 * Fires when the dropdown list is collapsed
12718 * @param {Roo.bootstrap.ComboBox} combo This combo box
12722 * @event beforeselect
12723 * Fires before a list item is selected. Return false to cancel the selection.
12724 * @param {Roo.bootstrap.ComboBox} combo This combo box
12725 * @param {Roo.data.Record} record The data record returned from the underlying store
12726 * @param {Number} index The index of the selected item in the dropdown list
12728 'beforeselect' : true,
12731 * Fires when a list item is selected
12732 * @param {Roo.bootstrap.ComboBox} combo This combo box
12733 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12734 * @param {Number} index The index of the selected item in the dropdown list
12738 * @event beforequery
12739 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12740 * The event object passed has these properties:
12741 * @param {Roo.bootstrap.ComboBox} combo This combo box
12742 * @param {String} query The query
12743 * @param {Boolean} forceAll true to force "all" query
12744 * @param {Boolean} cancel true to cancel the query
12745 * @param {Object} e The query event object
12747 'beforequery': true,
12750 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12751 * @param {Roo.bootstrap.ComboBox} combo This combo box
12756 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12757 * @param {Roo.bootstrap.ComboBox} combo This combo box
12758 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12763 * Fires when the remove value from the combobox array
12764 * @param {Roo.bootstrap.ComboBox} combo This combo box
12768 * @event afterremove
12769 * Fires when the remove value from the combobox array
12770 * @param {Roo.bootstrap.ComboBox} combo This combo box
12772 'afterremove' : true,
12774 * @event specialfilter
12775 * Fires when specialfilter
12776 * @param {Roo.bootstrap.ComboBox} combo This combo box
12778 'specialfilter' : true,
12781 * Fires when tick the element
12782 * @param {Roo.bootstrap.ComboBox} combo This combo box
12786 * @event touchviewdisplay
12787 * Fires when touch view require special display (default is using displayField)
12788 * @param {Roo.bootstrap.ComboBox} combo This combo box
12789 * @param {Object} cfg set html .
12791 'touchviewdisplay' : true
12796 this.tickItems = [];
12798 this.selectedIndex = -1;
12799 if(this.mode == 'local'){
12800 if(config.queryDelay === undefined){
12801 this.queryDelay = 10;
12803 if(config.minChars === undefined){
12809 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12812 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12813 * rendering into an Roo.Editor, defaults to false)
12816 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12817 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12820 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12823 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12824 * the dropdown list (defaults to undefined, with no header element)
12828 * @cfg {String/Roo.Template} tpl The template to use to render the output
12832 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12834 listWidth: undefined,
12836 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12837 * mode = 'remote' or 'text' if mode = 'local')
12839 displayField: undefined,
12842 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12843 * mode = 'remote' or 'value' if mode = 'local').
12844 * Note: use of a valueField requires the user make a selection
12845 * in order for a value to be mapped.
12847 valueField: undefined,
12849 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12854 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12855 * field's data value (defaults to the underlying DOM element's name)
12857 hiddenName: undefined,
12859 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12863 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12865 selectedClass: 'active',
12868 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12872 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12873 * anchor positions (defaults to 'tl-bl')
12875 listAlign: 'tl-bl?',
12877 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12881 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12882 * query specified by the allQuery config option (defaults to 'query')
12884 triggerAction: 'query',
12886 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12887 * (defaults to 4, does not apply if editable = false)
12891 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12892 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12896 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12897 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12901 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12902 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12906 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12907 * when editable = true (defaults to false)
12909 selectOnFocus:false,
12911 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12913 queryParam: 'query',
12915 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12916 * when mode = 'remote' (defaults to 'Loading...')
12918 loadingText: 'Loading...',
12920 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12924 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12928 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12929 * traditional select (defaults to true)
12933 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12937 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12941 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12942 * listWidth has a higher value)
12946 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12947 * allow the user to set arbitrary text into the field (defaults to false)
12949 forceSelection:false,
12951 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12952 * if typeAhead = true (defaults to 250)
12954 typeAheadDelay : 250,
12956 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12957 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12959 valueNotFoundText : undefined,
12961 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12963 blockFocus : false,
12966 * @cfg {Boolean} disableClear Disable showing of clear button.
12968 disableClear : false,
12970 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12972 alwaysQuery : false,
12975 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12980 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12982 invalidClass : "has-warning",
12985 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12987 validClass : "has-success",
12990 * @cfg {Boolean} specialFilter (true|false) special filter default false
12992 specialFilter : false,
12995 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12997 mobileTouchView : true,
13000 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13002 useNativeIOS : false,
13004 ios_options : false,
13016 btnPosition : 'right',
13017 triggerList : true,
13018 showToggleBtn : true,
13020 emptyResultText: 'Empty',
13021 triggerText : 'Select',
13024 // element that contains real text value.. (when hidden is used..)
13026 getAutoCreate : function()
13031 * Render classic select for iso
13034 if(Roo.isIOS && this.useNativeIOS){
13035 cfg = this.getAutoCreateNativeIOS();
13043 if(Roo.isTouch && this.mobileTouchView){
13044 cfg = this.getAutoCreateTouchView();
13051 if(!this.tickable){
13052 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13057 * ComboBox with tickable selections
13060 var align = this.labelAlign || this.parentLabelAlign();
13063 cls : 'form-group roo-combobox-tickable' //input-group
13066 var btn_text_select = '';
13067 var btn_text_done = '';
13068 var btn_text_cancel = '';
13070 if (this.btn_text_show) {
13071 btn_text_select = 'Select';
13072 btn_text_done = 'Done';
13073 btn_text_cancel = 'Cancel';
13078 cls : 'tickable-buttons',
13083 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13084 //html : this.triggerText
13085 html: btn_text_select
13091 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13093 html: btn_text_done
13099 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13101 html: btn_text_cancel
13107 buttons.cn.unshift({
13109 cls: 'roo-select2-search-field-input'
13115 Roo.each(buttons.cn, function(c){
13117 c.cls += ' btn-' + _this.size;
13120 if (_this.disabled) {
13131 cls: 'form-hidden-field'
13135 cls: 'roo-select2-choices',
13139 cls: 'roo-select2-search-field',
13150 cls: 'roo-select2-container input-group roo-select2-container-multi',
13155 // cls: 'typeahead typeahead-long dropdown-menu',
13156 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13161 if(this.hasFeedback && !this.allowBlank){
13165 cls: 'glyphicon form-control-feedback'
13168 combobox.cn.push(feedback);
13172 if (align ==='left' && this.fieldLabel.length) {
13174 cfg.cls += ' roo-form-group-label-left';
13179 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13180 tooltip : 'This field is required'
13185 cls : 'control-label',
13186 html : this.fieldLabel
13198 var labelCfg = cfg.cn[1];
13199 var contentCfg = cfg.cn[2];
13202 if(this.indicatorpos == 'right'){
13208 cls : 'control-label',
13212 html : this.fieldLabel
13216 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13217 tooltip : 'This field is required'
13232 labelCfg = cfg.cn[0];
13233 contentCfg = cfg.cn[1];
13237 if(this.labelWidth > 12){
13238 labelCfg.style = "width: " + this.labelWidth + 'px';
13241 if(this.labelWidth < 13 && this.labelmd == 0){
13242 this.labelmd = this.labelWidth;
13245 if(this.labellg > 0){
13246 labelCfg.cls += ' col-lg-' + this.labellg;
13247 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13250 if(this.labelmd > 0){
13251 labelCfg.cls += ' col-md-' + this.labelmd;
13252 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13255 if(this.labelsm > 0){
13256 labelCfg.cls += ' col-sm-' + this.labelsm;
13257 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13260 if(this.labelxs > 0){
13261 labelCfg.cls += ' col-xs-' + this.labelxs;
13262 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13266 } else if ( this.fieldLabel.length) {
13267 // Roo.log(" label");
13271 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13272 tooltip : 'This field is required'
13276 //cls : 'input-group-addon',
13277 html : this.fieldLabel
13282 if(this.indicatorpos == 'right'){
13286 //cls : 'input-group-addon',
13287 html : this.fieldLabel
13291 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13292 tooltip : 'This field is required'
13301 // Roo.log(" no label && no align");
13308 ['xs','sm','md','lg'].map(function(size){
13309 if (settings[size]) {
13310 cfg.cls += ' col-' + size + '-' + settings[size];
13318 _initEventsCalled : false,
13321 initEvents: function()
13323 if (this._initEventsCalled) { // as we call render... prevent looping...
13326 this._initEventsCalled = true;
13329 throw "can not find store for combo";
13332 this.indicator = this.indicatorEl();
13334 this.store = Roo.factory(this.store, Roo.data);
13335 this.store.parent = this;
13337 // if we are building from html. then this element is so complex, that we can not really
13338 // use the rendered HTML.
13339 // so we have to trash and replace the previous code.
13340 if (Roo.XComponent.build_from_html) {
13341 // remove this element....
13342 var e = this.el.dom, k=0;
13343 while (e ) { e = e.previousSibling; ++k;}
13348 this.rendered = false;
13350 this.render(this.parent().getChildContainer(true), k);
13353 if(Roo.isIOS && this.useNativeIOS){
13354 this.initIOSView();
13362 if(Roo.isTouch && this.mobileTouchView){
13363 this.initTouchView();
13368 this.initTickableEvents();
13372 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13374 if(this.hiddenName){
13376 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13378 this.hiddenField.dom.value =
13379 this.hiddenValue !== undefined ? this.hiddenValue :
13380 this.value !== undefined ? this.value : '';
13382 // prevent input submission
13383 this.el.dom.removeAttribute('name');
13384 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13389 // this.el.dom.setAttribute('autocomplete', 'off');
13392 var cls = 'x-combo-list';
13394 //this.list = new Roo.Layer({
13395 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13401 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13402 _this.list.setWidth(lw);
13405 this.list.on('mouseover', this.onViewOver, this);
13406 this.list.on('mousemove', this.onViewMove, this);
13407 this.list.on('scroll', this.onViewScroll, this);
13410 this.list.swallowEvent('mousewheel');
13411 this.assetHeight = 0;
13414 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13415 this.assetHeight += this.header.getHeight();
13418 this.innerList = this.list.createChild({cls:cls+'-inner'});
13419 this.innerList.on('mouseover', this.onViewOver, this);
13420 this.innerList.on('mousemove', this.onViewMove, this);
13421 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13423 if(this.allowBlank && !this.pageSize && !this.disableClear){
13424 this.footer = this.list.createChild({cls:cls+'-ft'});
13425 this.pageTb = new Roo.Toolbar(this.footer);
13429 this.footer = this.list.createChild({cls:cls+'-ft'});
13430 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13431 {pageSize: this.pageSize});
13435 if (this.pageTb && this.allowBlank && !this.disableClear) {
13437 this.pageTb.add(new Roo.Toolbar.Fill(), {
13438 cls: 'x-btn-icon x-btn-clear',
13440 handler: function()
13443 _this.clearValue();
13444 _this.onSelect(false, -1);
13449 this.assetHeight += this.footer.getHeight();
13454 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13457 this.view = new Roo.View(this.list, this.tpl, {
13458 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13460 //this.view.wrapEl.setDisplayed(false);
13461 this.view.on('click', this.onViewClick, this);
13464 this.store.on('beforeload', this.onBeforeLoad, this);
13465 this.store.on('load', this.onLoad, this);
13466 this.store.on('loadexception', this.onLoadException, this);
13468 if(this.resizable){
13469 this.resizer = new Roo.Resizable(this.list, {
13470 pinned:true, handles:'se'
13472 this.resizer.on('resize', function(r, w, h){
13473 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13474 this.listWidth = w;
13475 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13476 this.restrictHeight();
13478 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13481 if(!this.editable){
13482 this.editable = true;
13483 this.setEditable(false);
13488 if (typeof(this.events.add.listeners) != 'undefined') {
13490 this.addicon = this.wrap.createChild(
13491 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13493 this.addicon.on('click', function(e) {
13494 this.fireEvent('add', this);
13497 if (typeof(this.events.edit.listeners) != 'undefined') {
13499 this.editicon = this.wrap.createChild(
13500 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13501 if (this.addicon) {
13502 this.editicon.setStyle('margin-left', '40px');
13504 this.editicon.on('click', function(e) {
13506 // we fire even if inothing is selected..
13507 this.fireEvent('edit', this, this.lastData );
13513 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13514 "up" : function(e){
13515 this.inKeyMode = true;
13519 "down" : function(e){
13520 if(!this.isExpanded()){
13521 this.onTriggerClick();
13523 this.inKeyMode = true;
13528 "enter" : function(e){
13529 // this.onViewClick();
13533 if(this.fireEvent("specialkey", this, e)){
13534 this.onViewClick(false);
13540 "esc" : function(e){
13544 "tab" : function(e){
13547 if(this.fireEvent("specialkey", this, e)){
13548 this.onViewClick(false);
13556 doRelay : function(foo, bar, hname){
13557 if(hname == 'down' || this.scope.isExpanded()){
13558 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13567 this.queryDelay = Math.max(this.queryDelay || 10,
13568 this.mode == 'local' ? 10 : 250);
13571 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13573 if(this.typeAhead){
13574 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13576 if(this.editable !== false){
13577 this.inputEl().on("keyup", this.onKeyUp, this);
13579 if(this.forceSelection){
13580 this.inputEl().on('blur', this.doForce, this);
13584 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13585 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13589 initTickableEvents: function()
13593 if(this.hiddenName){
13595 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13597 this.hiddenField.dom.value =
13598 this.hiddenValue !== undefined ? this.hiddenValue :
13599 this.value !== undefined ? this.value : '';
13601 // prevent input submission
13602 this.el.dom.removeAttribute('name');
13603 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13608 // this.list = this.el.select('ul.dropdown-menu',true).first();
13610 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13611 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13612 if(this.triggerList){
13613 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13616 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13617 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13619 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13620 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13622 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13623 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13625 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13626 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13627 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13630 this.cancelBtn.hide();
13635 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13636 _this.list.setWidth(lw);
13639 this.list.on('mouseover', this.onViewOver, this);
13640 this.list.on('mousemove', this.onViewMove, this);
13642 this.list.on('scroll', this.onViewScroll, this);
13645 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13646 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13649 this.view = new Roo.View(this.list, this.tpl, {
13654 selectedClass: this.selectedClass
13657 //this.view.wrapEl.setDisplayed(false);
13658 this.view.on('click', this.onViewClick, this);
13662 this.store.on('beforeload', this.onBeforeLoad, this);
13663 this.store.on('load', this.onLoad, this);
13664 this.store.on('loadexception', this.onLoadException, this);
13667 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13668 "up" : function(e){
13669 this.inKeyMode = true;
13673 "down" : function(e){
13674 this.inKeyMode = true;
13678 "enter" : function(e){
13679 if(this.fireEvent("specialkey", this, e)){
13680 this.onViewClick(false);
13686 "esc" : function(e){
13687 this.onTickableFooterButtonClick(e, false, false);
13690 "tab" : function(e){
13691 this.fireEvent("specialkey", this, e);
13693 this.onTickableFooterButtonClick(e, false, false);
13700 doRelay : function(e, fn, key){
13701 if(this.scope.isExpanded()){
13702 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13711 this.queryDelay = Math.max(this.queryDelay || 10,
13712 this.mode == 'local' ? 10 : 250);
13715 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13717 if(this.typeAhead){
13718 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13721 if(this.editable !== false){
13722 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13725 this.indicator = this.indicatorEl();
13727 if(this.indicator){
13728 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13729 this.indicator.hide();
13734 onDestroy : function(){
13736 this.view.setStore(null);
13737 this.view.el.removeAllListeners();
13738 this.view.el.remove();
13739 this.view.purgeListeners();
13742 this.list.dom.innerHTML = '';
13746 this.store.un('beforeload', this.onBeforeLoad, this);
13747 this.store.un('load', this.onLoad, this);
13748 this.store.un('loadexception', this.onLoadException, this);
13750 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13754 fireKey : function(e){
13755 if(e.isNavKeyPress() && !this.list.isVisible()){
13756 this.fireEvent("specialkey", this, e);
13761 onResize: function(w, h){
13762 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13764 // if(typeof w != 'number'){
13765 // // we do not handle it!?!?
13768 // var tw = this.trigger.getWidth();
13769 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13770 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13772 // this.inputEl().setWidth( this.adjustWidth('input', x));
13774 // //this.trigger.setStyle('left', x+'px');
13776 // if(this.list && this.listWidth === undefined){
13777 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13778 // this.list.setWidth(lw);
13779 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13787 * Allow or prevent the user from directly editing the field text. If false is passed,
13788 * the user will only be able to select from the items defined in the dropdown list. This method
13789 * is the runtime equivalent of setting the 'editable' config option at config time.
13790 * @param {Boolean} value True to allow the user to directly edit the field text
13792 setEditable : function(value){
13793 if(value == this.editable){
13796 this.editable = value;
13798 this.inputEl().dom.setAttribute('readOnly', true);
13799 this.inputEl().on('mousedown', this.onTriggerClick, this);
13800 this.inputEl().addClass('x-combo-noedit');
13802 this.inputEl().dom.setAttribute('readOnly', false);
13803 this.inputEl().un('mousedown', this.onTriggerClick, this);
13804 this.inputEl().removeClass('x-combo-noedit');
13810 onBeforeLoad : function(combo,opts){
13811 if(!this.hasFocus){
13815 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13817 this.restrictHeight();
13818 this.selectedIndex = -1;
13822 onLoad : function(){
13824 this.hasQuery = false;
13826 if(!this.hasFocus){
13830 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13831 this.loading.hide();
13834 if(this.store.getCount() > 0){
13837 this.restrictHeight();
13838 if(this.lastQuery == this.allQuery){
13839 if(this.editable && !this.tickable){
13840 this.inputEl().dom.select();
13844 !this.selectByValue(this.value, true) &&
13847 !this.store.lastOptions ||
13848 typeof(this.store.lastOptions.add) == 'undefined' ||
13849 this.store.lastOptions.add != true
13852 this.select(0, true);
13855 if(this.autoFocus){
13858 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13859 this.taTask.delay(this.typeAheadDelay);
13863 this.onEmptyResults();
13869 onLoadException : function()
13871 this.hasQuery = false;
13873 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13874 this.loading.hide();
13877 if(this.tickable && this.editable){
13882 // only causes errors at present
13883 //Roo.log(this.store.reader.jsonData);
13884 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13886 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13892 onTypeAhead : function(){
13893 if(this.store.getCount() > 0){
13894 var r = this.store.getAt(0);
13895 var newValue = r.data[this.displayField];
13896 var len = newValue.length;
13897 var selStart = this.getRawValue().length;
13899 if(selStart != len){
13900 this.setRawValue(newValue);
13901 this.selectText(selStart, newValue.length);
13907 onSelect : function(record, index){
13909 if(this.fireEvent('beforeselect', this, record, index) !== false){
13911 this.setFromData(index > -1 ? record.data : false);
13914 this.fireEvent('select', this, record, index);
13919 * Returns the currently selected field value or empty string if no value is set.
13920 * @return {String} value The selected value
13922 getValue : function()
13924 if(Roo.isIOS && this.useNativeIOS){
13925 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13929 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13932 if(this.valueField){
13933 return typeof this.value != 'undefined' ? this.value : '';
13935 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13939 getRawValue : function()
13941 if(Roo.isIOS && this.useNativeIOS){
13942 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13945 var v = this.inputEl().getValue();
13951 * Clears any text/value currently set in the field
13953 clearValue : function(){
13955 if(this.hiddenField){
13956 this.hiddenField.dom.value = '';
13959 this.setRawValue('');
13960 this.lastSelectionText = '';
13961 this.lastData = false;
13963 var close = this.closeTriggerEl();
13974 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13975 * will be displayed in the field. If the value does not match the data value of an existing item,
13976 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13977 * Otherwise the field will be blank (although the value will still be set).
13978 * @param {String} value The value to match
13980 setValue : function(v)
13982 if(Roo.isIOS && this.useNativeIOS){
13983 this.setIOSValue(v);
13993 if(this.valueField){
13994 var r = this.findRecord(this.valueField, v);
13996 text = r.data[this.displayField];
13997 }else if(this.valueNotFoundText !== undefined){
13998 text = this.valueNotFoundText;
14001 this.lastSelectionText = text;
14002 if(this.hiddenField){
14003 this.hiddenField.dom.value = v;
14005 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14008 var close = this.closeTriggerEl();
14011 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14017 * @property {Object} the last set data for the element
14022 * Sets the value of the field based on a object which is related to the record format for the store.
14023 * @param {Object} value the value to set as. or false on reset?
14025 setFromData : function(o){
14032 var dv = ''; // display value
14033 var vv = ''; // value value..
14035 if (this.displayField) {
14036 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14038 // this is an error condition!!!
14039 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14042 if(this.valueField){
14043 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14046 var close = this.closeTriggerEl();
14049 if(dv.length || vv * 1 > 0){
14051 this.blockFocus=true;
14057 if(this.hiddenField){
14058 this.hiddenField.dom.value = vv;
14060 this.lastSelectionText = dv;
14061 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14065 // no hidden field.. - we store the value in 'value', but still display
14066 // display field!!!!
14067 this.lastSelectionText = dv;
14068 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14075 reset : function(){
14076 // overridden so that last data is reset..
14083 this.setValue(this.originalValue);
14084 //this.clearInvalid();
14085 this.lastData = false;
14087 this.view.clearSelections();
14093 findRecord : function(prop, value){
14095 if(this.store.getCount() > 0){
14096 this.store.each(function(r){
14097 if(r.data[prop] == value){
14107 getName: function()
14109 // returns hidden if it's set..
14110 if (!this.rendered) {return ''};
14111 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14115 onViewMove : function(e, t){
14116 this.inKeyMode = false;
14120 onViewOver : function(e, t){
14121 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14124 var item = this.view.findItemFromChild(t);
14127 var index = this.view.indexOf(item);
14128 this.select(index, false);
14133 onViewClick : function(view, doFocus, el, e)
14135 var index = this.view.getSelectedIndexes()[0];
14137 var r = this.store.getAt(index);
14141 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14148 Roo.each(this.tickItems, function(v,k){
14150 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14152 _this.tickItems.splice(k, 1);
14154 if(typeof(e) == 'undefined' && view == false){
14155 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14167 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14168 this.tickItems.push(r.data);
14171 if(typeof(e) == 'undefined' && view == false){
14172 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14179 this.onSelect(r, index);
14181 if(doFocus !== false && !this.blockFocus){
14182 this.inputEl().focus();
14187 restrictHeight : function(){
14188 //this.innerList.dom.style.height = '';
14189 //var inner = this.innerList.dom;
14190 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14191 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14192 //this.list.beginUpdate();
14193 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14194 this.list.alignTo(this.inputEl(), this.listAlign);
14195 this.list.alignTo(this.inputEl(), this.listAlign);
14196 //this.list.endUpdate();
14200 onEmptyResults : function(){
14202 if(this.tickable && this.editable){
14203 this.hasFocus = false;
14204 this.restrictHeight();
14212 * Returns true if the dropdown list is expanded, else false.
14214 isExpanded : function(){
14215 return this.list.isVisible();
14219 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14220 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14221 * @param {String} value The data value of the item to select
14222 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14223 * selected item if it is not currently in view (defaults to true)
14224 * @return {Boolean} True if the value matched an item in the list, else false
14226 selectByValue : function(v, scrollIntoView){
14227 if(v !== undefined && v !== null){
14228 var r = this.findRecord(this.valueField || this.displayField, v);
14230 this.select(this.store.indexOf(r), scrollIntoView);
14238 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14239 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14240 * @param {Number} index The zero-based index of the list item to select
14241 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14242 * selected item if it is not currently in view (defaults to true)
14244 select : function(index, scrollIntoView){
14245 this.selectedIndex = index;
14246 this.view.select(index);
14247 if(scrollIntoView !== false){
14248 var el = this.view.getNode(index);
14250 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14253 this.list.scrollChildIntoView(el, false);
14259 selectNext : function(){
14260 var ct = this.store.getCount();
14262 if(this.selectedIndex == -1){
14264 }else if(this.selectedIndex < ct-1){
14265 this.select(this.selectedIndex+1);
14271 selectPrev : function(){
14272 var ct = this.store.getCount();
14274 if(this.selectedIndex == -1){
14276 }else if(this.selectedIndex != 0){
14277 this.select(this.selectedIndex-1);
14283 onKeyUp : function(e){
14284 if(this.editable !== false && !e.isSpecialKey()){
14285 this.lastKey = e.getKey();
14286 this.dqTask.delay(this.queryDelay);
14291 validateBlur : function(){
14292 return !this.list || !this.list.isVisible();
14296 initQuery : function(){
14298 var v = this.getRawValue();
14300 if(this.tickable && this.editable){
14301 v = this.tickableInputEl().getValue();
14308 doForce : function(){
14309 if(this.inputEl().dom.value.length > 0){
14310 this.inputEl().dom.value =
14311 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14317 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14318 * query allowing the query action to be canceled if needed.
14319 * @param {String} query The SQL query to execute
14320 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14321 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14322 * saved in the current store (defaults to false)
14324 doQuery : function(q, forceAll){
14326 if(q === undefined || q === null){
14331 forceAll: forceAll,
14335 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14340 forceAll = qe.forceAll;
14341 if(forceAll === true || (q.length >= this.minChars)){
14343 this.hasQuery = true;
14345 if(this.lastQuery != q || this.alwaysQuery){
14346 this.lastQuery = q;
14347 if(this.mode == 'local'){
14348 this.selectedIndex = -1;
14350 this.store.clearFilter();
14353 if(this.specialFilter){
14354 this.fireEvent('specialfilter', this);
14359 this.store.filter(this.displayField, q);
14362 this.store.fireEvent("datachanged", this.store);
14369 this.store.baseParams[this.queryParam] = q;
14371 var options = {params : this.getParams(q)};
14374 options.add = true;
14375 options.params.start = this.page * this.pageSize;
14378 this.store.load(options);
14381 * this code will make the page width larger, at the beginning, the list not align correctly,
14382 * we should expand the list on onLoad
14383 * so command out it
14388 this.selectedIndex = -1;
14393 this.loadNext = false;
14397 getParams : function(q){
14399 //p[this.queryParam] = q;
14403 p.limit = this.pageSize;
14409 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14411 collapse : function(){
14412 if(!this.isExpanded()){
14418 this.hasFocus = false;
14422 this.cancelBtn.hide();
14423 this.trigger.show();
14426 this.tickableInputEl().dom.value = '';
14427 this.tickableInputEl().blur();
14432 Roo.get(document).un('mousedown', this.collapseIf, this);
14433 Roo.get(document).un('mousewheel', this.collapseIf, this);
14434 if (!this.editable) {
14435 Roo.get(document).un('keydown', this.listKeyPress, this);
14437 this.fireEvent('collapse', this);
14443 collapseIf : function(e){
14444 var in_combo = e.within(this.el);
14445 var in_list = e.within(this.list);
14446 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14448 if (in_combo || in_list || is_list) {
14449 //e.stopPropagation();
14454 this.onTickableFooterButtonClick(e, false, false);
14462 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14464 expand : function(){
14466 if(this.isExpanded() || !this.hasFocus){
14470 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14471 this.list.setWidth(lw);
14477 this.restrictHeight();
14481 this.tickItems = Roo.apply([], this.item);
14484 this.cancelBtn.show();
14485 this.trigger.hide();
14488 this.tickableInputEl().focus();
14493 Roo.get(document).on('mousedown', this.collapseIf, this);
14494 Roo.get(document).on('mousewheel', this.collapseIf, this);
14495 if (!this.editable) {
14496 Roo.get(document).on('keydown', this.listKeyPress, this);
14499 this.fireEvent('expand', this);
14503 // Implements the default empty TriggerField.onTriggerClick function
14504 onTriggerClick : function(e)
14506 Roo.log('trigger click');
14508 if(this.disabled || !this.triggerList){
14513 this.loadNext = false;
14515 if(this.isExpanded()){
14517 if (!this.blockFocus) {
14518 this.inputEl().focus();
14522 this.hasFocus = true;
14523 if(this.triggerAction == 'all') {
14524 this.doQuery(this.allQuery, true);
14526 this.doQuery(this.getRawValue());
14528 if (!this.blockFocus) {
14529 this.inputEl().focus();
14534 onTickableTriggerClick : function(e)
14541 this.loadNext = false;
14542 this.hasFocus = true;
14544 if(this.triggerAction == 'all') {
14545 this.doQuery(this.allQuery, true);
14547 this.doQuery(this.getRawValue());
14551 onSearchFieldClick : function(e)
14553 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14554 this.onTickableFooterButtonClick(e, false, false);
14558 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14563 this.loadNext = false;
14564 this.hasFocus = true;
14566 if(this.triggerAction == 'all') {
14567 this.doQuery(this.allQuery, true);
14569 this.doQuery(this.getRawValue());
14573 listKeyPress : function(e)
14575 //Roo.log('listkeypress');
14576 // scroll to first matching element based on key pres..
14577 if (e.isSpecialKey()) {
14580 var k = String.fromCharCode(e.getKey()).toUpperCase();
14583 var csel = this.view.getSelectedNodes();
14584 var cselitem = false;
14586 var ix = this.view.indexOf(csel[0]);
14587 cselitem = this.store.getAt(ix);
14588 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14594 this.store.each(function(v) {
14596 // start at existing selection.
14597 if (cselitem.id == v.id) {
14603 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14604 match = this.store.indexOf(v);
14610 if (match === false) {
14611 return true; // no more action?
14614 this.view.select(match);
14615 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14616 sn.scrollIntoView(sn.dom.parentNode, false);
14619 onViewScroll : function(e, t){
14621 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){
14625 this.hasQuery = true;
14627 this.loading = this.list.select('.loading', true).first();
14629 if(this.loading === null){
14630 this.list.createChild({
14632 cls: 'loading roo-select2-more-results roo-select2-active',
14633 html: 'Loading more results...'
14636 this.loading = this.list.select('.loading', true).first();
14638 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14640 this.loading.hide();
14643 this.loading.show();
14648 this.loadNext = true;
14650 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14655 addItem : function(o)
14657 var dv = ''; // display value
14659 if (this.displayField) {
14660 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14662 // this is an error condition!!!
14663 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14670 var choice = this.choices.createChild({
14672 cls: 'roo-select2-search-choice',
14681 cls: 'roo-select2-search-choice-close fa fa-times',
14686 }, this.searchField);
14688 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14690 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14698 this.inputEl().dom.value = '';
14703 onRemoveItem : function(e, _self, o)
14705 e.preventDefault();
14707 this.lastItem = Roo.apply([], this.item);
14709 var index = this.item.indexOf(o.data) * 1;
14712 Roo.log('not this item?!');
14716 this.item.splice(index, 1);
14721 this.fireEvent('remove', this, e);
14727 syncValue : function()
14729 if(!this.item.length){
14736 Roo.each(this.item, function(i){
14737 if(_this.valueField){
14738 value.push(i[_this.valueField]);
14745 this.value = value.join(',');
14747 if(this.hiddenField){
14748 this.hiddenField.dom.value = this.value;
14751 this.store.fireEvent("datachanged", this.store);
14756 clearItem : function()
14758 if(!this.multiple){
14764 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14772 if(this.tickable && !Roo.isTouch){
14773 this.view.refresh();
14777 inputEl: function ()
14779 if(Roo.isIOS && this.useNativeIOS){
14780 return this.el.select('select.roo-ios-select', true).first();
14783 if(Roo.isTouch && this.mobileTouchView){
14784 return this.el.select('input.form-control',true).first();
14788 return this.searchField;
14791 return this.el.select('input.form-control',true).first();
14794 onTickableFooterButtonClick : function(e, btn, el)
14796 e.preventDefault();
14798 this.lastItem = Roo.apply([], this.item);
14800 if(btn && btn.name == 'cancel'){
14801 this.tickItems = Roo.apply([], this.item);
14810 Roo.each(this.tickItems, function(o){
14818 validate : function()
14820 if(this.getVisibilityEl().hasClass('hidden')){
14824 var v = this.getRawValue();
14827 v = this.getValue();
14830 if(this.disabled || this.allowBlank || v.length){
14835 this.markInvalid();
14839 tickableInputEl : function()
14841 if(!this.tickable || !this.editable){
14842 return this.inputEl();
14845 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14849 getAutoCreateTouchView : function()
14854 cls: 'form-group' //input-group
14860 type : this.inputType,
14861 cls : 'form-control x-combo-noedit',
14862 autocomplete: 'new-password',
14863 placeholder : this.placeholder || '',
14868 input.name = this.name;
14872 input.cls += ' input-' + this.size;
14875 if (this.disabled) {
14876 input.disabled = true;
14887 inputblock.cls += ' input-group';
14889 inputblock.cn.unshift({
14891 cls : 'input-group-addon',
14896 if(this.removable && !this.multiple){
14897 inputblock.cls += ' roo-removable';
14899 inputblock.cn.push({
14902 cls : 'roo-combo-removable-btn close'
14906 if(this.hasFeedback && !this.allowBlank){
14908 inputblock.cls += ' has-feedback';
14910 inputblock.cn.push({
14912 cls: 'glyphicon form-control-feedback'
14919 inputblock.cls += (this.before) ? '' : ' input-group';
14921 inputblock.cn.push({
14923 cls : 'input-group-addon',
14934 cls: 'form-hidden-field'
14948 cls: 'form-hidden-field'
14952 cls: 'roo-select2-choices',
14956 cls: 'roo-select2-search-field',
14969 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14975 if(!this.multiple && this.showToggleBtn){
14982 if (this.caret != false) {
14985 cls: 'fa fa-' + this.caret
14992 cls : 'input-group-addon btn dropdown-toggle',
14997 cls: 'combobox-clear',
15011 combobox.cls += ' roo-select2-container-multi';
15014 var align = this.labelAlign || this.parentLabelAlign();
15016 if (align ==='left' && this.fieldLabel.length) {
15021 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15022 tooltip : 'This field is required'
15026 cls : 'control-label',
15027 html : this.fieldLabel
15038 var labelCfg = cfg.cn[1];
15039 var contentCfg = cfg.cn[2];
15042 if(this.indicatorpos == 'right'){
15047 cls : 'control-label',
15051 html : this.fieldLabel
15055 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15056 tooltip : 'This field is required'
15069 labelCfg = cfg.cn[0];
15070 contentCfg = cfg.cn[1];
15075 if(this.labelWidth > 12){
15076 labelCfg.style = "width: " + this.labelWidth + 'px';
15079 if(this.labelWidth < 13 && this.labelmd == 0){
15080 this.labelmd = this.labelWidth;
15083 if(this.labellg > 0){
15084 labelCfg.cls += ' col-lg-' + this.labellg;
15085 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15088 if(this.labelmd > 0){
15089 labelCfg.cls += ' col-md-' + this.labelmd;
15090 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15093 if(this.labelsm > 0){
15094 labelCfg.cls += ' col-sm-' + this.labelsm;
15095 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15098 if(this.labelxs > 0){
15099 labelCfg.cls += ' col-xs-' + this.labelxs;
15100 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15104 } else if ( this.fieldLabel.length) {
15108 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15109 tooltip : 'This field is required'
15113 cls : 'control-label',
15114 html : this.fieldLabel
15125 if(this.indicatorpos == 'right'){
15129 cls : 'control-label',
15130 html : this.fieldLabel,
15134 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15135 tooltip : 'This field is required'
15152 var settings = this;
15154 ['xs','sm','md','lg'].map(function(size){
15155 if (settings[size]) {
15156 cfg.cls += ' col-' + size + '-' + settings[size];
15163 initTouchView : function()
15165 this.renderTouchView();
15167 this.touchViewEl.on('scroll', function(){
15168 this.el.dom.scrollTop = 0;
15171 this.originalValue = this.getValue();
15173 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15175 this.inputEl().on("click", this.showTouchView, this);
15176 if (this.triggerEl) {
15177 this.triggerEl.on("click", this.showTouchView, this);
15181 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15182 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15184 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15186 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15187 this.store.on('load', this.onTouchViewLoad, this);
15188 this.store.on('loadexception', this.onTouchViewLoadException, this);
15190 if(this.hiddenName){
15192 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15194 this.hiddenField.dom.value =
15195 this.hiddenValue !== undefined ? this.hiddenValue :
15196 this.value !== undefined ? this.value : '';
15198 this.el.dom.removeAttribute('name');
15199 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15203 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15204 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15207 if(this.removable && !this.multiple){
15208 var close = this.closeTriggerEl();
15210 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15211 close.on('click', this.removeBtnClick, this, close);
15215 * fix the bug in Safari iOS8
15217 this.inputEl().on("focus", function(e){
15218 document.activeElement.blur();
15226 renderTouchView : function()
15228 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15229 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15231 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15232 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15234 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15235 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15236 this.touchViewBodyEl.setStyle('overflow', 'auto');
15238 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15239 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15241 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15242 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15246 showTouchView : function()
15252 this.touchViewHeaderEl.hide();
15254 if(this.modalTitle.length){
15255 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15256 this.touchViewHeaderEl.show();
15259 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15260 this.touchViewEl.show();
15262 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15264 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15265 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15267 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15269 if(this.modalTitle.length){
15270 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15273 this.touchViewBodyEl.setHeight(bodyHeight);
15277 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15279 this.touchViewEl.addClass('in');
15282 this.doTouchViewQuery();
15286 hideTouchView : function()
15288 this.touchViewEl.removeClass('in');
15292 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15294 this.touchViewEl.setStyle('display', 'none');
15299 setTouchViewValue : function()
15306 Roo.each(this.tickItems, function(o){
15311 this.hideTouchView();
15314 doTouchViewQuery : function()
15323 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15327 if(!this.alwaysQuery || this.mode == 'local'){
15328 this.onTouchViewLoad();
15335 onTouchViewBeforeLoad : function(combo,opts)
15341 onTouchViewLoad : function()
15343 if(this.store.getCount() < 1){
15344 this.onTouchViewEmptyResults();
15348 this.clearTouchView();
15350 var rawValue = this.getRawValue();
15352 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15354 this.tickItems = [];
15356 this.store.data.each(function(d, rowIndex){
15357 var row = this.touchViewListGroup.createChild(template);
15359 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15360 row.addClass(d.data.cls);
15363 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15366 html : d.data[this.displayField]
15369 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15370 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15373 row.removeClass('selected');
15374 if(!this.multiple && this.valueField &&
15375 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15378 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15379 row.addClass('selected');
15382 if(this.multiple && this.valueField &&
15383 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15387 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15388 this.tickItems.push(d.data);
15391 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15395 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15397 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15399 if(this.modalTitle.length){
15400 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15403 var listHeight = this.touchViewListGroup.getHeight();
15407 if(firstChecked && listHeight > bodyHeight){
15408 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15413 onTouchViewLoadException : function()
15415 this.hideTouchView();
15418 onTouchViewEmptyResults : function()
15420 this.clearTouchView();
15422 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15424 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15428 clearTouchView : function()
15430 this.touchViewListGroup.dom.innerHTML = '';
15433 onTouchViewClick : function(e, el, o)
15435 e.preventDefault();
15438 var rowIndex = o.rowIndex;
15440 var r = this.store.getAt(rowIndex);
15442 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15444 if(!this.multiple){
15445 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15446 c.dom.removeAttribute('checked');
15449 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15451 this.setFromData(r.data);
15453 var close = this.closeTriggerEl();
15459 this.hideTouchView();
15461 this.fireEvent('select', this, r, rowIndex);
15466 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15467 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15468 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15472 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15473 this.addItem(r.data);
15474 this.tickItems.push(r.data);
15478 getAutoCreateNativeIOS : function()
15481 cls: 'form-group' //input-group,
15486 cls : 'roo-ios-select'
15490 combobox.name = this.name;
15493 if (this.disabled) {
15494 combobox.disabled = true;
15497 var settings = this;
15499 ['xs','sm','md','lg'].map(function(size){
15500 if (settings[size]) {
15501 cfg.cls += ' col-' + size + '-' + settings[size];
15511 initIOSView : function()
15513 this.store.on('load', this.onIOSViewLoad, this);
15518 onIOSViewLoad : function()
15520 if(this.store.getCount() < 1){
15524 this.clearIOSView();
15526 if(this.allowBlank) {
15528 var default_text = '-- SELECT --';
15530 if(this.placeholder.length){
15531 default_text = this.placeholder;
15534 if(this.emptyTitle.length){
15535 default_text += ' - ' + this.emptyTitle + ' -';
15538 var opt = this.inputEl().createChild({
15541 html : default_text
15545 o[this.valueField] = 0;
15546 o[this.displayField] = default_text;
15548 this.ios_options.push({
15555 this.store.data.each(function(d, rowIndex){
15559 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15560 html = d.data[this.displayField];
15565 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15566 value = d.data[this.valueField];
15575 if(this.value == d.data[this.valueField]){
15576 option['selected'] = true;
15579 var opt = this.inputEl().createChild(option);
15581 this.ios_options.push({
15588 this.inputEl().on('change', function(){
15589 this.fireEvent('select', this);
15594 clearIOSView: function()
15596 this.inputEl().dom.innerHTML = '';
15598 this.ios_options = [];
15601 setIOSValue: function(v)
15605 if(!this.ios_options){
15609 Roo.each(this.ios_options, function(opts){
15611 opts.el.dom.removeAttribute('selected');
15613 if(opts.data[this.valueField] != v){
15617 opts.el.dom.setAttribute('selected', true);
15623 * @cfg {Boolean} grow
15627 * @cfg {Number} growMin
15631 * @cfg {Number} growMax
15640 Roo.apply(Roo.bootstrap.ComboBox, {
15644 cls: 'modal-header',
15666 cls: 'list-group-item',
15670 cls: 'roo-combobox-list-group-item-value'
15674 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15688 listItemCheckbox : {
15690 cls: 'list-group-item',
15694 cls: 'roo-combobox-list-group-item-value'
15698 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15714 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15719 cls: 'modal-footer',
15727 cls: 'col-xs-6 text-left',
15730 cls: 'btn btn-danger roo-touch-view-cancel',
15736 cls: 'col-xs-6 text-right',
15739 cls: 'btn btn-success roo-touch-view-ok',
15750 Roo.apply(Roo.bootstrap.ComboBox, {
15752 touchViewTemplate : {
15754 cls: 'modal fade roo-combobox-touch-view',
15758 cls: 'modal-dialog',
15759 style : 'position:fixed', // we have to fix position....
15763 cls: 'modal-content',
15765 Roo.bootstrap.ComboBox.header,
15766 Roo.bootstrap.ComboBox.body,
15767 Roo.bootstrap.ComboBox.footer
15776 * Ext JS Library 1.1.1
15777 * Copyright(c) 2006-2007, Ext JS, LLC.
15779 * Originally Released Under LGPL - original licence link has changed is not relivant.
15782 * <script type="text/javascript">
15787 * @extends Roo.util.Observable
15788 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15789 * This class also supports single and multi selection modes. <br>
15790 * Create a data model bound view:
15792 var store = new Roo.data.Store(...);
15794 var view = new Roo.View({
15796 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15798 singleSelect: true,
15799 selectedClass: "ydataview-selected",
15803 // listen for node click?
15804 view.on("click", function(vw, index, node, e){
15805 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15809 dataModel.load("foobar.xml");
15811 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15813 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15814 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15816 * Note: old style constructor is still suported (container, template, config)
15819 * Create a new View
15820 * @param {Object} config The config object
15823 Roo.View = function(config, depreciated_tpl, depreciated_config){
15825 this.parent = false;
15827 if (typeof(depreciated_tpl) == 'undefined') {
15828 // new way.. - universal constructor.
15829 Roo.apply(this, config);
15830 this.el = Roo.get(this.el);
15833 this.el = Roo.get(config);
15834 this.tpl = depreciated_tpl;
15835 Roo.apply(this, depreciated_config);
15837 this.wrapEl = this.el.wrap().wrap();
15838 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15841 if(typeof(this.tpl) == "string"){
15842 this.tpl = new Roo.Template(this.tpl);
15844 // support xtype ctors..
15845 this.tpl = new Roo.factory(this.tpl, Roo);
15849 this.tpl.compile();
15854 * @event beforeclick
15855 * Fires before a click is processed. Returns false to cancel the default action.
15856 * @param {Roo.View} this
15857 * @param {Number} index The index of the target node
15858 * @param {HTMLElement} node The target node
15859 * @param {Roo.EventObject} e The raw event object
15861 "beforeclick" : true,
15864 * Fires when a template node is clicked.
15865 * @param {Roo.View} this
15866 * @param {Number} index The index of the target node
15867 * @param {HTMLElement} node The target node
15868 * @param {Roo.EventObject} e The raw event object
15873 * Fires when a template node is double clicked.
15874 * @param {Roo.View} this
15875 * @param {Number} index The index of the target node
15876 * @param {HTMLElement} node The target node
15877 * @param {Roo.EventObject} e The raw event object
15881 * @event contextmenu
15882 * Fires when a template node is right clicked.
15883 * @param {Roo.View} this
15884 * @param {Number} index The index of the target node
15885 * @param {HTMLElement} node The target node
15886 * @param {Roo.EventObject} e The raw event object
15888 "contextmenu" : true,
15890 * @event selectionchange
15891 * Fires when the selected nodes change.
15892 * @param {Roo.View} this
15893 * @param {Array} selections Array of the selected nodes
15895 "selectionchange" : true,
15898 * @event beforeselect
15899 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15900 * @param {Roo.View} this
15901 * @param {HTMLElement} node The node to be selected
15902 * @param {Array} selections Array of currently selected nodes
15904 "beforeselect" : true,
15906 * @event preparedata
15907 * Fires on every row to render, to allow you to change the data.
15908 * @param {Roo.View} this
15909 * @param {Object} data to be rendered (change this)
15911 "preparedata" : true
15919 "click": this.onClick,
15920 "dblclick": this.onDblClick,
15921 "contextmenu": this.onContextMenu,
15925 this.selections = [];
15927 this.cmp = new Roo.CompositeElementLite([]);
15929 this.store = Roo.factory(this.store, Roo.data);
15930 this.setStore(this.store, true);
15933 if ( this.footer && this.footer.xtype) {
15935 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15937 this.footer.dataSource = this.store;
15938 this.footer.container = fctr;
15939 this.footer = Roo.factory(this.footer, Roo);
15940 fctr.insertFirst(this.el);
15942 // this is a bit insane - as the paging toolbar seems to detach the el..
15943 // dom.parentNode.parentNode.parentNode
15944 // they get detached?
15948 Roo.View.superclass.constructor.call(this);
15953 Roo.extend(Roo.View, Roo.util.Observable, {
15956 * @cfg {Roo.data.Store} store Data store to load data from.
15961 * @cfg {String|Roo.Element} el The container element.
15966 * @cfg {String|Roo.Template} tpl The template used by this View
15970 * @cfg {String} dataName the named area of the template to use as the data area
15971 * Works with domtemplates roo-name="name"
15975 * @cfg {String} selectedClass The css class to add to selected nodes
15977 selectedClass : "x-view-selected",
15979 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15984 * @cfg {String} text to display on mask (default Loading)
15988 * @cfg {Boolean} multiSelect Allow multiple selection
15990 multiSelect : false,
15992 * @cfg {Boolean} singleSelect Allow single selection
15994 singleSelect: false,
15997 * @cfg {Boolean} toggleSelect - selecting
15999 toggleSelect : false,
16002 * @cfg {Boolean} tickable - selecting
16007 * Returns the element this view is bound to.
16008 * @return {Roo.Element}
16010 getEl : function(){
16011 return this.wrapEl;
16017 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16019 refresh : function(){
16020 //Roo.log('refresh');
16023 // if we are using something like 'domtemplate', then
16024 // the what gets used is:
16025 // t.applySubtemplate(NAME, data, wrapping data..)
16026 // the outer template then get' applied with
16027 // the store 'extra data'
16028 // and the body get's added to the
16029 // roo-name="data" node?
16030 // <span class='roo-tpl-{name}'></span> ?????
16034 this.clearSelections();
16035 this.el.update("");
16037 var records = this.store.getRange();
16038 if(records.length < 1) {
16040 // is this valid?? = should it render a template??
16042 this.el.update(this.emptyText);
16046 if (this.dataName) {
16047 this.el.update(t.apply(this.store.meta)); //????
16048 el = this.el.child('.roo-tpl-' + this.dataName);
16051 for(var i = 0, len = records.length; i < len; i++){
16052 var data = this.prepareData(records[i].data, i, records[i]);
16053 this.fireEvent("preparedata", this, data, i, records[i]);
16055 var d = Roo.apply({}, data);
16058 Roo.apply(d, {'roo-id' : Roo.id()});
16062 Roo.each(this.parent.item, function(item){
16063 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16066 Roo.apply(d, {'roo-data-checked' : 'checked'});
16070 html[html.length] = Roo.util.Format.trim(
16072 t.applySubtemplate(this.dataName, d, this.store.meta) :
16079 el.update(html.join(""));
16080 this.nodes = el.dom.childNodes;
16081 this.updateIndexes(0);
16086 * Function to override to reformat the data that is sent to
16087 * the template for each node.
16088 * DEPRICATED - use the preparedata event handler.
16089 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16090 * a JSON object for an UpdateManager bound view).
16092 prepareData : function(data, index, record)
16094 this.fireEvent("preparedata", this, data, index, record);
16098 onUpdate : function(ds, record){
16099 // Roo.log('on update');
16100 this.clearSelections();
16101 var index = this.store.indexOf(record);
16102 var n = this.nodes[index];
16103 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16104 n.parentNode.removeChild(n);
16105 this.updateIndexes(index, index);
16111 onAdd : function(ds, records, index)
16113 //Roo.log(['on Add', ds, records, index] );
16114 this.clearSelections();
16115 if(this.nodes.length == 0){
16119 var n = this.nodes[index];
16120 for(var i = 0, len = records.length; i < len; i++){
16121 var d = this.prepareData(records[i].data, i, records[i]);
16123 this.tpl.insertBefore(n, d);
16126 this.tpl.append(this.el, d);
16129 this.updateIndexes(index);
16132 onRemove : function(ds, record, index){
16133 // Roo.log('onRemove');
16134 this.clearSelections();
16135 var el = this.dataName ?
16136 this.el.child('.roo-tpl-' + this.dataName) :
16139 el.dom.removeChild(this.nodes[index]);
16140 this.updateIndexes(index);
16144 * Refresh an individual node.
16145 * @param {Number} index
16147 refreshNode : function(index){
16148 this.onUpdate(this.store, this.store.getAt(index));
16151 updateIndexes : function(startIndex, endIndex){
16152 var ns = this.nodes;
16153 startIndex = startIndex || 0;
16154 endIndex = endIndex || ns.length - 1;
16155 for(var i = startIndex; i <= endIndex; i++){
16156 ns[i].nodeIndex = i;
16161 * Changes the data store this view uses and refresh the view.
16162 * @param {Store} store
16164 setStore : function(store, initial){
16165 if(!initial && this.store){
16166 this.store.un("datachanged", this.refresh);
16167 this.store.un("add", this.onAdd);
16168 this.store.un("remove", this.onRemove);
16169 this.store.un("update", this.onUpdate);
16170 this.store.un("clear", this.refresh);
16171 this.store.un("beforeload", this.onBeforeLoad);
16172 this.store.un("load", this.onLoad);
16173 this.store.un("loadexception", this.onLoad);
16177 store.on("datachanged", this.refresh, this);
16178 store.on("add", this.onAdd, this);
16179 store.on("remove", this.onRemove, this);
16180 store.on("update", this.onUpdate, this);
16181 store.on("clear", this.refresh, this);
16182 store.on("beforeload", this.onBeforeLoad, this);
16183 store.on("load", this.onLoad, this);
16184 store.on("loadexception", this.onLoad, this);
16192 * onbeforeLoad - masks the loading area.
16195 onBeforeLoad : function(store,opts)
16197 //Roo.log('onBeforeLoad');
16199 this.el.update("");
16201 this.el.mask(this.mask ? this.mask : "Loading" );
16203 onLoad : function ()
16210 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16211 * @param {HTMLElement} node
16212 * @return {HTMLElement} The template node
16214 findItemFromChild : function(node){
16215 var el = this.dataName ?
16216 this.el.child('.roo-tpl-' + this.dataName,true) :
16219 if(!node || node.parentNode == el){
16222 var p = node.parentNode;
16223 while(p && p != el){
16224 if(p.parentNode == el){
16233 onClick : function(e){
16234 var item = this.findItemFromChild(e.getTarget());
16236 var index = this.indexOf(item);
16237 if(this.onItemClick(item, index, e) !== false){
16238 this.fireEvent("click", this, index, item, e);
16241 this.clearSelections();
16246 onContextMenu : function(e){
16247 var item = this.findItemFromChild(e.getTarget());
16249 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16254 onDblClick : function(e){
16255 var item = this.findItemFromChild(e.getTarget());
16257 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16261 onItemClick : function(item, index, e)
16263 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16266 if (this.toggleSelect) {
16267 var m = this.isSelected(item) ? 'unselect' : 'select';
16270 _t[m](item, true, false);
16273 if(this.multiSelect || this.singleSelect){
16274 if(this.multiSelect && e.shiftKey && this.lastSelection){
16275 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16277 this.select(item, this.multiSelect && e.ctrlKey);
16278 this.lastSelection = item;
16281 if(!this.tickable){
16282 e.preventDefault();
16290 * Get the number of selected nodes.
16293 getSelectionCount : function(){
16294 return this.selections.length;
16298 * Get the currently selected nodes.
16299 * @return {Array} An array of HTMLElements
16301 getSelectedNodes : function(){
16302 return this.selections;
16306 * Get the indexes of the selected nodes.
16309 getSelectedIndexes : function(){
16310 var indexes = [], s = this.selections;
16311 for(var i = 0, len = s.length; i < len; i++){
16312 indexes.push(s[i].nodeIndex);
16318 * Clear all selections
16319 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16321 clearSelections : function(suppressEvent){
16322 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16323 this.cmp.elements = this.selections;
16324 this.cmp.removeClass(this.selectedClass);
16325 this.selections = [];
16326 if(!suppressEvent){
16327 this.fireEvent("selectionchange", this, this.selections);
16333 * Returns true if the passed node is selected
16334 * @param {HTMLElement/Number} node The node or node index
16335 * @return {Boolean}
16337 isSelected : function(node){
16338 var s = this.selections;
16342 node = this.getNode(node);
16343 return s.indexOf(node) !== -1;
16348 * @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
16349 * @param {Boolean} keepExisting (optional) true to keep existing selections
16350 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16352 select : function(nodeInfo, keepExisting, suppressEvent){
16353 if(nodeInfo instanceof Array){
16355 this.clearSelections(true);
16357 for(var i = 0, len = nodeInfo.length; i < len; i++){
16358 this.select(nodeInfo[i], true, true);
16362 var node = this.getNode(nodeInfo);
16363 if(!node || this.isSelected(node)){
16364 return; // already selected.
16367 this.clearSelections(true);
16370 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16371 Roo.fly(node).addClass(this.selectedClass);
16372 this.selections.push(node);
16373 if(!suppressEvent){
16374 this.fireEvent("selectionchange", this, this.selections);
16382 * @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
16383 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16384 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16386 unselect : function(nodeInfo, keepExisting, suppressEvent)
16388 if(nodeInfo instanceof Array){
16389 Roo.each(this.selections, function(s) {
16390 this.unselect(s, nodeInfo);
16394 var node = this.getNode(nodeInfo);
16395 if(!node || !this.isSelected(node)){
16396 //Roo.log("not selected");
16397 return; // not selected.
16401 Roo.each(this.selections, function(s) {
16403 Roo.fly(node).removeClass(this.selectedClass);
16410 this.selections= ns;
16411 this.fireEvent("selectionchange", this, this.selections);
16415 * Gets a template node.
16416 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16417 * @return {HTMLElement} The node or null if it wasn't found
16419 getNode : function(nodeInfo){
16420 if(typeof nodeInfo == "string"){
16421 return document.getElementById(nodeInfo);
16422 }else if(typeof nodeInfo == "number"){
16423 return this.nodes[nodeInfo];
16429 * Gets a range template nodes.
16430 * @param {Number} startIndex
16431 * @param {Number} endIndex
16432 * @return {Array} An array of nodes
16434 getNodes : function(start, end){
16435 var ns = this.nodes;
16436 start = start || 0;
16437 end = typeof end == "undefined" ? ns.length - 1 : end;
16440 for(var i = start; i <= end; i++){
16444 for(var i = start; i >= end; i--){
16452 * Finds the index of the passed node
16453 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16454 * @return {Number} The index of the node or -1
16456 indexOf : function(node){
16457 node = this.getNode(node);
16458 if(typeof node.nodeIndex == "number"){
16459 return node.nodeIndex;
16461 var ns = this.nodes;
16462 for(var i = 0, len = ns.length; i < len; i++){
16473 * based on jquery fullcalendar
16477 Roo.bootstrap = Roo.bootstrap || {};
16479 * @class Roo.bootstrap.Calendar
16480 * @extends Roo.bootstrap.Component
16481 * Bootstrap Calendar class
16482 * @cfg {Boolean} loadMask (true|false) default false
16483 * @cfg {Object} header generate the user specific header of the calendar, default false
16486 * Create a new Container
16487 * @param {Object} config The config object
16492 Roo.bootstrap.Calendar = function(config){
16493 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16497 * Fires when a date is selected
16498 * @param {DatePicker} this
16499 * @param {Date} date The selected date
16503 * @event monthchange
16504 * Fires when the displayed month changes
16505 * @param {DatePicker} this
16506 * @param {Date} date The selected month
16508 'monthchange': true,
16510 * @event evententer
16511 * Fires when mouse over an event
16512 * @param {Calendar} this
16513 * @param {event} Event
16515 'evententer': true,
16517 * @event eventleave
16518 * Fires when the mouse leaves an
16519 * @param {Calendar} this
16522 'eventleave': true,
16524 * @event eventclick
16525 * Fires when the mouse click an
16526 * @param {Calendar} this
16535 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16538 * @cfg {Number} startDay
16539 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16547 getAutoCreate : function(){
16550 var fc_button = function(name, corner, style, content ) {
16551 return Roo.apply({},{
16553 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16555 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16558 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16569 style : 'width:100%',
16576 cls : 'fc-header-left',
16578 fc_button('prev', 'left', 'arrow', '‹' ),
16579 fc_button('next', 'right', 'arrow', '›' ),
16580 { tag: 'span', cls: 'fc-header-space' },
16581 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16589 cls : 'fc-header-center',
16593 cls: 'fc-header-title',
16596 html : 'month / year'
16604 cls : 'fc-header-right',
16606 /* fc_button('month', 'left', '', 'month' ),
16607 fc_button('week', '', '', 'week' ),
16608 fc_button('day', 'right', '', 'day' )
16620 header = this.header;
16623 var cal_heads = function() {
16625 // fixme - handle this.
16627 for (var i =0; i < Date.dayNames.length; i++) {
16628 var d = Date.dayNames[i];
16631 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16632 html : d.substring(0,3)
16636 ret[0].cls += ' fc-first';
16637 ret[6].cls += ' fc-last';
16640 var cal_cell = function(n) {
16643 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16648 cls: 'fc-day-number',
16652 cls: 'fc-day-content',
16656 style: 'position: relative;' // height: 17px;
16668 var cal_rows = function() {
16671 for (var r = 0; r < 6; r++) {
16678 for (var i =0; i < Date.dayNames.length; i++) {
16679 var d = Date.dayNames[i];
16680 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16683 row.cn[0].cls+=' fc-first';
16684 row.cn[0].cn[0].style = 'min-height:90px';
16685 row.cn[6].cls+=' fc-last';
16689 ret[0].cls += ' fc-first';
16690 ret[4].cls += ' fc-prev-last';
16691 ret[5].cls += ' fc-last';
16698 cls: 'fc-border-separate',
16699 style : 'width:100%',
16707 cls : 'fc-first fc-last',
16725 cls : 'fc-content',
16726 style : "position: relative;",
16729 cls : 'fc-view fc-view-month fc-grid',
16730 style : 'position: relative',
16731 unselectable : 'on',
16734 cls : 'fc-event-container',
16735 style : 'position:absolute;z-index:8;top:0;left:0;'
16753 initEvents : function()
16756 throw "can not find store for calendar";
16762 style: "text-align:center",
16766 style: "background-color:white;width:50%;margin:250 auto",
16770 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16781 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16783 var size = this.el.select('.fc-content', true).first().getSize();
16784 this.maskEl.setSize(size.width, size.height);
16785 this.maskEl.enableDisplayMode("block");
16786 if(!this.loadMask){
16787 this.maskEl.hide();
16790 this.store = Roo.factory(this.store, Roo.data);
16791 this.store.on('load', this.onLoad, this);
16792 this.store.on('beforeload', this.onBeforeLoad, this);
16796 this.cells = this.el.select('.fc-day',true);
16797 //Roo.log(this.cells);
16798 this.textNodes = this.el.query('.fc-day-number');
16799 this.cells.addClassOnOver('fc-state-hover');
16801 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16802 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16803 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16804 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16806 this.on('monthchange', this.onMonthChange, this);
16808 this.update(new Date().clearTime());
16811 resize : function() {
16812 var sz = this.el.getSize();
16814 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16815 this.el.select('.fc-day-content div',true).setHeight(34);
16820 showPrevMonth : function(e){
16821 this.update(this.activeDate.add("mo", -1));
16823 showToday : function(e){
16824 this.update(new Date().clearTime());
16827 showNextMonth : function(e){
16828 this.update(this.activeDate.add("mo", 1));
16832 showPrevYear : function(){
16833 this.update(this.activeDate.add("y", -1));
16837 showNextYear : function(){
16838 this.update(this.activeDate.add("y", 1));
16843 update : function(date)
16845 var vd = this.activeDate;
16846 this.activeDate = date;
16847 // if(vd && this.el){
16848 // var t = date.getTime();
16849 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16850 // Roo.log('using add remove');
16852 // this.fireEvent('monthchange', this, date);
16854 // this.cells.removeClass("fc-state-highlight");
16855 // this.cells.each(function(c){
16856 // if(c.dateValue == t){
16857 // c.addClass("fc-state-highlight");
16858 // setTimeout(function(){
16859 // try{c.dom.firstChild.focus();}catch(e){}
16869 var days = date.getDaysInMonth();
16871 var firstOfMonth = date.getFirstDateOfMonth();
16872 var startingPos = firstOfMonth.getDay()-this.startDay;
16874 if(startingPos < this.startDay){
16878 var pm = date.add(Date.MONTH, -1);
16879 var prevStart = pm.getDaysInMonth()-startingPos;
16881 this.cells = this.el.select('.fc-day',true);
16882 this.textNodes = this.el.query('.fc-day-number');
16883 this.cells.addClassOnOver('fc-state-hover');
16885 var cells = this.cells.elements;
16886 var textEls = this.textNodes;
16888 Roo.each(cells, function(cell){
16889 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16892 days += startingPos;
16894 // convert everything to numbers so it's fast
16895 var day = 86400000;
16896 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16899 //Roo.log(prevStart);
16901 var today = new Date().clearTime().getTime();
16902 var sel = date.clearTime().getTime();
16903 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16904 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16905 var ddMatch = this.disabledDatesRE;
16906 var ddText = this.disabledDatesText;
16907 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16908 var ddaysText = this.disabledDaysText;
16909 var format = this.format;
16911 var setCellClass = function(cal, cell){
16915 //Roo.log('set Cell Class');
16917 var t = d.getTime();
16921 cell.dateValue = t;
16923 cell.className += " fc-today";
16924 cell.className += " fc-state-highlight";
16925 cell.title = cal.todayText;
16928 // disable highlight in other month..
16929 //cell.className += " fc-state-highlight";
16934 cell.className = " fc-state-disabled";
16935 cell.title = cal.minText;
16939 cell.className = " fc-state-disabled";
16940 cell.title = cal.maxText;
16944 if(ddays.indexOf(d.getDay()) != -1){
16945 cell.title = ddaysText;
16946 cell.className = " fc-state-disabled";
16949 if(ddMatch && format){
16950 var fvalue = d.dateFormat(format);
16951 if(ddMatch.test(fvalue)){
16952 cell.title = ddText.replace("%0", fvalue);
16953 cell.className = " fc-state-disabled";
16957 if (!cell.initialClassName) {
16958 cell.initialClassName = cell.dom.className;
16961 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16966 for(; i < startingPos; i++) {
16967 textEls[i].innerHTML = (++prevStart);
16968 d.setDate(d.getDate()+1);
16970 cells[i].className = "fc-past fc-other-month";
16971 setCellClass(this, cells[i]);
16976 for(; i < days; i++){
16977 intDay = i - startingPos + 1;
16978 textEls[i].innerHTML = (intDay);
16979 d.setDate(d.getDate()+1);
16981 cells[i].className = ''; // "x-date-active";
16982 setCellClass(this, cells[i]);
16986 for(; i < 42; i++) {
16987 textEls[i].innerHTML = (++extraDays);
16988 d.setDate(d.getDate()+1);
16990 cells[i].className = "fc-future fc-other-month";
16991 setCellClass(this, cells[i]);
16994 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16996 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16998 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16999 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17001 if(totalRows != 6){
17002 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17003 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17006 this.fireEvent('monthchange', this, date);
17010 if(!this.internalRender){
17011 var main = this.el.dom.firstChild;
17012 var w = main.offsetWidth;
17013 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17014 Roo.fly(main).setWidth(w);
17015 this.internalRender = true;
17016 // opera does not respect the auto grow header center column
17017 // then, after it gets a width opera refuses to recalculate
17018 // without a second pass
17019 if(Roo.isOpera && !this.secondPass){
17020 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17021 this.secondPass = true;
17022 this.update.defer(10, this, [date]);
17029 findCell : function(dt) {
17030 dt = dt.clearTime().getTime();
17032 this.cells.each(function(c){
17033 //Roo.log("check " +c.dateValue + '?=' + dt);
17034 if(c.dateValue == dt){
17044 findCells : function(ev) {
17045 var s = ev.start.clone().clearTime().getTime();
17047 var e= ev.end.clone().clearTime().getTime();
17050 this.cells.each(function(c){
17051 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17053 if(c.dateValue > e){
17056 if(c.dateValue < s){
17065 // findBestRow: function(cells)
17069 // for (var i =0 ; i < cells.length;i++) {
17070 // ret = Math.max(cells[i].rows || 0,ret);
17077 addItem : function(ev)
17079 // look for vertical location slot in
17080 var cells = this.findCells(ev);
17082 // ev.row = this.findBestRow(cells);
17084 // work out the location.
17088 for(var i =0; i < cells.length; i++) {
17090 cells[i].row = cells[0].row;
17093 cells[i].row = cells[i].row + 1;
17103 if (crow.start.getY() == cells[i].getY()) {
17105 crow.end = cells[i];
17122 cells[0].events.push(ev);
17124 this.calevents.push(ev);
17127 clearEvents: function() {
17129 if(!this.calevents){
17133 Roo.each(this.cells.elements, function(c){
17139 Roo.each(this.calevents, function(e) {
17140 Roo.each(e.els, function(el) {
17141 el.un('mouseenter' ,this.onEventEnter, this);
17142 el.un('mouseleave' ,this.onEventLeave, this);
17147 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17153 renderEvents: function()
17157 this.cells.each(function(c) {
17166 if(c.row != c.events.length){
17167 r = 4 - (4 - (c.row - c.events.length));
17170 c.events = ev.slice(0, r);
17171 c.more = ev.slice(r);
17173 if(c.more.length && c.more.length == 1){
17174 c.events.push(c.more.pop());
17177 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17181 this.cells.each(function(c) {
17183 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17186 for (var e = 0; e < c.events.length; e++){
17187 var ev = c.events[e];
17188 var rows = ev.rows;
17190 for(var i = 0; i < rows.length; i++) {
17192 // how many rows should it span..
17195 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17196 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17198 unselectable : "on",
17201 cls: 'fc-event-inner',
17205 // cls: 'fc-event-time',
17206 // html : cells.length > 1 ? '' : ev.time
17210 cls: 'fc-event-title',
17211 html : String.format('{0}', ev.title)
17218 cls: 'ui-resizable-handle ui-resizable-e',
17219 html : '  '
17226 cfg.cls += ' fc-event-start';
17228 if ((i+1) == rows.length) {
17229 cfg.cls += ' fc-event-end';
17232 var ctr = _this.el.select('.fc-event-container',true).first();
17233 var cg = ctr.createChild(cfg);
17235 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17236 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17238 var r = (c.more.length) ? 1 : 0;
17239 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17240 cg.setWidth(ebox.right - sbox.x -2);
17242 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17243 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17244 cg.on('click', _this.onEventClick, _this, ev);
17255 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17256 style : 'position: absolute',
17257 unselectable : "on",
17260 cls: 'fc-event-inner',
17264 cls: 'fc-event-title',
17272 cls: 'ui-resizable-handle ui-resizable-e',
17273 html : '  '
17279 var ctr = _this.el.select('.fc-event-container',true).first();
17280 var cg = ctr.createChild(cfg);
17282 var sbox = c.select('.fc-day-content',true).first().getBox();
17283 var ebox = c.select('.fc-day-content',true).first().getBox();
17285 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17286 cg.setWidth(ebox.right - sbox.x -2);
17288 cg.on('click', _this.onMoreEventClick, _this, c.more);
17298 onEventEnter: function (e, el,event,d) {
17299 this.fireEvent('evententer', this, el, event);
17302 onEventLeave: function (e, el,event,d) {
17303 this.fireEvent('eventleave', this, el, event);
17306 onEventClick: function (e, el,event,d) {
17307 this.fireEvent('eventclick', this, el, event);
17310 onMonthChange: function () {
17314 onMoreEventClick: function(e, el, more)
17318 this.calpopover.placement = 'right';
17319 this.calpopover.setTitle('More');
17321 this.calpopover.setContent('');
17323 var ctr = this.calpopover.el.select('.popover-content', true).first();
17325 Roo.each(more, function(m){
17327 cls : 'fc-event-hori fc-event-draggable',
17330 var cg = ctr.createChild(cfg);
17332 cg.on('click', _this.onEventClick, _this, m);
17335 this.calpopover.show(el);
17340 onLoad: function ()
17342 this.calevents = [];
17345 if(this.store.getCount() > 0){
17346 this.store.data.each(function(d){
17349 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17350 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17351 time : d.data.start_time,
17352 title : d.data.title,
17353 description : d.data.description,
17354 venue : d.data.venue
17359 this.renderEvents();
17361 if(this.calevents.length && this.loadMask){
17362 this.maskEl.hide();
17366 onBeforeLoad: function()
17368 this.clearEvents();
17370 this.maskEl.show();
17384 * @class Roo.bootstrap.Popover
17385 * @extends Roo.bootstrap.Component
17386 * Bootstrap Popover class
17387 * @cfg {String} html contents of the popover (or false to use children..)
17388 * @cfg {String} title of popover (or false to hide)
17389 * @cfg {String} placement how it is placed
17390 * @cfg {String} trigger click || hover (or false to trigger manually)
17391 * @cfg {String} over what (parent or false to trigger manually.)
17392 * @cfg {Number} delay - delay before showing
17395 * Create a new Popover
17396 * @param {Object} config The config object
17399 Roo.bootstrap.Popover = function(config){
17400 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17406 * After the popover show
17408 * @param {Roo.bootstrap.Popover} this
17413 * After the popover hide
17415 * @param {Roo.bootstrap.Popover} this
17421 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17423 title: 'Fill in a title',
17426 placement : 'right',
17427 trigger : 'hover', // hover
17433 can_build_overlaid : false,
17435 getChildContainer : function()
17437 return this.el.select('.popover-content',true).first();
17440 getAutoCreate : function(){
17443 cls : 'popover roo-dynamic',
17444 style: 'display:block',
17450 cls : 'popover-inner',
17454 cls: 'popover-title',
17458 cls : 'popover-content',
17469 setTitle: function(str)
17472 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17474 setContent: function(str)
17477 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17479 // as it get's added to the bottom of the page.
17480 onRender : function(ct, position)
17482 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17484 var cfg = Roo.apply({}, this.getAutoCreate());
17488 cfg.cls += ' ' + this.cls;
17491 cfg.style = this.style;
17493 //Roo.log("adding to ");
17494 this.el = Roo.get(document.body).createChild(cfg, position);
17495 // Roo.log(this.el);
17500 initEvents : function()
17502 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17503 this.el.enableDisplayMode('block');
17505 if (this.over === false) {
17508 if (this.triggers === false) {
17511 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17512 var triggers = this.trigger ? this.trigger.split(' ') : [];
17513 Roo.each(triggers, function(trigger) {
17515 if (trigger == 'click') {
17516 on_el.on('click', this.toggle, this);
17517 } else if (trigger != 'manual') {
17518 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17519 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17521 on_el.on(eventIn ,this.enter, this);
17522 on_el.on(eventOut, this.leave, this);
17533 toggle : function () {
17534 this.hoverState == 'in' ? this.leave() : this.enter();
17537 enter : function () {
17539 clearTimeout(this.timeout);
17541 this.hoverState = 'in';
17543 if (!this.delay || !this.delay.show) {
17548 this.timeout = setTimeout(function () {
17549 if (_t.hoverState == 'in') {
17552 }, this.delay.show)
17555 leave : function() {
17556 clearTimeout(this.timeout);
17558 this.hoverState = 'out';
17560 if (!this.delay || !this.delay.hide) {
17565 this.timeout = setTimeout(function () {
17566 if (_t.hoverState == 'out') {
17569 }, this.delay.hide)
17572 show : function (on_el)
17575 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17579 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17580 if (this.html !== false) {
17581 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17583 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17584 if (!this.title.length) {
17585 this.el.select('.popover-title',true).hide();
17588 var placement = typeof this.placement == 'function' ?
17589 this.placement.call(this, this.el, on_el) :
17592 var autoToken = /\s?auto?\s?/i;
17593 var autoPlace = autoToken.test(placement);
17595 placement = placement.replace(autoToken, '') || 'top';
17599 //this.el.setXY([0,0]);
17601 this.el.dom.style.display='block';
17602 this.el.addClass(placement);
17604 //this.el.appendTo(on_el);
17606 var p = this.getPosition();
17607 var box = this.el.getBox();
17612 var align = Roo.bootstrap.Popover.alignment[placement];
17615 this.el.alignTo(on_el, align[0],align[1]);
17616 //var arrow = this.el.select('.arrow',true).first();
17617 //arrow.set(align[2],
17619 this.el.addClass('in');
17622 if (this.el.hasClass('fade')) {
17626 this.hoverState = 'in';
17628 this.fireEvent('show', this);
17633 this.el.setXY([0,0]);
17634 this.el.removeClass('in');
17636 this.hoverState = null;
17638 this.fireEvent('hide', this);
17643 Roo.bootstrap.Popover.alignment = {
17644 'left' : ['r-l', [-10,0], 'right'],
17645 'right' : ['l-r', [10,0], 'left'],
17646 'bottom' : ['t-b', [0,10], 'top'],
17647 'top' : [ 'b-t', [0,-10], 'bottom']
17658 * @class Roo.bootstrap.Progress
17659 * @extends Roo.bootstrap.Component
17660 * Bootstrap Progress class
17661 * @cfg {Boolean} striped striped of the progress bar
17662 * @cfg {Boolean} active animated of the progress bar
17666 * Create a new Progress
17667 * @param {Object} config The config object
17670 Roo.bootstrap.Progress = function(config){
17671 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17674 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17679 getAutoCreate : function(){
17687 cfg.cls += ' progress-striped';
17691 cfg.cls += ' active';
17710 * @class Roo.bootstrap.ProgressBar
17711 * @extends Roo.bootstrap.Component
17712 * Bootstrap ProgressBar class
17713 * @cfg {Number} aria_valuenow aria-value now
17714 * @cfg {Number} aria_valuemin aria-value min
17715 * @cfg {Number} aria_valuemax aria-value max
17716 * @cfg {String} label label for the progress bar
17717 * @cfg {String} panel (success | info | warning | danger )
17718 * @cfg {String} role role of the progress bar
17719 * @cfg {String} sr_only text
17723 * Create a new ProgressBar
17724 * @param {Object} config The config object
17727 Roo.bootstrap.ProgressBar = function(config){
17728 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17731 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17735 aria_valuemax : 100,
17741 getAutoCreate : function()
17746 cls: 'progress-bar',
17747 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17759 cfg.role = this.role;
17762 if(this.aria_valuenow){
17763 cfg['aria-valuenow'] = this.aria_valuenow;
17766 if(this.aria_valuemin){
17767 cfg['aria-valuemin'] = this.aria_valuemin;
17770 if(this.aria_valuemax){
17771 cfg['aria-valuemax'] = this.aria_valuemax;
17774 if(this.label && !this.sr_only){
17775 cfg.html = this.label;
17779 cfg.cls += ' progress-bar-' + this.panel;
17785 update : function(aria_valuenow)
17787 this.aria_valuenow = aria_valuenow;
17789 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17804 * @class Roo.bootstrap.TabGroup
17805 * @extends Roo.bootstrap.Column
17806 * Bootstrap Column class
17807 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17808 * @cfg {Boolean} carousel true to make the group behave like a carousel
17809 * @cfg {Boolean} bullets show bullets for the panels
17810 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17811 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17812 * @cfg {Boolean} showarrow (true|false) show arrow default true
17815 * Create a new TabGroup
17816 * @param {Object} config The config object
17819 Roo.bootstrap.TabGroup = function(config){
17820 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17822 this.navId = Roo.id();
17825 Roo.bootstrap.TabGroup.register(this);
17829 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17832 transition : false,
17837 slideOnTouch : false,
17840 getAutoCreate : function()
17842 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17844 cfg.cls += ' tab-content';
17846 if (this.carousel) {
17847 cfg.cls += ' carousel slide';
17850 cls : 'carousel-inner',
17854 if(this.bullets && !Roo.isTouch){
17857 cls : 'carousel-bullets',
17861 if(this.bullets_cls){
17862 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17869 cfg.cn[0].cn.push(bullets);
17872 if(this.showarrow){
17873 cfg.cn[0].cn.push({
17875 class : 'carousel-arrow',
17879 class : 'carousel-prev',
17883 class : 'fa fa-chevron-left'
17889 class : 'carousel-next',
17893 class : 'fa fa-chevron-right'
17906 initEvents: function()
17908 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17909 // this.el.on("touchstart", this.onTouchStart, this);
17912 if(this.autoslide){
17915 this.slideFn = window.setInterval(function() {
17916 _this.showPanelNext();
17920 if(this.showarrow){
17921 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17922 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17928 // onTouchStart : function(e, el, o)
17930 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17934 // this.showPanelNext();
17938 getChildContainer : function()
17940 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17944 * register a Navigation item
17945 * @param {Roo.bootstrap.NavItem} the navitem to add
17947 register : function(item)
17949 this.tabs.push( item);
17950 item.navId = this.navId; // not really needed..
17955 getActivePanel : function()
17958 Roo.each(this.tabs, function(t) {
17968 getPanelByName : function(n)
17971 Roo.each(this.tabs, function(t) {
17972 if (t.tabId == n) {
17980 indexOfPanel : function(p)
17983 Roo.each(this.tabs, function(t,i) {
17984 if (t.tabId == p.tabId) {
17993 * show a specific panel
17994 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17995 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17997 showPanel : function (pan)
17999 if(this.transition || typeof(pan) == 'undefined'){
18000 Roo.log("waiting for the transitionend");
18004 if (typeof(pan) == 'number') {
18005 pan = this.tabs[pan];
18008 if (typeof(pan) == 'string') {
18009 pan = this.getPanelByName(pan);
18012 var cur = this.getActivePanel();
18015 Roo.log('pan or acitve pan is undefined');
18019 if (pan.tabId == this.getActivePanel().tabId) {
18023 if (false === cur.fireEvent('beforedeactivate')) {
18027 if(this.bullets > 0 && !Roo.isTouch){
18028 this.setActiveBullet(this.indexOfPanel(pan));
18031 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18033 this.transition = true;
18034 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18035 var lr = dir == 'next' ? 'left' : 'right';
18036 pan.el.addClass(dir); // or prev
18037 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18038 cur.el.addClass(lr); // or right
18039 pan.el.addClass(lr);
18042 cur.el.on('transitionend', function() {
18043 Roo.log("trans end?");
18045 pan.el.removeClass([lr,dir]);
18046 pan.setActive(true);
18048 cur.el.removeClass([lr]);
18049 cur.setActive(false);
18051 _this.transition = false;
18053 }, this, { single: true } );
18058 cur.setActive(false);
18059 pan.setActive(true);
18064 showPanelNext : function()
18066 var i = this.indexOfPanel(this.getActivePanel());
18068 if (i >= this.tabs.length - 1 && !this.autoslide) {
18072 if (i >= this.tabs.length - 1 && this.autoslide) {
18076 this.showPanel(this.tabs[i+1]);
18079 showPanelPrev : function()
18081 var i = this.indexOfPanel(this.getActivePanel());
18083 if (i < 1 && !this.autoslide) {
18087 if (i < 1 && this.autoslide) {
18088 i = this.tabs.length;
18091 this.showPanel(this.tabs[i-1]);
18095 addBullet: function()
18097 if(!this.bullets || Roo.isTouch){
18100 var ctr = this.el.select('.carousel-bullets',true).first();
18101 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18102 var bullet = ctr.createChild({
18103 cls : 'bullet bullet-' + i
18104 },ctr.dom.lastChild);
18109 bullet.on('click', (function(e, el, o, ii, t){
18111 e.preventDefault();
18113 this.showPanel(ii);
18115 if(this.autoslide && this.slideFn){
18116 clearInterval(this.slideFn);
18117 this.slideFn = window.setInterval(function() {
18118 _this.showPanelNext();
18122 }).createDelegate(this, [i, bullet], true));
18127 setActiveBullet : function(i)
18133 Roo.each(this.el.select('.bullet', true).elements, function(el){
18134 el.removeClass('selected');
18137 var bullet = this.el.select('.bullet-' + i, true).first();
18143 bullet.addClass('selected');
18154 Roo.apply(Roo.bootstrap.TabGroup, {
18158 * register a Navigation Group
18159 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18161 register : function(navgrp)
18163 this.groups[navgrp.navId] = navgrp;
18167 * fetch a Navigation Group based on the navigation ID
18168 * if one does not exist , it will get created.
18169 * @param {string} the navgroup to add
18170 * @returns {Roo.bootstrap.NavGroup} the navgroup
18172 get: function(navId) {
18173 if (typeof(this.groups[navId]) == 'undefined') {
18174 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18176 return this.groups[navId] ;
18191 * @class Roo.bootstrap.TabPanel
18192 * @extends Roo.bootstrap.Component
18193 * Bootstrap TabPanel class
18194 * @cfg {Boolean} active panel active
18195 * @cfg {String} html panel content
18196 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18197 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18198 * @cfg {String} href click to link..
18202 * Create a new TabPanel
18203 * @param {Object} config The config object
18206 Roo.bootstrap.TabPanel = function(config){
18207 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18211 * Fires when the active status changes
18212 * @param {Roo.bootstrap.TabPanel} this
18213 * @param {Boolean} state the new state
18218 * @event beforedeactivate
18219 * Fires before a tab is de-activated - can be used to do validation on a form.
18220 * @param {Roo.bootstrap.TabPanel} this
18221 * @return {Boolean} false if there is an error
18224 'beforedeactivate': true
18227 this.tabId = this.tabId || Roo.id();
18231 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18239 getAutoCreate : function(){
18242 // item is needed for carousel - not sure if it has any effect otherwise
18243 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18244 html: this.html || ''
18248 cfg.cls += ' active';
18252 cfg.tabId = this.tabId;
18259 initEvents: function()
18261 var p = this.parent();
18263 this.navId = this.navId || p.navId;
18265 if (typeof(this.navId) != 'undefined') {
18266 // not really needed.. but just in case.. parent should be a NavGroup.
18267 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18271 var i = tg.tabs.length - 1;
18273 if(this.active && tg.bullets > 0 && i < tg.bullets){
18274 tg.setActiveBullet(i);
18278 this.el.on('click', this.onClick, this);
18281 this.el.on("touchstart", this.onTouchStart, this);
18282 this.el.on("touchmove", this.onTouchMove, this);
18283 this.el.on("touchend", this.onTouchEnd, this);
18288 onRender : function(ct, position)
18290 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18293 setActive : function(state)
18295 Roo.log("panel - set active " + this.tabId + "=" + state);
18297 this.active = state;
18299 this.el.removeClass('active');
18301 } else if (!this.el.hasClass('active')) {
18302 this.el.addClass('active');
18305 this.fireEvent('changed', this, state);
18308 onClick : function(e)
18310 e.preventDefault();
18312 if(!this.href.length){
18316 window.location.href = this.href;
18325 onTouchStart : function(e)
18327 this.swiping = false;
18329 this.startX = e.browserEvent.touches[0].clientX;
18330 this.startY = e.browserEvent.touches[0].clientY;
18333 onTouchMove : function(e)
18335 this.swiping = true;
18337 this.endX = e.browserEvent.touches[0].clientX;
18338 this.endY = e.browserEvent.touches[0].clientY;
18341 onTouchEnd : function(e)
18348 var tabGroup = this.parent();
18350 if(this.endX > this.startX){ // swiping right
18351 tabGroup.showPanelPrev();
18355 if(this.startX > this.endX){ // swiping left
18356 tabGroup.showPanelNext();
18375 * @class Roo.bootstrap.DateField
18376 * @extends Roo.bootstrap.Input
18377 * Bootstrap DateField class
18378 * @cfg {Number} weekStart default 0
18379 * @cfg {String} viewMode default empty, (months|years)
18380 * @cfg {String} minViewMode default empty, (months|years)
18381 * @cfg {Number} startDate default -Infinity
18382 * @cfg {Number} endDate default Infinity
18383 * @cfg {Boolean} todayHighlight default false
18384 * @cfg {Boolean} todayBtn default false
18385 * @cfg {Boolean} calendarWeeks default false
18386 * @cfg {Object} daysOfWeekDisabled default empty
18387 * @cfg {Boolean} singleMode default false (true | false)
18389 * @cfg {Boolean} keyboardNavigation default true
18390 * @cfg {String} language default en
18393 * Create a new DateField
18394 * @param {Object} config The config object
18397 Roo.bootstrap.DateField = function(config){
18398 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18402 * Fires when this field show.
18403 * @param {Roo.bootstrap.DateField} this
18404 * @param {Mixed} date The date value
18409 * Fires when this field hide.
18410 * @param {Roo.bootstrap.DateField} this
18411 * @param {Mixed} date The date value
18416 * Fires when select a date.
18417 * @param {Roo.bootstrap.DateField} this
18418 * @param {Mixed} date The date value
18422 * @event beforeselect
18423 * Fires when before select a date.
18424 * @param {Roo.bootstrap.DateField} this
18425 * @param {Mixed} date The date value
18427 beforeselect : true
18431 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18434 * @cfg {String} format
18435 * The default date format string which can be overriden for localization support. The format must be
18436 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18440 * @cfg {String} altFormats
18441 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18442 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18444 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18452 todayHighlight : false,
18458 keyboardNavigation: true,
18460 calendarWeeks: false,
18462 startDate: -Infinity,
18466 daysOfWeekDisabled: [],
18470 singleMode : false,
18472 UTCDate: function()
18474 return new Date(Date.UTC.apply(Date, arguments));
18477 UTCToday: function()
18479 var today = new Date();
18480 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18483 getDate: function() {
18484 var d = this.getUTCDate();
18485 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18488 getUTCDate: function() {
18492 setDate: function(d) {
18493 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18496 setUTCDate: function(d) {
18498 this.setValue(this.formatDate(this.date));
18501 onRender: function(ct, position)
18504 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18506 this.language = this.language || 'en';
18507 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18508 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18510 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18511 this.format = this.format || 'm/d/y';
18512 this.isInline = false;
18513 this.isInput = true;
18514 this.component = this.el.select('.add-on', true).first() || false;
18515 this.component = (this.component && this.component.length === 0) ? false : this.component;
18516 this.hasInput = this.component && this.inputEl().length;
18518 if (typeof(this.minViewMode === 'string')) {
18519 switch (this.minViewMode) {
18521 this.minViewMode = 1;
18524 this.minViewMode = 2;
18527 this.minViewMode = 0;
18532 if (typeof(this.viewMode === 'string')) {
18533 switch (this.viewMode) {
18546 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18548 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18550 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18552 this.picker().on('mousedown', this.onMousedown, this);
18553 this.picker().on('click', this.onClick, this);
18555 this.picker().addClass('datepicker-dropdown');
18557 this.startViewMode = this.viewMode;
18559 if(this.singleMode){
18560 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18561 v.setVisibilityMode(Roo.Element.DISPLAY);
18565 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18566 v.setStyle('width', '189px');
18570 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18571 if(!this.calendarWeeks){
18576 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18577 v.attr('colspan', function(i, val){
18578 return parseInt(val) + 1;
18583 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18585 this.setStartDate(this.startDate);
18586 this.setEndDate(this.endDate);
18588 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18595 if(this.isInline) {
18600 picker : function()
18602 return this.pickerEl;
18603 // return this.el.select('.datepicker', true).first();
18606 fillDow: function()
18608 var dowCnt = this.weekStart;
18617 if(this.calendarWeeks){
18625 while (dowCnt < this.weekStart + 7) {
18629 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18633 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18636 fillMonths: function()
18639 var months = this.picker().select('>.datepicker-months td', true).first();
18641 months.dom.innerHTML = '';
18647 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18650 months.createChild(month);
18657 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;
18659 if (this.date < this.startDate) {
18660 this.viewDate = new Date(this.startDate);
18661 } else if (this.date > this.endDate) {
18662 this.viewDate = new Date(this.endDate);
18664 this.viewDate = new Date(this.date);
18672 var d = new Date(this.viewDate),
18673 year = d.getUTCFullYear(),
18674 month = d.getUTCMonth(),
18675 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18676 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18677 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18678 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18679 currentDate = this.date && this.date.valueOf(),
18680 today = this.UTCToday();
18682 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18684 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18686 // this.picker.select('>tfoot th.today').
18687 // .text(dates[this.language].today)
18688 // .toggle(this.todayBtn !== false);
18690 this.updateNavArrows();
18693 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18695 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18697 prevMonth.setUTCDate(day);
18699 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18701 var nextMonth = new Date(prevMonth);
18703 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18705 nextMonth = nextMonth.valueOf();
18707 var fillMonths = false;
18709 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18711 while(prevMonth.valueOf() <= nextMonth) {
18714 if (prevMonth.getUTCDay() === this.weekStart) {
18716 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18724 if(this.calendarWeeks){
18725 // ISO 8601: First week contains first thursday.
18726 // ISO also states week starts on Monday, but we can be more abstract here.
18728 // Start of current week: based on weekstart/current date
18729 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18730 // Thursday of this week
18731 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18732 // First Thursday of year, year from thursday
18733 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18734 // Calendar week: ms between thursdays, div ms per day, div 7 days
18735 calWeek = (th - yth) / 864e5 / 7 + 1;
18737 fillMonths.cn.push({
18745 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18747 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18750 if (this.todayHighlight &&
18751 prevMonth.getUTCFullYear() == today.getFullYear() &&
18752 prevMonth.getUTCMonth() == today.getMonth() &&
18753 prevMonth.getUTCDate() == today.getDate()) {
18754 clsName += ' today';
18757 if (currentDate && prevMonth.valueOf() === currentDate) {
18758 clsName += ' active';
18761 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18762 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18763 clsName += ' disabled';
18766 fillMonths.cn.push({
18768 cls: 'day ' + clsName,
18769 html: prevMonth.getDate()
18772 prevMonth.setDate(prevMonth.getDate()+1);
18775 var currentYear = this.date && this.date.getUTCFullYear();
18776 var currentMonth = this.date && this.date.getUTCMonth();
18778 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18780 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18781 v.removeClass('active');
18783 if(currentYear === year && k === currentMonth){
18784 v.addClass('active');
18787 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18788 v.addClass('disabled');
18794 year = parseInt(year/10, 10) * 10;
18796 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18798 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18801 for (var i = -1; i < 11; i++) {
18802 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18804 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18812 showMode: function(dir)
18815 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18818 Roo.each(this.picker().select('>div',true).elements, function(v){
18819 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18822 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18827 if(this.isInline) {
18831 this.picker().removeClass(['bottom', 'top']);
18833 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18835 * place to the top of element!
18839 this.picker().addClass('top');
18840 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18845 this.picker().addClass('bottom');
18847 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18850 parseDate : function(value)
18852 if(!value || value instanceof Date){
18855 var v = Date.parseDate(value, this.format);
18856 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18857 v = Date.parseDate(value, 'Y-m-d');
18859 if(!v && this.altFormats){
18860 if(!this.altFormatsArray){
18861 this.altFormatsArray = this.altFormats.split("|");
18863 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18864 v = Date.parseDate(value, this.altFormatsArray[i]);
18870 formatDate : function(date, fmt)
18872 return (!date || !(date instanceof Date)) ?
18873 date : date.dateFormat(fmt || this.format);
18876 onFocus : function()
18878 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18882 onBlur : function()
18884 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18886 var d = this.inputEl().getValue();
18893 showPopup : function()
18895 this.picker().show();
18899 this.fireEvent('showpopup', this, this.date);
18902 hidePopup : function()
18904 if(this.isInline) {
18907 this.picker().hide();
18908 this.viewMode = this.startViewMode;
18911 this.fireEvent('hidepopup', this, this.date);
18915 onMousedown: function(e)
18917 e.stopPropagation();
18918 e.preventDefault();
18923 Roo.bootstrap.DateField.superclass.keyup.call(this);
18927 setValue: function(v)
18929 if(this.fireEvent('beforeselect', this, v) !== false){
18930 var d = new Date(this.parseDate(v) ).clearTime();
18932 if(isNaN(d.getTime())){
18933 this.date = this.viewDate = '';
18934 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18938 v = this.formatDate(d);
18940 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18942 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18946 this.fireEvent('select', this, this.date);
18950 getValue: function()
18952 return this.formatDate(this.date);
18955 fireKey: function(e)
18957 if (!this.picker().isVisible()){
18958 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18964 var dateChanged = false,
18966 newDate, newViewDate;
18971 e.preventDefault();
18975 if (!this.keyboardNavigation) {
18978 dir = e.keyCode == 37 ? -1 : 1;
18981 newDate = this.moveYear(this.date, dir);
18982 newViewDate = this.moveYear(this.viewDate, dir);
18983 } else if (e.shiftKey){
18984 newDate = this.moveMonth(this.date, dir);
18985 newViewDate = this.moveMonth(this.viewDate, dir);
18987 newDate = new Date(this.date);
18988 newDate.setUTCDate(this.date.getUTCDate() + dir);
18989 newViewDate = new Date(this.viewDate);
18990 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18992 if (this.dateWithinRange(newDate)){
18993 this.date = newDate;
18994 this.viewDate = newViewDate;
18995 this.setValue(this.formatDate(this.date));
18997 e.preventDefault();
18998 dateChanged = true;
19003 if (!this.keyboardNavigation) {
19006 dir = e.keyCode == 38 ? -1 : 1;
19008 newDate = this.moveYear(this.date, dir);
19009 newViewDate = this.moveYear(this.viewDate, dir);
19010 } else if (e.shiftKey){
19011 newDate = this.moveMonth(this.date, dir);
19012 newViewDate = this.moveMonth(this.viewDate, dir);
19014 newDate = new Date(this.date);
19015 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19016 newViewDate = new Date(this.viewDate);
19017 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19019 if (this.dateWithinRange(newDate)){
19020 this.date = newDate;
19021 this.viewDate = newViewDate;
19022 this.setValue(this.formatDate(this.date));
19024 e.preventDefault();
19025 dateChanged = true;
19029 this.setValue(this.formatDate(this.date));
19031 e.preventDefault();
19034 this.setValue(this.formatDate(this.date));
19048 onClick: function(e)
19050 e.stopPropagation();
19051 e.preventDefault();
19053 var target = e.getTarget();
19055 if(target.nodeName.toLowerCase() === 'i'){
19056 target = Roo.get(target).dom.parentNode;
19059 var nodeName = target.nodeName;
19060 var className = target.className;
19061 var html = target.innerHTML;
19062 //Roo.log(nodeName);
19064 switch(nodeName.toLowerCase()) {
19066 switch(className) {
19072 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19073 switch(this.viewMode){
19075 this.viewDate = this.moveMonth(this.viewDate, dir);
19079 this.viewDate = this.moveYear(this.viewDate, dir);
19085 var date = new Date();
19086 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19088 this.setValue(this.formatDate(this.date));
19095 if (className.indexOf('disabled') < 0) {
19096 this.viewDate.setUTCDate(1);
19097 if (className.indexOf('month') > -1) {
19098 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19100 var year = parseInt(html, 10) || 0;
19101 this.viewDate.setUTCFullYear(year);
19105 if(this.singleMode){
19106 this.setValue(this.formatDate(this.viewDate));
19117 //Roo.log(className);
19118 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19119 var day = parseInt(html, 10) || 1;
19120 var year = this.viewDate.getUTCFullYear(),
19121 month = this.viewDate.getUTCMonth();
19123 if (className.indexOf('old') > -1) {
19130 } else if (className.indexOf('new') > -1) {
19138 //Roo.log([year,month,day]);
19139 this.date = this.UTCDate(year, month, day,0,0,0,0);
19140 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19142 //Roo.log(this.formatDate(this.date));
19143 this.setValue(this.formatDate(this.date));
19150 setStartDate: function(startDate)
19152 this.startDate = startDate || -Infinity;
19153 if (this.startDate !== -Infinity) {
19154 this.startDate = this.parseDate(this.startDate);
19157 this.updateNavArrows();
19160 setEndDate: function(endDate)
19162 this.endDate = endDate || Infinity;
19163 if (this.endDate !== Infinity) {
19164 this.endDate = this.parseDate(this.endDate);
19167 this.updateNavArrows();
19170 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19172 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19173 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19174 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19176 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19177 return parseInt(d, 10);
19180 this.updateNavArrows();
19183 updateNavArrows: function()
19185 if(this.singleMode){
19189 var d = new Date(this.viewDate),
19190 year = d.getUTCFullYear(),
19191 month = d.getUTCMonth();
19193 Roo.each(this.picker().select('.prev', true).elements, function(v){
19195 switch (this.viewMode) {
19198 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19204 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19211 Roo.each(this.picker().select('.next', true).elements, function(v){
19213 switch (this.viewMode) {
19216 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19222 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19230 moveMonth: function(date, dir)
19235 var new_date = new Date(date.valueOf()),
19236 day = new_date.getUTCDate(),
19237 month = new_date.getUTCMonth(),
19238 mag = Math.abs(dir),
19240 dir = dir > 0 ? 1 : -1;
19243 // If going back one month, make sure month is not current month
19244 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19246 return new_date.getUTCMonth() == month;
19248 // If going forward one month, make sure month is as expected
19249 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19251 return new_date.getUTCMonth() != new_month;
19253 new_month = month + dir;
19254 new_date.setUTCMonth(new_month);
19255 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19256 if (new_month < 0 || new_month > 11) {
19257 new_month = (new_month + 12) % 12;
19260 // For magnitudes >1, move one month at a time...
19261 for (var i=0; i<mag; i++) {
19262 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19263 new_date = this.moveMonth(new_date, dir);
19265 // ...then reset the day, keeping it in the new month
19266 new_month = new_date.getUTCMonth();
19267 new_date.setUTCDate(day);
19269 return new_month != new_date.getUTCMonth();
19272 // Common date-resetting loop -- if date is beyond end of month, make it
19275 new_date.setUTCDate(--day);
19276 new_date.setUTCMonth(new_month);
19281 moveYear: function(date, dir)
19283 return this.moveMonth(date, dir*12);
19286 dateWithinRange: function(date)
19288 return date >= this.startDate && date <= this.endDate;
19294 this.picker().remove();
19297 validateValue : function(value)
19299 if(this.getVisibilityEl().hasClass('hidden')){
19303 if(value.length < 1) {
19304 if(this.allowBlank){
19310 if(value.length < this.minLength){
19313 if(value.length > this.maxLength){
19317 var vt = Roo.form.VTypes;
19318 if(!vt[this.vtype](value, this)){
19322 if(typeof this.validator == "function"){
19323 var msg = this.validator(value);
19329 if(this.regex && !this.regex.test(value)){
19333 if(typeof(this.parseDate(value)) == 'undefined'){
19337 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19341 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19351 this.date = this.viewDate = '';
19353 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19358 Roo.apply(Roo.bootstrap.DateField, {
19369 html: '<i class="fa fa-arrow-left"/>'
19379 html: '<i class="fa fa-arrow-right"/>'
19421 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19422 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19423 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19424 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19425 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19438 navFnc: 'FullYear',
19443 navFnc: 'FullYear',
19448 Roo.apply(Roo.bootstrap.DateField, {
19452 cls: 'datepicker dropdown-menu roo-dynamic',
19456 cls: 'datepicker-days',
19460 cls: 'table-condensed',
19462 Roo.bootstrap.DateField.head,
19466 Roo.bootstrap.DateField.footer
19473 cls: 'datepicker-months',
19477 cls: 'table-condensed',
19479 Roo.bootstrap.DateField.head,
19480 Roo.bootstrap.DateField.content,
19481 Roo.bootstrap.DateField.footer
19488 cls: 'datepicker-years',
19492 cls: 'table-condensed',
19494 Roo.bootstrap.DateField.head,
19495 Roo.bootstrap.DateField.content,
19496 Roo.bootstrap.DateField.footer
19515 * @class Roo.bootstrap.TimeField
19516 * @extends Roo.bootstrap.Input
19517 * Bootstrap DateField class
19521 * Create a new TimeField
19522 * @param {Object} config The config object
19525 Roo.bootstrap.TimeField = function(config){
19526 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19530 * Fires when this field show.
19531 * @param {Roo.bootstrap.DateField} thisthis
19532 * @param {Mixed} date The date value
19537 * Fires when this field hide.
19538 * @param {Roo.bootstrap.DateField} this
19539 * @param {Mixed} date The date value
19544 * Fires when select a date.
19545 * @param {Roo.bootstrap.DateField} this
19546 * @param {Mixed} date The date value
19552 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19555 * @cfg {String} format
19556 * The default time format string which can be overriden for localization support. The format must be
19557 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19561 onRender: function(ct, position)
19564 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19566 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19568 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19570 this.pop = this.picker().select('>.datepicker-time',true).first();
19571 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19573 this.picker().on('mousedown', this.onMousedown, this);
19574 this.picker().on('click', this.onClick, this);
19576 this.picker().addClass('datepicker-dropdown');
19581 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19582 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19583 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19584 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19585 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19586 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19590 fireKey: function(e){
19591 if (!this.picker().isVisible()){
19592 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19598 e.preventDefault();
19606 this.onTogglePeriod();
19609 this.onIncrementMinutes();
19612 this.onDecrementMinutes();
19621 onClick: function(e) {
19622 e.stopPropagation();
19623 e.preventDefault();
19626 picker : function()
19628 return this.el.select('.datepicker', true).first();
19631 fillTime: function()
19633 var time = this.pop.select('tbody', true).first();
19635 time.dom.innerHTML = '';
19650 cls: 'hours-up glyphicon glyphicon-chevron-up'
19670 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19691 cls: 'timepicker-hour',
19706 cls: 'timepicker-minute',
19721 cls: 'btn btn-primary period',
19743 cls: 'hours-down glyphicon glyphicon-chevron-down'
19763 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19781 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19788 var hours = this.time.getHours();
19789 var minutes = this.time.getMinutes();
19802 hours = hours - 12;
19806 hours = '0' + hours;
19810 minutes = '0' + minutes;
19813 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19814 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19815 this.pop.select('button', true).first().dom.innerHTML = period;
19821 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19823 var cls = ['bottom'];
19825 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19832 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19837 this.picker().addClass(cls.join('-'));
19841 Roo.each(cls, function(c){
19843 _this.picker().setTop(_this.inputEl().getHeight());
19847 _this.picker().setTop(0 - _this.picker().getHeight());
19852 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19856 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19863 onFocus : function()
19865 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19869 onBlur : function()
19871 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19877 this.picker().show();
19882 this.fireEvent('show', this, this.date);
19887 this.picker().hide();
19890 this.fireEvent('hide', this, this.date);
19893 setTime : function()
19896 this.setValue(this.time.format(this.format));
19898 this.fireEvent('select', this, this.date);
19903 onMousedown: function(e){
19904 e.stopPropagation();
19905 e.preventDefault();
19908 onIncrementHours: function()
19910 Roo.log('onIncrementHours');
19911 this.time = this.time.add(Date.HOUR, 1);
19916 onDecrementHours: function()
19918 Roo.log('onDecrementHours');
19919 this.time = this.time.add(Date.HOUR, -1);
19923 onIncrementMinutes: function()
19925 Roo.log('onIncrementMinutes');
19926 this.time = this.time.add(Date.MINUTE, 1);
19930 onDecrementMinutes: function()
19932 Roo.log('onDecrementMinutes');
19933 this.time = this.time.add(Date.MINUTE, -1);
19937 onTogglePeriod: function()
19939 Roo.log('onTogglePeriod');
19940 this.time = this.time.add(Date.HOUR, 12);
19947 Roo.apply(Roo.bootstrap.TimeField, {
19977 cls: 'btn btn-info ok',
19989 Roo.apply(Roo.bootstrap.TimeField, {
19993 cls: 'datepicker dropdown-menu',
19997 cls: 'datepicker-time',
20001 cls: 'table-condensed',
20003 Roo.bootstrap.TimeField.content,
20004 Roo.bootstrap.TimeField.footer
20023 * @class Roo.bootstrap.MonthField
20024 * @extends Roo.bootstrap.Input
20025 * Bootstrap MonthField class
20027 * @cfg {String} language default en
20030 * Create a new MonthField
20031 * @param {Object} config The config object
20034 Roo.bootstrap.MonthField = function(config){
20035 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20040 * Fires when this field show.
20041 * @param {Roo.bootstrap.MonthField} this
20042 * @param {Mixed} date The date value
20047 * Fires when this field hide.
20048 * @param {Roo.bootstrap.MonthField} this
20049 * @param {Mixed} date The date value
20054 * Fires when select a date.
20055 * @param {Roo.bootstrap.MonthField} this
20056 * @param {String} oldvalue The old value
20057 * @param {String} newvalue The new value
20063 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20065 onRender: function(ct, position)
20068 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20070 this.language = this.language || 'en';
20071 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20072 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20074 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20075 this.isInline = false;
20076 this.isInput = true;
20077 this.component = this.el.select('.add-on', true).first() || false;
20078 this.component = (this.component && this.component.length === 0) ? false : this.component;
20079 this.hasInput = this.component && this.inputEL().length;
20081 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20083 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20085 this.picker().on('mousedown', this.onMousedown, this);
20086 this.picker().on('click', this.onClick, this);
20088 this.picker().addClass('datepicker-dropdown');
20090 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20091 v.setStyle('width', '189px');
20098 if(this.isInline) {
20104 setValue: function(v, suppressEvent)
20106 var o = this.getValue();
20108 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20112 if(suppressEvent !== true){
20113 this.fireEvent('select', this, o, v);
20118 getValue: function()
20123 onClick: function(e)
20125 e.stopPropagation();
20126 e.preventDefault();
20128 var target = e.getTarget();
20130 if(target.nodeName.toLowerCase() === 'i'){
20131 target = Roo.get(target).dom.parentNode;
20134 var nodeName = target.nodeName;
20135 var className = target.className;
20136 var html = target.innerHTML;
20138 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20142 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20144 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20150 picker : function()
20152 return this.pickerEl;
20155 fillMonths: function()
20158 var months = this.picker().select('>.datepicker-months td', true).first();
20160 months.dom.innerHTML = '';
20166 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20169 months.createChild(month);
20178 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20179 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20182 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20183 e.removeClass('active');
20185 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20186 e.addClass('active');
20193 if(this.isInline) {
20197 this.picker().removeClass(['bottom', 'top']);
20199 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20201 * place to the top of element!
20205 this.picker().addClass('top');
20206 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20211 this.picker().addClass('bottom');
20213 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20216 onFocus : function()
20218 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20222 onBlur : function()
20224 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20226 var d = this.inputEl().getValue();
20235 this.picker().show();
20236 this.picker().select('>.datepicker-months', true).first().show();
20240 this.fireEvent('show', this, this.date);
20245 if(this.isInline) {
20248 this.picker().hide();
20249 this.fireEvent('hide', this, this.date);
20253 onMousedown: function(e)
20255 e.stopPropagation();
20256 e.preventDefault();
20261 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20265 fireKey: function(e)
20267 if (!this.picker().isVisible()){
20268 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20279 e.preventDefault();
20283 dir = e.keyCode == 37 ? -1 : 1;
20285 this.vIndex = this.vIndex + dir;
20287 if(this.vIndex < 0){
20291 if(this.vIndex > 11){
20295 if(isNaN(this.vIndex)){
20299 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20305 dir = e.keyCode == 38 ? -1 : 1;
20307 this.vIndex = this.vIndex + dir * 4;
20309 if(this.vIndex < 0){
20313 if(this.vIndex > 11){
20317 if(isNaN(this.vIndex)){
20321 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20326 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20327 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20331 e.preventDefault();
20334 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20335 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20351 this.picker().remove();
20356 Roo.apply(Roo.bootstrap.MonthField, {
20375 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20376 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20381 Roo.apply(Roo.bootstrap.MonthField, {
20385 cls: 'datepicker dropdown-menu roo-dynamic',
20389 cls: 'datepicker-months',
20393 cls: 'table-condensed',
20395 Roo.bootstrap.DateField.content
20415 * @class Roo.bootstrap.CheckBox
20416 * @extends Roo.bootstrap.Input
20417 * Bootstrap CheckBox class
20419 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20420 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20421 * @cfg {String} boxLabel The text that appears beside the checkbox
20422 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20423 * @cfg {Boolean} checked initnal the element
20424 * @cfg {Boolean} inline inline the element (default false)
20425 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20426 * @cfg {String} tooltip label tooltip
20429 * Create a new CheckBox
20430 * @param {Object} config The config object
20433 Roo.bootstrap.CheckBox = function(config){
20434 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20439 * Fires when the element is checked or unchecked.
20440 * @param {Roo.bootstrap.CheckBox} this This input
20441 * @param {Boolean} checked The new checked value
20446 * Fires when the element is click.
20447 * @param {Roo.bootstrap.CheckBox} this This input
20454 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20456 inputType: 'checkbox',
20465 getAutoCreate : function()
20467 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20473 cfg.cls = 'form-group ' + this.inputType; //input-group
20476 cfg.cls += ' ' + this.inputType + '-inline';
20482 type : this.inputType,
20483 value : this.inputValue,
20484 cls : 'roo-' + this.inputType, //'form-box',
20485 placeholder : this.placeholder || ''
20489 if(this.inputType != 'radio'){
20493 cls : 'roo-hidden-value',
20494 value : this.checked ? this.inputValue : this.valueOff
20499 if (this.weight) { // Validity check?
20500 cfg.cls += " " + this.inputType + "-" + this.weight;
20503 if (this.disabled) {
20504 input.disabled=true;
20508 input.checked = this.checked;
20513 input.name = this.name;
20515 if(this.inputType != 'radio'){
20516 hidden.name = this.name;
20517 input.name = '_hidden_' + this.name;
20522 input.cls += ' input-' + this.size;
20527 ['xs','sm','md','lg'].map(function(size){
20528 if (settings[size]) {
20529 cfg.cls += ' col-' + size + '-' + settings[size];
20533 var inputblock = input;
20535 if (this.before || this.after) {
20538 cls : 'input-group',
20543 inputblock.cn.push({
20545 cls : 'input-group-addon',
20550 inputblock.cn.push(input);
20552 if(this.inputType != 'radio'){
20553 inputblock.cn.push(hidden);
20557 inputblock.cn.push({
20559 cls : 'input-group-addon',
20566 if (align ==='left' && this.fieldLabel.length) {
20567 // Roo.log("left and has label");
20572 cls : 'control-label',
20573 html : this.fieldLabel
20583 if(this.labelWidth > 12){
20584 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20587 if(this.labelWidth < 13 && this.labelmd == 0){
20588 this.labelmd = this.labelWidth;
20591 if(this.labellg > 0){
20592 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20593 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20596 if(this.labelmd > 0){
20597 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20598 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20601 if(this.labelsm > 0){
20602 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20603 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20606 if(this.labelxs > 0){
20607 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20608 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20611 } else if ( this.fieldLabel.length) {
20612 // Roo.log(" label");
20616 tag: this.boxLabel ? 'span' : 'label',
20618 cls: 'control-label box-input-label',
20619 //cls : 'input-group-addon',
20620 html : this.fieldLabel
20629 // Roo.log(" no label && no align");
20630 cfg.cn = [ inputblock ] ;
20636 var boxLabelCfg = {
20638 //'for': id, // box label is handled by onclick - so no for...
20640 html: this.boxLabel
20644 boxLabelCfg.tooltip = this.tooltip;
20647 cfg.cn.push(boxLabelCfg);
20650 if(this.inputType != 'radio'){
20651 cfg.cn.push(hidden);
20659 * return the real input element.
20661 inputEl: function ()
20663 return this.el.select('input.roo-' + this.inputType,true).first();
20665 hiddenEl: function ()
20667 return this.el.select('input.roo-hidden-value',true).first();
20670 labelEl: function()
20672 return this.el.select('label.control-label',true).first();
20674 /* depricated... */
20678 return this.labelEl();
20681 boxLabelEl: function()
20683 return this.el.select('label.box-label',true).first();
20686 initEvents : function()
20688 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20690 this.inputEl().on('click', this.onClick, this);
20692 if (this.boxLabel) {
20693 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20696 this.startValue = this.getValue();
20699 Roo.bootstrap.CheckBox.register(this);
20703 onClick : function(e)
20705 if(this.fireEvent('click', this, e) !== false){
20706 this.setChecked(!this.checked);
20711 setChecked : function(state,suppressEvent)
20713 this.startValue = this.getValue();
20715 if(this.inputType == 'radio'){
20717 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20718 e.dom.checked = false;
20721 this.inputEl().dom.checked = true;
20723 this.inputEl().dom.value = this.inputValue;
20725 if(suppressEvent !== true){
20726 this.fireEvent('check', this, true);
20734 this.checked = state;
20736 this.inputEl().dom.checked = state;
20739 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20741 if(suppressEvent !== true){
20742 this.fireEvent('check', this, state);
20748 getValue : function()
20750 if(this.inputType == 'radio'){
20751 return this.getGroupValue();
20754 return this.hiddenEl().dom.value;
20758 getGroupValue : function()
20760 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20764 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20767 setValue : function(v,suppressEvent)
20769 if(this.inputType == 'radio'){
20770 this.setGroupValue(v, suppressEvent);
20774 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20779 setGroupValue : function(v, suppressEvent)
20781 this.startValue = this.getValue();
20783 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20784 e.dom.checked = false;
20786 if(e.dom.value == v){
20787 e.dom.checked = true;
20791 if(suppressEvent !== true){
20792 this.fireEvent('check', this, true);
20800 validate : function()
20802 if(this.getVisibilityEl().hasClass('hidden')){
20808 (this.inputType == 'radio' && this.validateRadio()) ||
20809 (this.inputType == 'checkbox' && this.validateCheckbox())
20815 this.markInvalid();
20819 validateRadio : function()
20821 if(this.getVisibilityEl().hasClass('hidden')){
20825 if(this.allowBlank){
20831 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20832 if(!e.dom.checked){
20844 validateCheckbox : function()
20847 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20848 //return (this.getValue() == this.inputValue) ? true : false;
20851 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20859 for(var i in group){
20860 if(group[i].el.isVisible(true)){
20868 for(var i in group){
20873 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20880 * Mark this field as valid
20882 markValid : function()
20886 this.fireEvent('valid', this);
20888 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20891 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20898 if(this.inputType == 'radio'){
20899 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20900 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20901 e.findParent('.form-group', false, true).addClass(_this.validClass);
20908 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20909 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20913 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20919 for(var i in group){
20920 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20921 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20926 * Mark this field as invalid
20927 * @param {String} msg The validation message
20929 markInvalid : function(msg)
20931 if(this.allowBlank){
20937 this.fireEvent('invalid', this, msg);
20939 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20942 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20946 label.markInvalid();
20949 if(this.inputType == 'radio'){
20950 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20951 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20952 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20959 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20960 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20964 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20970 for(var i in group){
20971 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20972 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20977 clearInvalid : function()
20979 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20981 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20983 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20985 if (label && label.iconEl) {
20986 label.iconEl.removeClass(label.validClass);
20987 label.iconEl.removeClass(label.invalidClass);
20991 disable : function()
20993 if(this.inputType != 'radio'){
20994 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21001 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21002 _this.getActionEl().addClass(this.disabledClass);
21003 e.dom.disabled = true;
21007 this.disabled = true;
21008 this.fireEvent("disable", this);
21012 enable : function()
21014 if(this.inputType != 'radio'){
21015 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21022 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21023 _this.getActionEl().removeClass(this.disabledClass);
21024 e.dom.disabled = false;
21028 this.disabled = false;
21029 this.fireEvent("enable", this);
21033 setBoxLabel : function(v)
21038 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21044 Roo.apply(Roo.bootstrap.CheckBox, {
21049 * register a CheckBox Group
21050 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21052 register : function(checkbox)
21054 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21055 this.groups[checkbox.groupId] = {};
21058 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21062 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21066 * fetch a CheckBox Group based on the group ID
21067 * @param {string} the group ID
21068 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21070 get: function(groupId) {
21071 if (typeof(this.groups[groupId]) == 'undefined') {
21075 return this.groups[groupId] ;
21088 * @class Roo.bootstrap.Radio
21089 * @extends Roo.bootstrap.Component
21090 * Bootstrap Radio class
21091 * @cfg {String} boxLabel - the label associated
21092 * @cfg {String} value - the value of radio
21095 * Create a new Radio
21096 * @param {Object} config The config object
21098 Roo.bootstrap.Radio = function(config){
21099 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21103 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21109 getAutoCreate : function()
21113 cls : 'form-group radio',
21118 html : this.boxLabel
21126 initEvents : function()
21128 this.parent().register(this);
21130 this.el.on('click', this.onClick, this);
21134 onClick : function(e)
21136 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21137 this.setChecked(true);
21141 setChecked : function(state, suppressEvent)
21143 this.parent().setValue(this.value, suppressEvent);
21147 setBoxLabel : function(v)
21152 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21167 * @class Roo.bootstrap.SecurePass
21168 * @extends Roo.bootstrap.Input
21169 * Bootstrap SecurePass class
21173 * Create a new SecurePass
21174 * @param {Object} config The config object
21177 Roo.bootstrap.SecurePass = function (config) {
21178 // these go here, so the translation tool can replace them..
21180 PwdEmpty: "Please type a password, and then retype it to confirm.",
21181 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21182 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21183 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21184 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21185 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21186 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21187 TooWeak: "Your password is Too Weak."
21189 this.meterLabel = "Password strength:";
21190 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21191 this.meterClass = [
21192 "roo-password-meter-tooweak",
21193 "roo-password-meter-weak",
21194 "roo-password-meter-medium",
21195 "roo-password-meter-strong",
21196 "roo-password-meter-grey"
21201 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21204 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21206 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21208 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21209 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21210 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21211 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21212 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21213 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21214 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21224 * @cfg {String/Object} Label for the strength meter (defaults to
21225 * 'Password strength:')
21230 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21231 * ['Weak', 'Medium', 'Strong'])
21234 pwdStrengths: false,
21247 initEvents: function ()
21249 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21251 if (this.el.is('input[type=password]') && Roo.isSafari) {
21252 this.el.on('keydown', this.SafariOnKeyDown, this);
21255 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21258 onRender: function (ct, position)
21260 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21261 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21262 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21264 this.trigger.createChild({
21269 cls: 'roo-password-meter-grey col-xs-12',
21272 //width: this.meterWidth + 'px'
21276 cls: 'roo-password-meter-text'
21282 if (this.hideTrigger) {
21283 this.trigger.setDisplayed(false);
21285 this.setSize(this.width || '', this.height || '');
21288 onDestroy: function ()
21290 if (this.trigger) {
21291 this.trigger.removeAllListeners();
21292 this.trigger.remove();
21295 this.wrap.remove();
21297 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21300 checkStrength: function ()
21302 var pwd = this.inputEl().getValue();
21303 if (pwd == this._lastPwd) {
21308 if (this.ClientSideStrongPassword(pwd)) {
21310 } else if (this.ClientSideMediumPassword(pwd)) {
21312 } else if (this.ClientSideWeakPassword(pwd)) {
21318 Roo.log('strength1: ' + strength);
21320 //var pm = this.trigger.child('div/div/div').dom;
21321 var pm = this.trigger.child('div/div');
21322 pm.removeClass(this.meterClass);
21323 pm.addClass(this.meterClass[strength]);
21326 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21328 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21330 this._lastPwd = pwd;
21334 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21336 this._lastPwd = '';
21338 var pm = this.trigger.child('div/div');
21339 pm.removeClass(this.meterClass);
21340 pm.addClass('roo-password-meter-grey');
21343 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21346 this.inputEl().dom.type='password';
21349 validateValue: function (value)
21352 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21355 if (value.length == 0) {
21356 if (this.allowBlank) {
21357 this.clearInvalid();
21361 this.markInvalid(this.errors.PwdEmpty);
21362 this.errorMsg = this.errors.PwdEmpty;
21370 if ('[\x21-\x7e]*'.match(value)) {
21371 this.markInvalid(this.errors.PwdBadChar);
21372 this.errorMsg = this.errors.PwdBadChar;
21375 if (value.length < 6) {
21376 this.markInvalid(this.errors.PwdShort);
21377 this.errorMsg = this.errors.PwdShort;
21380 if (value.length > 16) {
21381 this.markInvalid(this.errors.PwdLong);
21382 this.errorMsg = this.errors.PwdLong;
21386 if (this.ClientSideStrongPassword(value)) {
21388 } else if (this.ClientSideMediumPassword(value)) {
21390 } else if (this.ClientSideWeakPassword(value)) {
21397 if (strength < 2) {
21398 //this.markInvalid(this.errors.TooWeak);
21399 this.errorMsg = this.errors.TooWeak;
21404 console.log('strength2: ' + strength);
21406 //var pm = this.trigger.child('div/div/div').dom;
21408 var pm = this.trigger.child('div/div');
21409 pm.removeClass(this.meterClass);
21410 pm.addClass(this.meterClass[strength]);
21412 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21414 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21416 this.errorMsg = '';
21420 CharacterSetChecks: function (type)
21423 this.fResult = false;
21426 isctype: function (character, type)
21429 case this.kCapitalLetter:
21430 if (character >= 'A' && character <= 'Z') {
21435 case this.kSmallLetter:
21436 if (character >= 'a' && character <= 'z') {
21442 if (character >= '0' && character <= '9') {
21447 case this.kPunctuation:
21448 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21459 IsLongEnough: function (pwd, size)
21461 return !(pwd == null || isNaN(size) || pwd.length < size);
21464 SpansEnoughCharacterSets: function (word, nb)
21466 if (!this.IsLongEnough(word, nb))
21471 var characterSetChecks = new Array(
21472 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21473 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21476 for (var index = 0; index < word.length; ++index) {
21477 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21478 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21479 characterSetChecks[nCharSet].fResult = true;
21486 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21487 if (characterSetChecks[nCharSet].fResult) {
21492 if (nCharSets < nb) {
21498 ClientSideStrongPassword: function (pwd)
21500 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21503 ClientSideMediumPassword: function (pwd)
21505 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21508 ClientSideWeakPassword: function (pwd)
21510 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21513 })//<script type="text/javascript">
21516 * Based Ext JS Library 1.1.1
21517 * Copyright(c) 2006-2007, Ext JS, LLC.
21523 * @class Roo.HtmlEditorCore
21524 * @extends Roo.Component
21525 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21527 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21530 Roo.HtmlEditorCore = function(config){
21533 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21538 * @event initialize
21539 * Fires when the editor is fully initialized (including the iframe)
21540 * @param {Roo.HtmlEditorCore} this
21545 * Fires when the editor is first receives the focus. Any insertion must wait
21546 * until after this event.
21547 * @param {Roo.HtmlEditorCore} this
21551 * @event beforesync
21552 * Fires before the textarea is updated with content from the editor iframe. Return false
21553 * to cancel the sync.
21554 * @param {Roo.HtmlEditorCore} this
21555 * @param {String} html
21559 * @event beforepush
21560 * Fires before the iframe editor is updated with content from the textarea. Return false
21561 * to cancel the push.
21562 * @param {Roo.HtmlEditorCore} this
21563 * @param {String} html
21568 * Fires when the textarea is updated with content from the editor iframe.
21569 * @param {Roo.HtmlEditorCore} this
21570 * @param {String} html
21575 * Fires when the iframe editor is updated with content from the textarea.
21576 * @param {Roo.HtmlEditorCore} this
21577 * @param {String} html
21582 * @event editorevent
21583 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21584 * @param {Roo.HtmlEditorCore} this
21590 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21592 // defaults : white / black...
21593 this.applyBlacklists();
21600 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21604 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21610 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21615 * @cfg {Number} height (in pixels)
21619 * @cfg {Number} width (in pixels)
21624 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21627 stylesheets: false,
21632 // private properties
21633 validationEvent : false,
21635 initialized : false,
21637 sourceEditMode : false,
21638 onFocus : Roo.emptyFn,
21640 hideMode:'offsets',
21644 // blacklist + whitelisted elements..
21651 * Protected method that will not generally be called directly. It
21652 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21653 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21655 getDocMarkup : function(){
21659 // inherit styels from page...??
21660 if (this.stylesheets === false) {
21662 Roo.get(document.head).select('style').each(function(node) {
21663 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21666 Roo.get(document.head).select('link').each(function(node) {
21667 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21670 } else if (!this.stylesheets.length) {
21672 st = '<style type="text/css">' +
21673 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21676 st = '<style type="text/css">' +
21681 st += '<style type="text/css">' +
21682 'IMG { cursor: pointer } ' +
21685 var cls = 'roo-htmleditor-body';
21687 if(this.bodyCls.length){
21688 cls += ' ' + this.bodyCls;
21691 return '<html><head>' + st +
21692 //<style type="text/css">' +
21693 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21695 ' </head><body class="' + cls + '"></body></html>';
21699 onRender : function(ct, position)
21702 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21703 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21706 this.el.dom.style.border = '0 none';
21707 this.el.dom.setAttribute('tabIndex', -1);
21708 this.el.addClass('x-hidden hide');
21712 if(Roo.isIE){ // fix IE 1px bogus margin
21713 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21717 this.frameId = Roo.id();
21721 var iframe = this.owner.wrap.createChild({
21723 cls: 'form-control', // bootstrap..
21725 name: this.frameId,
21726 frameBorder : 'no',
21727 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21732 this.iframe = iframe.dom;
21734 this.assignDocWin();
21736 this.doc.designMode = 'on';
21739 this.doc.write(this.getDocMarkup());
21743 var task = { // must defer to wait for browser to be ready
21745 //console.log("run task?" + this.doc.readyState);
21746 this.assignDocWin();
21747 if(this.doc.body || this.doc.readyState == 'complete'){
21749 this.doc.designMode="on";
21753 Roo.TaskMgr.stop(task);
21754 this.initEditor.defer(10, this);
21761 Roo.TaskMgr.start(task);
21766 onResize : function(w, h)
21768 Roo.log('resize: ' +w + ',' + h );
21769 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21773 if(typeof w == 'number'){
21775 this.iframe.style.width = w + 'px';
21777 if(typeof h == 'number'){
21779 this.iframe.style.height = h + 'px';
21781 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21788 * Toggles the editor between standard and source edit mode.
21789 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21791 toggleSourceEdit : function(sourceEditMode){
21793 this.sourceEditMode = sourceEditMode === true;
21795 if(this.sourceEditMode){
21797 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21800 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21801 //this.iframe.className = '';
21804 //this.setSize(this.owner.wrap.getSize());
21805 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21812 * Protected method that will not generally be called directly. If you need/want
21813 * custom HTML cleanup, this is the method you should override.
21814 * @param {String} html The HTML to be cleaned
21815 * return {String} The cleaned HTML
21817 cleanHtml : function(html){
21818 html = String(html);
21819 if(html.length > 5){
21820 if(Roo.isSafari){ // strip safari nonsense
21821 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21824 if(html == ' '){
21831 * HTML Editor -> Textarea
21832 * Protected method that will not generally be called directly. Syncs the contents
21833 * of the editor iframe with the textarea.
21835 syncValue : function(){
21836 if(this.initialized){
21837 var bd = (this.doc.body || this.doc.documentElement);
21838 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21839 var html = bd.innerHTML;
21841 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21842 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21844 html = '<div style="'+m[0]+'">' + html + '</div>';
21847 html = this.cleanHtml(html);
21848 // fix up the special chars.. normaly like back quotes in word...
21849 // however we do not want to do this with chinese..
21850 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21851 var cc = b.charCodeAt();
21853 (cc >= 0x4E00 && cc < 0xA000 ) ||
21854 (cc >= 0x3400 && cc < 0x4E00 ) ||
21855 (cc >= 0xf900 && cc < 0xfb00 )
21861 if(this.owner.fireEvent('beforesync', this, html) !== false){
21862 this.el.dom.value = html;
21863 this.owner.fireEvent('sync', this, html);
21869 * Protected method that will not generally be called directly. Pushes the value of the textarea
21870 * into the iframe editor.
21872 pushValue : function(){
21873 if(this.initialized){
21874 var v = this.el.dom.value.trim();
21876 // if(v.length < 1){
21880 if(this.owner.fireEvent('beforepush', this, v) !== false){
21881 var d = (this.doc.body || this.doc.documentElement);
21883 this.cleanUpPaste();
21884 this.el.dom.value = d.innerHTML;
21885 this.owner.fireEvent('push', this, v);
21891 deferFocus : function(){
21892 this.focus.defer(10, this);
21896 focus : function(){
21897 if(this.win && !this.sourceEditMode){
21904 assignDocWin: function()
21906 var iframe = this.iframe;
21909 this.doc = iframe.contentWindow.document;
21910 this.win = iframe.contentWindow;
21912 // if (!Roo.get(this.frameId)) {
21915 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21916 // this.win = Roo.get(this.frameId).dom.contentWindow;
21918 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21922 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21923 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21928 initEditor : function(){
21929 //console.log("INIT EDITOR");
21930 this.assignDocWin();
21934 this.doc.designMode="on";
21936 this.doc.write(this.getDocMarkup());
21939 var dbody = (this.doc.body || this.doc.documentElement);
21940 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21941 // this copies styles from the containing element into thsi one..
21942 // not sure why we need all of this..
21943 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21945 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21946 //ss['background-attachment'] = 'fixed'; // w3c
21947 dbody.bgProperties = 'fixed'; // ie
21948 //Roo.DomHelper.applyStyles(dbody, ss);
21949 Roo.EventManager.on(this.doc, {
21950 //'mousedown': this.onEditorEvent,
21951 'mouseup': this.onEditorEvent,
21952 'dblclick': this.onEditorEvent,
21953 'click': this.onEditorEvent,
21954 'keyup': this.onEditorEvent,
21959 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21961 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21962 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21964 this.initialized = true;
21966 this.owner.fireEvent('initialize', this);
21971 onDestroy : function(){
21977 //for (var i =0; i < this.toolbars.length;i++) {
21978 // // fixme - ask toolbars for heights?
21979 // this.toolbars[i].onDestroy();
21982 //this.wrap.dom.innerHTML = '';
21983 //this.wrap.remove();
21988 onFirstFocus : function(){
21990 this.assignDocWin();
21993 this.activated = true;
21996 if(Roo.isGecko){ // prevent silly gecko errors
21998 var s = this.win.getSelection();
21999 if(!s.focusNode || s.focusNode.nodeType != 3){
22000 var r = s.getRangeAt(0);
22001 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22006 this.execCmd('useCSS', true);
22007 this.execCmd('styleWithCSS', false);
22010 this.owner.fireEvent('activate', this);
22014 adjustFont: function(btn){
22015 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22016 //if(Roo.isSafari){ // safari
22019 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22020 if(Roo.isSafari){ // safari
22021 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22022 v = (v < 10) ? 10 : v;
22023 v = (v > 48) ? 48 : v;
22024 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22029 v = Math.max(1, v+adjust);
22031 this.execCmd('FontSize', v );
22034 onEditorEvent : function(e)
22036 this.owner.fireEvent('editorevent', this, e);
22037 // this.updateToolbar();
22038 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22041 insertTag : function(tg)
22043 // could be a bit smarter... -> wrap the current selected tRoo..
22044 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22046 range = this.createRange(this.getSelection());
22047 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22048 wrappingNode.appendChild(range.extractContents());
22049 range.insertNode(wrappingNode);
22056 this.execCmd("formatblock", tg);
22060 insertText : function(txt)
22064 var range = this.createRange();
22065 range.deleteContents();
22066 //alert(Sender.getAttribute('label'));
22068 range.insertNode(this.doc.createTextNode(txt));
22074 * Executes a Midas editor command on the editor document and performs necessary focus and
22075 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22076 * @param {String} cmd The Midas command
22077 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22079 relayCmd : function(cmd, value){
22081 this.execCmd(cmd, value);
22082 this.owner.fireEvent('editorevent', this);
22083 //this.updateToolbar();
22084 this.owner.deferFocus();
22088 * Executes a Midas editor command directly on the editor document.
22089 * For visual commands, you should use {@link #relayCmd} instead.
22090 * <b>This should only be called after the editor is initialized.</b>
22091 * @param {String} cmd The Midas command
22092 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22094 execCmd : function(cmd, value){
22095 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22102 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22104 * @param {String} text | dom node..
22106 insertAtCursor : function(text)
22109 if(!this.activated){
22115 var r = this.doc.selection.createRange();
22126 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22130 // from jquery ui (MIT licenced)
22132 var win = this.win;
22134 if (win.getSelection && win.getSelection().getRangeAt) {
22135 range = win.getSelection().getRangeAt(0);
22136 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22137 range.insertNode(node);
22138 } else if (win.document.selection && win.document.selection.createRange) {
22139 // no firefox support
22140 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22141 win.document.selection.createRange().pasteHTML(txt);
22143 // no firefox support
22144 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22145 this.execCmd('InsertHTML', txt);
22154 mozKeyPress : function(e){
22156 var c = e.getCharCode(), cmd;
22159 c = String.fromCharCode(c).toLowerCase();
22173 this.cleanUpPaste.defer(100, this);
22181 e.preventDefault();
22189 fixKeys : function(){ // load time branching for fastest keydown performance
22191 return function(e){
22192 var k = e.getKey(), r;
22195 r = this.doc.selection.createRange();
22198 r.pasteHTML('    ');
22205 r = this.doc.selection.createRange();
22207 var target = r.parentElement();
22208 if(!target || target.tagName.toLowerCase() != 'li'){
22210 r.pasteHTML('<br />');
22216 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22217 this.cleanUpPaste.defer(100, this);
22223 }else if(Roo.isOpera){
22224 return function(e){
22225 var k = e.getKey();
22229 this.execCmd('InsertHTML','    ');
22232 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22233 this.cleanUpPaste.defer(100, this);
22238 }else if(Roo.isSafari){
22239 return function(e){
22240 var k = e.getKey();
22244 this.execCmd('InsertText','\t');
22248 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22249 this.cleanUpPaste.defer(100, this);
22257 getAllAncestors: function()
22259 var p = this.getSelectedNode();
22262 a.push(p); // push blank onto stack..
22263 p = this.getParentElement();
22267 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22271 a.push(this.doc.body);
22275 lastSelNode : false,
22278 getSelection : function()
22280 this.assignDocWin();
22281 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22284 getSelectedNode: function()
22286 // this may only work on Gecko!!!
22288 // should we cache this!!!!
22293 var range = this.createRange(this.getSelection()).cloneRange();
22296 var parent = range.parentElement();
22298 var testRange = range.duplicate();
22299 testRange.moveToElementText(parent);
22300 if (testRange.inRange(range)) {
22303 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22306 parent = parent.parentElement;
22311 // is ancestor a text element.
22312 var ac = range.commonAncestorContainer;
22313 if (ac.nodeType == 3) {
22314 ac = ac.parentNode;
22317 var ar = ac.childNodes;
22320 var other_nodes = [];
22321 var has_other_nodes = false;
22322 for (var i=0;i<ar.length;i++) {
22323 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22326 // fullly contained node.
22328 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22333 // probably selected..
22334 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22335 other_nodes.push(ar[i]);
22339 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22344 has_other_nodes = true;
22346 if (!nodes.length && other_nodes.length) {
22347 nodes= other_nodes;
22349 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22355 createRange: function(sel)
22357 // this has strange effects when using with
22358 // top toolbar - not sure if it's a great idea.
22359 //this.editor.contentWindow.focus();
22360 if (typeof sel != "undefined") {
22362 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22364 return this.doc.createRange();
22367 return this.doc.createRange();
22370 getParentElement: function()
22373 this.assignDocWin();
22374 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22376 var range = this.createRange(sel);
22379 var p = range.commonAncestorContainer;
22380 while (p.nodeType == 3) { // text node
22391 * Range intersection.. the hard stuff...
22395 * [ -- selected range --- ]
22399 * if end is before start or hits it. fail.
22400 * if start is after end or hits it fail.
22402 * if either hits (but other is outside. - then it's not
22408 // @see http://www.thismuchiknow.co.uk/?p=64.
22409 rangeIntersectsNode : function(range, node)
22411 var nodeRange = node.ownerDocument.createRange();
22413 nodeRange.selectNode(node);
22415 nodeRange.selectNodeContents(node);
22418 var rangeStartRange = range.cloneRange();
22419 rangeStartRange.collapse(true);
22421 var rangeEndRange = range.cloneRange();
22422 rangeEndRange.collapse(false);
22424 var nodeStartRange = nodeRange.cloneRange();
22425 nodeStartRange.collapse(true);
22427 var nodeEndRange = nodeRange.cloneRange();
22428 nodeEndRange.collapse(false);
22430 return rangeStartRange.compareBoundaryPoints(
22431 Range.START_TO_START, nodeEndRange) == -1 &&
22432 rangeEndRange.compareBoundaryPoints(
22433 Range.START_TO_START, nodeStartRange) == 1;
22437 rangeCompareNode : function(range, node)
22439 var nodeRange = node.ownerDocument.createRange();
22441 nodeRange.selectNode(node);
22443 nodeRange.selectNodeContents(node);
22447 range.collapse(true);
22449 nodeRange.collapse(true);
22451 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22452 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22454 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22456 var nodeIsBefore = ss == 1;
22457 var nodeIsAfter = ee == -1;
22459 if (nodeIsBefore && nodeIsAfter) {
22462 if (!nodeIsBefore && nodeIsAfter) {
22463 return 1; //right trailed.
22466 if (nodeIsBefore && !nodeIsAfter) {
22467 return 2; // left trailed.
22473 // private? - in a new class?
22474 cleanUpPaste : function()
22476 // cleans up the whole document..
22477 Roo.log('cleanuppaste');
22479 this.cleanUpChildren(this.doc.body);
22480 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22481 if (clean != this.doc.body.innerHTML) {
22482 this.doc.body.innerHTML = clean;
22487 cleanWordChars : function(input) {// change the chars to hex code
22488 var he = Roo.HtmlEditorCore;
22490 var output = input;
22491 Roo.each(he.swapCodes, function(sw) {
22492 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22494 output = output.replace(swapper, sw[1]);
22501 cleanUpChildren : function (n)
22503 if (!n.childNodes.length) {
22506 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22507 this.cleanUpChild(n.childNodes[i]);
22514 cleanUpChild : function (node)
22517 //console.log(node);
22518 if (node.nodeName == "#text") {
22519 // clean up silly Windows -- stuff?
22522 if (node.nodeName == "#comment") {
22523 node.parentNode.removeChild(node);
22524 // clean up silly Windows -- stuff?
22527 var lcname = node.tagName.toLowerCase();
22528 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22529 // whitelist of tags..
22531 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22533 node.parentNode.removeChild(node);
22538 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22540 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22541 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22543 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22544 // remove_keep_children = true;
22547 if (remove_keep_children) {
22548 this.cleanUpChildren(node);
22549 // inserts everything just before this node...
22550 while (node.childNodes.length) {
22551 var cn = node.childNodes[0];
22552 node.removeChild(cn);
22553 node.parentNode.insertBefore(cn, node);
22555 node.parentNode.removeChild(node);
22559 if (!node.attributes || !node.attributes.length) {
22560 this.cleanUpChildren(node);
22564 function cleanAttr(n,v)
22567 if (v.match(/^\./) || v.match(/^\//)) {
22570 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22573 if (v.match(/^#/)) {
22576 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22577 node.removeAttribute(n);
22581 var cwhite = this.cwhite;
22582 var cblack = this.cblack;
22584 function cleanStyle(n,v)
22586 if (v.match(/expression/)) { //XSS?? should we even bother..
22587 node.removeAttribute(n);
22591 var parts = v.split(/;/);
22594 Roo.each(parts, function(p) {
22595 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22599 var l = p.split(':').shift().replace(/\s+/g,'');
22600 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22602 if ( cwhite.length && cblack.indexOf(l) > -1) {
22603 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22604 //node.removeAttribute(n);
22608 // only allow 'c whitelisted system attributes'
22609 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22610 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22611 //node.removeAttribute(n);
22621 if (clean.length) {
22622 node.setAttribute(n, clean.join(';'));
22624 node.removeAttribute(n);
22630 for (var i = node.attributes.length-1; i > -1 ; i--) {
22631 var a = node.attributes[i];
22634 if (a.name.toLowerCase().substr(0,2)=='on') {
22635 node.removeAttribute(a.name);
22638 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22639 node.removeAttribute(a.name);
22642 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22643 cleanAttr(a.name,a.value); // fixme..
22646 if (a.name == 'style') {
22647 cleanStyle(a.name,a.value);
22650 /// clean up MS crap..
22651 // tecnically this should be a list of valid class'es..
22654 if (a.name == 'class') {
22655 if (a.value.match(/^Mso/)) {
22656 node.className = '';
22659 if (a.value.match(/^body$/)) {
22660 node.className = '';
22671 this.cleanUpChildren(node);
22677 * Clean up MS wordisms...
22679 cleanWord : function(node)
22684 this.cleanWord(this.doc.body);
22687 if (node.nodeName == "#text") {
22688 // clean up silly Windows -- stuff?
22691 if (node.nodeName == "#comment") {
22692 node.parentNode.removeChild(node);
22693 // clean up silly Windows -- stuff?
22697 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22698 node.parentNode.removeChild(node);
22702 // remove - but keep children..
22703 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22704 while (node.childNodes.length) {
22705 var cn = node.childNodes[0];
22706 node.removeChild(cn);
22707 node.parentNode.insertBefore(cn, node);
22709 node.parentNode.removeChild(node);
22710 this.iterateChildren(node, this.cleanWord);
22714 if (node.className.length) {
22716 var cn = node.className.split(/\W+/);
22718 Roo.each(cn, function(cls) {
22719 if (cls.match(/Mso[a-zA-Z]+/)) {
22724 node.className = cna.length ? cna.join(' ') : '';
22726 node.removeAttribute("class");
22730 if (node.hasAttribute("lang")) {
22731 node.removeAttribute("lang");
22734 if (node.hasAttribute("style")) {
22736 var styles = node.getAttribute("style").split(";");
22738 Roo.each(styles, function(s) {
22739 if (!s.match(/:/)) {
22742 var kv = s.split(":");
22743 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22746 // what ever is left... we allow.
22749 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22750 if (!nstyle.length) {
22751 node.removeAttribute('style');
22754 this.iterateChildren(node, this.cleanWord);
22760 * iterateChildren of a Node, calling fn each time, using this as the scole..
22761 * @param {DomNode} node node to iterate children of.
22762 * @param {Function} fn method of this class to call on each item.
22764 iterateChildren : function(node, fn)
22766 if (!node.childNodes.length) {
22769 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22770 fn.call(this, node.childNodes[i])
22776 * cleanTableWidths.
22778 * Quite often pasting from word etc.. results in tables with column and widths.
22779 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22782 cleanTableWidths : function(node)
22787 this.cleanTableWidths(this.doc.body);
22792 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22795 Roo.log(node.tagName);
22796 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22797 this.iterateChildren(node, this.cleanTableWidths);
22800 if (node.hasAttribute('width')) {
22801 node.removeAttribute('width');
22805 if (node.hasAttribute("style")) {
22808 var styles = node.getAttribute("style").split(";");
22810 Roo.each(styles, function(s) {
22811 if (!s.match(/:/)) {
22814 var kv = s.split(":");
22815 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22818 // what ever is left... we allow.
22821 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22822 if (!nstyle.length) {
22823 node.removeAttribute('style');
22827 this.iterateChildren(node, this.cleanTableWidths);
22835 domToHTML : function(currentElement, depth, nopadtext) {
22837 depth = depth || 0;
22838 nopadtext = nopadtext || false;
22840 if (!currentElement) {
22841 return this.domToHTML(this.doc.body);
22844 //Roo.log(currentElement);
22846 var allText = false;
22847 var nodeName = currentElement.nodeName;
22848 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22850 if (nodeName == '#text') {
22852 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22857 if (nodeName != 'BODY') {
22860 // Prints the node tagName, such as <A>, <IMG>, etc
22863 for(i = 0; i < currentElement.attributes.length;i++) {
22865 var aname = currentElement.attributes.item(i).name;
22866 if (!currentElement.attributes.item(i).value.length) {
22869 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22872 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22881 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22884 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22889 // Traverse the tree
22891 var currentElementChild = currentElement.childNodes.item(i);
22892 var allText = true;
22893 var innerHTML = '';
22895 while (currentElementChild) {
22896 // Formatting code (indent the tree so it looks nice on the screen)
22897 var nopad = nopadtext;
22898 if (lastnode == 'SPAN') {
22902 if (currentElementChild.nodeName == '#text') {
22903 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22904 toadd = nopadtext ? toadd : toadd.trim();
22905 if (!nopad && toadd.length > 80) {
22906 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22908 innerHTML += toadd;
22911 currentElementChild = currentElement.childNodes.item(i);
22917 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22919 // Recursively traverse the tree structure of the child node
22920 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22921 lastnode = currentElementChild.nodeName;
22923 currentElementChild=currentElement.childNodes.item(i);
22929 // The remaining code is mostly for formatting the tree
22930 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22935 ret+= "</"+tagName+">";
22941 applyBlacklists : function()
22943 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22944 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22948 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22949 if (b.indexOf(tag) > -1) {
22952 this.white.push(tag);
22956 Roo.each(w, function(tag) {
22957 if (b.indexOf(tag) > -1) {
22960 if (this.white.indexOf(tag) > -1) {
22963 this.white.push(tag);
22968 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22969 if (w.indexOf(tag) > -1) {
22972 this.black.push(tag);
22976 Roo.each(b, function(tag) {
22977 if (w.indexOf(tag) > -1) {
22980 if (this.black.indexOf(tag) > -1) {
22983 this.black.push(tag);
22988 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22989 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22993 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22994 if (b.indexOf(tag) > -1) {
22997 this.cwhite.push(tag);
23001 Roo.each(w, function(tag) {
23002 if (b.indexOf(tag) > -1) {
23005 if (this.cwhite.indexOf(tag) > -1) {
23008 this.cwhite.push(tag);
23013 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23014 if (w.indexOf(tag) > -1) {
23017 this.cblack.push(tag);
23021 Roo.each(b, function(tag) {
23022 if (w.indexOf(tag) > -1) {
23025 if (this.cblack.indexOf(tag) > -1) {
23028 this.cblack.push(tag);
23033 setStylesheets : function(stylesheets)
23035 if(typeof(stylesheets) == 'string'){
23036 Roo.get(this.iframe.contentDocument.head).createChild({
23038 rel : 'stylesheet',
23047 Roo.each(stylesheets, function(s) {
23052 Roo.get(_this.iframe.contentDocument.head).createChild({
23054 rel : 'stylesheet',
23063 removeStylesheets : function()
23067 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23072 setStyle : function(style)
23074 Roo.get(this.iframe.contentDocument.head).createChild({
23083 // hide stuff that is not compatible
23097 * @event specialkey
23101 * @cfg {String} fieldClass @hide
23104 * @cfg {String} focusClass @hide
23107 * @cfg {String} autoCreate @hide
23110 * @cfg {String} inputType @hide
23113 * @cfg {String} invalidClass @hide
23116 * @cfg {String} invalidText @hide
23119 * @cfg {String} msgFx @hide
23122 * @cfg {String} validateOnBlur @hide
23126 Roo.HtmlEditorCore.white = [
23127 'area', 'br', 'img', 'input', 'hr', 'wbr',
23129 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23130 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23131 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23132 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23133 'table', 'ul', 'xmp',
23135 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23138 'dir', 'menu', 'ol', 'ul', 'dl',
23144 Roo.HtmlEditorCore.black = [
23145 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23147 'base', 'basefont', 'bgsound', 'blink', 'body',
23148 'frame', 'frameset', 'head', 'html', 'ilayer',
23149 'iframe', 'layer', 'link', 'meta', 'object',
23150 'script', 'style' ,'title', 'xml' // clean later..
23152 Roo.HtmlEditorCore.clean = [
23153 'script', 'style', 'title', 'xml'
23155 Roo.HtmlEditorCore.remove = [
23160 Roo.HtmlEditorCore.ablack = [
23164 Roo.HtmlEditorCore.aclean = [
23165 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23169 Roo.HtmlEditorCore.pwhite= [
23170 'http', 'https', 'mailto'
23173 // white listed style attributes.
23174 Roo.HtmlEditorCore.cwhite= [
23175 // 'text-align', /// default is to allow most things..
23181 // black listed style attributes.
23182 Roo.HtmlEditorCore.cblack= [
23183 // 'font-size' -- this can be set by the project
23187 Roo.HtmlEditorCore.swapCodes =[
23206 * @class Roo.bootstrap.HtmlEditor
23207 * @extends Roo.bootstrap.TextArea
23208 * Bootstrap HtmlEditor class
23211 * Create a new HtmlEditor
23212 * @param {Object} config The config object
23215 Roo.bootstrap.HtmlEditor = function(config){
23216 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23217 if (!this.toolbars) {
23218 this.toolbars = [];
23221 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23224 * @event initialize
23225 * Fires when the editor is fully initialized (including the iframe)
23226 * @param {HtmlEditor} this
23231 * Fires when the editor is first receives the focus. Any insertion must wait
23232 * until after this event.
23233 * @param {HtmlEditor} this
23237 * @event beforesync
23238 * Fires before the textarea is updated with content from the editor iframe. Return false
23239 * to cancel the sync.
23240 * @param {HtmlEditor} this
23241 * @param {String} html
23245 * @event beforepush
23246 * Fires before the iframe editor is updated with content from the textarea. Return false
23247 * to cancel the push.
23248 * @param {HtmlEditor} this
23249 * @param {String} html
23254 * Fires when the textarea is updated with content from the editor iframe.
23255 * @param {HtmlEditor} this
23256 * @param {String} html
23261 * Fires when the iframe editor is updated with content from the textarea.
23262 * @param {HtmlEditor} this
23263 * @param {String} html
23267 * @event editmodechange
23268 * Fires when the editor switches edit modes
23269 * @param {HtmlEditor} this
23270 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23272 editmodechange: true,
23274 * @event editorevent
23275 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23276 * @param {HtmlEditor} this
23280 * @event firstfocus
23281 * Fires when on first focus - needed by toolbars..
23282 * @param {HtmlEditor} this
23287 * Auto save the htmlEditor value as a file into Events
23288 * @param {HtmlEditor} this
23292 * @event savedpreview
23293 * preview the saved version of htmlEditor
23294 * @param {HtmlEditor} this
23301 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23305 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23310 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23315 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23320 * @cfg {Number} height (in pixels)
23324 * @cfg {Number} width (in pixels)
23329 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23332 stylesheets: false,
23337 // private properties
23338 validationEvent : false,
23340 initialized : false,
23343 onFocus : Roo.emptyFn,
23345 hideMode:'offsets',
23347 tbContainer : false,
23351 toolbarContainer :function() {
23352 return this.wrap.select('.x-html-editor-tb',true).first();
23356 * Protected method that will not generally be called directly. It
23357 * is called when the editor creates its toolbar. Override this method if you need to
23358 * add custom toolbar buttons.
23359 * @param {HtmlEditor} editor
23361 createToolbar : function(){
23362 Roo.log('renewing');
23363 Roo.log("create toolbars");
23365 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23366 this.toolbars[0].render(this.toolbarContainer());
23370 // if (!editor.toolbars || !editor.toolbars.length) {
23371 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23374 // for (var i =0 ; i < editor.toolbars.length;i++) {
23375 // editor.toolbars[i] = Roo.factory(
23376 // typeof(editor.toolbars[i]) == 'string' ?
23377 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23378 // Roo.bootstrap.HtmlEditor);
23379 // editor.toolbars[i].init(editor);
23385 onRender : function(ct, position)
23387 // Roo.log("Call onRender: " + this.xtype);
23389 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23391 this.wrap = this.inputEl().wrap({
23392 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23395 this.editorcore.onRender(ct, position);
23397 if (this.resizable) {
23398 this.resizeEl = new Roo.Resizable(this.wrap, {
23402 minHeight : this.height,
23403 height: this.height,
23404 handles : this.resizable,
23407 resize : function(r, w, h) {
23408 _t.onResize(w,h); // -something
23414 this.createToolbar(this);
23417 if(!this.width && this.resizable){
23418 this.setSize(this.wrap.getSize());
23420 if (this.resizeEl) {
23421 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23422 // should trigger onReize..
23428 onResize : function(w, h)
23430 Roo.log('resize: ' +w + ',' + h );
23431 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23435 if(this.inputEl() ){
23436 if(typeof w == 'number'){
23437 var aw = w - this.wrap.getFrameWidth('lr');
23438 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23441 if(typeof h == 'number'){
23442 var tbh = -11; // fixme it needs to tool bar size!
23443 for (var i =0; i < this.toolbars.length;i++) {
23444 // fixme - ask toolbars for heights?
23445 tbh += this.toolbars[i].el.getHeight();
23446 //if (this.toolbars[i].footer) {
23447 // tbh += this.toolbars[i].footer.el.getHeight();
23455 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23456 ah -= 5; // knock a few pixes off for look..
23457 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23461 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23462 this.editorcore.onResize(ew,eh);
23467 * Toggles the editor between standard and source edit mode.
23468 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23470 toggleSourceEdit : function(sourceEditMode)
23472 this.editorcore.toggleSourceEdit(sourceEditMode);
23474 if(this.editorcore.sourceEditMode){
23475 Roo.log('editor - showing textarea');
23478 // Roo.log(this.syncValue());
23480 this.inputEl().removeClass(['hide', 'x-hidden']);
23481 this.inputEl().dom.removeAttribute('tabIndex');
23482 this.inputEl().focus();
23484 Roo.log('editor - hiding textarea');
23486 // Roo.log(this.pushValue());
23489 this.inputEl().addClass(['hide', 'x-hidden']);
23490 this.inputEl().dom.setAttribute('tabIndex', -1);
23491 //this.deferFocus();
23494 if(this.resizable){
23495 this.setSize(this.wrap.getSize());
23498 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23501 // private (for BoxComponent)
23502 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23504 // private (for BoxComponent)
23505 getResizeEl : function(){
23509 // private (for BoxComponent)
23510 getPositionEl : function(){
23515 initEvents : function(){
23516 this.originalValue = this.getValue();
23520 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23523 // markInvalid : Roo.emptyFn,
23525 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23528 // clearInvalid : Roo.emptyFn,
23530 setValue : function(v){
23531 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23532 this.editorcore.pushValue();
23537 deferFocus : function(){
23538 this.focus.defer(10, this);
23542 focus : function(){
23543 this.editorcore.focus();
23549 onDestroy : function(){
23555 for (var i =0; i < this.toolbars.length;i++) {
23556 // fixme - ask toolbars for heights?
23557 this.toolbars[i].onDestroy();
23560 this.wrap.dom.innerHTML = '';
23561 this.wrap.remove();
23566 onFirstFocus : function(){
23567 //Roo.log("onFirstFocus");
23568 this.editorcore.onFirstFocus();
23569 for (var i =0; i < this.toolbars.length;i++) {
23570 this.toolbars[i].onFirstFocus();
23576 syncValue : function()
23578 this.editorcore.syncValue();
23581 pushValue : function()
23583 this.editorcore.pushValue();
23587 // hide stuff that is not compatible
23601 * @event specialkey
23605 * @cfg {String} fieldClass @hide
23608 * @cfg {String} focusClass @hide
23611 * @cfg {String} autoCreate @hide
23614 * @cfg {String} inputType @hide
23617 * @cfg {String} invalidClass @hide
23620 * @cfg {String} invalidText @hide
23623 * @cfg {String} msgFx @hide
23626 * @cfg {String} validateOnBlur @hide
23635 Roo.namespace('Roo.bootstrap.htmleditor');
23637 * @class Roo.bootstrap.HtmlEditorToolbar1
23642 new Roo.bootstrap.HtmlEditor({
23645 new Roo.bootstrap.HtmlEditorToolbar1({
23646 disable : { fonts: 1 , format: 1, ..., ... , ...],
23652 * @cfg {Object} disable List of elements to disable..
23653 * @cfg {Array} btns List of additional buttons.
23657 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23660 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23663 Roo.apply(this, config);
23665 // default disabled, based on 'good practice'..
23666 this.disable = this.disable || {};
23667 Roo.applyIf(this.disable, {
23670 specialElements : true
23672 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23674 this.editor = config.editor;
23675 this.editorcore = config.editor.editorcore;
23677 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23679 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23680 // dont call parent... till later.
23682 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23687 editorcore : false,
23692 "h1","h2","h3","h4","h5","h6",
23694 "abbr", "acronym", "address", "cite", "samp", "var",
23698 onRender : function(ct, position)
23700 // Roo.log("Call onRender: " + this.xtype);
23702 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23704 this.el.dom.style.marginBottom = '0';
23706 var editorcore = this.editorcore;
23707 var editor= this.editor;
23710 var btn = function(id,cmd , toggle, handler, html){
23712 var event = toggle ? 'toggle' : 'click';
23717 xns: Roo.bootstrap,
23720 enableToggle:toggle !== false,
23722 pressed : toggle ? false : null,
23725 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23726 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23732 // var cb_box = function...
23737 xns: Roo.bootstrap,
23738 glyphicon : 'font',
23742 xns: Roo.bootstrap,
23746 Roo.each(this.formats, function(f) {
23747 style.menu.items.push({
23749 xns: Roo.bootstrap,
23750 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23755 editorcore.insertTag(this.tagname);
23762 children.push(style);
23764 btn('bold',false,true);
23765 btn('italic',false,true);
23766 btn('align-left', 'justifyleft',true);
23767 btn('align-center', 'justifycenter',true);
23768 btn('align-right' , 'justifyright',true);
23769 btn('link', false, false, function(btn) {
23770 //Roo.log("create link?");
23771 var url = prompt(this.createLinkText, this.defaultLinkValue);
23772 if(url && url != 'http:/'+'/'){
23773 this.editorcore.relayCmd('createlink', url);
23776 btn('list','insertunorderedlist',true);
23777 btn('pencil', false,true, function(btn){
23779 this.toggleSourceEdit(btn.pressed);
23782 if (this.editor.btns.length > 0) {
23783 for (var i = 0; i<this.editor.btns.length; i++) {
23784 children.push(this.editor.btns[i]);
23792 xns: Roo.bootstrap,
23797 xns: Roo.bootstrap,
23802 cog.menu.items.push({
23804 xns: Roo.bootstrap,
23805 html : Clean styles,
23810 editorcore.insertTag(this.tagname);
23819 this.xtype = 'NavSimplebar';
23821 for(var i=0;i< children.length;i++) {
23823 this.buttons.add(this.addxtypeChild(children[i]));
23827 editor.on('editorevent', this.updateToolbar, this);
23829 onBtnClick : function(id)
23831 this.editorcore.relayCmd(id);
23832 this.editorcore.focus();
23836 * Protected method that will not generally be called directly. It triggers
23837 * a toolbar update by reading the markup state of the current selection in the editor.
23839 updateToolbar: function(){
23841 if(!this.editorcore.activated){
23842 this.editor.onFirstFocus(); // is this neeed?
23846 var btns = this.buttons;
23847 var doc = this.editorcore.doc;
23848 btns.get('bold').setActive(doc.queryCommandState('bold'));
23849 btns.get('italic').setActive(doc.queryCommandState('italic'));
23850 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23852 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23853 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23854 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23856 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23857 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23860 var ans = this.editorcore.getAllAncestors();
23861 if (this.formatCombo) {
23864 var store = this.formatCombo.store;
23865 this.formatCombo.setValue("");
23866 for (var i =0; i < ans.length;i++) {
23867 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23869 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23877 // hides menus... - so this cant be on a menu...
23878 Roo.bootstrap.MenuMgr.hideAll();
23880 Roo.bootstrap.MenuMgr.hideAll();
23881 //this.editorsyncValue();
23883 onFirstFocus: function() {
23884 this.buttons.each(function(item){
23888 toggleSourceEdit : function(sourceEditMode){
23891 if(sourceEditMode){
23892 Roo.log("disabling buttons");
23893 this.buttons.each( function(item){
23894 if(item.cmd != 'pencil'){
23900 Roo.log("enabling buttons");
23901 if(this.editorcore.initialized){
23902 this.buttons.each( function(item){
23908 Roo.log("calling toggole on editor");
23909 // tell the editor that it's been pressed..
23910 this.editor.toggleSourceEdit(sourceEditMode);
23920 * @class Roo.bootstrap.Table.AbstractSelectionModel
23921 * @extends Roo.util.Observable
23922 * Abstract base class for grid SelectionModels. It provides the interface that should be
23923 * implemented by descendant classes. This class should not be directly instantiated.
23926 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23927 this.locked = false;
23928 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23932 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23933 /** @ignore Called by the grid automatically. Do not call directly. */
23934 init : function(grid){
23940 * Locks the selections.
23943 this.locked = true;
23947 * Unlocks the selections.
23949 unlock : function(){
23950 this.locked = false;
23954 * Returns true if the selections are locked.
23955 * @return {Boolean}
23957 isLocked : function(){
23958 return this.locked;
23962 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23963 * @class Roo.bootstrap.Table.RowSelectionModel
23964 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23965 * It supports multiple selections and keyboard selection/navigation.
23967 * @param {Object} config
23970 Roo.bootstrap.Table.RowSelectionModel = function(config){
23971 Roo.apply(this, config);
23972 this.selections = new Roo.util.MixedCollection(false, function(o){
23977 this.lastActive = false;
23981 * @event selectionchange
23982 * Fires when the selection changes
23983 * @param {SelectionModel} this
23985 "selectionchange" : true,
23987 * @event afterselectionchange
23988 * Fires after the selection changes (eg. by key press or clicking)
23989 * @param {SelectionModel} this
23991 "afterselectionchange" : true,
23993 * @event beforerowselect
23994 * Fires when a row is selected being selected, return false to cancel.
23995 * @param {SelectionModel} this
23996 * @param {Number} rowIndex The selected index
23997 * @param {Boolean} keepExisting False if other selections will be cleared
23999 "beforerowselect" : true,
24002 * Fires when a row is selected.
24003 * @param {SelectionModel} this
24004 * @param {Number} rowIndex The selected index
24005 * @param {Roo.data.Record} r The record
24007 "rowselect" : true,
24009 * @event rowdeselect
24010 * Fires when a row is deselected.
24011 * @param {SelectionModel} this
24012 * @param {Number} rowIndex The selected index
24014 "rowdeselect" : true
24016 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24017 this.locked = false;
24020 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24022 * @cfg {Boolean} singleSelect
24023 * True to allow selection of only one row at a time (defaults to false)
24025 singleSelect : false,
24028 initEvents : function()
24031 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24032 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24033 //}else{ // allow click to work like normal
24034 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24036 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24037 this.grid.on("rowclick", this.handleMouseDown, this);
24039 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24040 "up" : function(e){
24042 this.selectPrevious(e.shiftKey);
24043 }else if(this.last !== false && this.lastActive !== false){
24044 var last = this.last;
24045 this.selectRange(this.last, this.lastActive-1);
24046 this.grid.getView().focusRow(this.lastActive);
24047 if(last !== false){
24051 this.selectFirstRow();
24053 this.fireEvent("afterselectionchange", this);
24055 "down" : function(e){
24057 this.selectNext(e.shiftKey);
24058 }else if(this.last !== false && this.lastActive !== false){
24059 var last = this.last;
24060 this.selectRange(this.last, this.lastActive+1);
24061 this.grid.getView().focusRow(this.lastActive);
24062 if(last !== false){
24066 this.selectFirstRow();
24068 this.fireEvent("afterselectionchange", this);
24072 this.grid.store.on('load', function(){
24073 this.selections.clear();
24076 var view = this.grid.view;
24077 view.on("refresh", this.onRefresh, this);
24078 view.on("rowupdated", this.onRowUpdated, this);
24079 view.on("rowremoved", this.onRemove, this);
24084 onRefresh : function()
24086 var ds = this.grid.store, i, v = this.grid.view;
24087 var s = this.selections;
24088 s.each(function(r){
24089 if((i = ds.indexOfId(r.id)) != -1){
24098 onRemove : function(v, index, r){
24099 this.selections.remove(r);
24103 onRowUpdated : function(v, index, r){
24104 if(this.isSelected(r)){
24105 v.onRowSelect(index);
24111 * @param {Array} records The records to select
24112 * @param {Boolean} keepExisting (optional) True to keep existing selections
24114 selectRecords : function(records, keepExisting)
24117 this.clearSelections();
24119 var ds = this.grid.store;
24120 for(var i = 0, len = records.length; i < len; i++){
24121 this.selectRow(ds.indexOf(records[i]), true);
24126 * Gets the number of selected rows.
24129 getCount : function(){
24130 return this.selections.length;
24134 * Selects the first row in the grid.
24136 selectFirstRow : function(){
24141 * Select the last row.
24142 * @param {Boolean} keepExisting (optional) True to keep existing selections
24144 selectLastRow : function(keepExisting){
24145 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24146 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24150 * Selects the row immediately following the last selected row.
24151 * @param {Boolean} keepExisting (optional) True to keep existing selections
24153 selectNext : function(keepExisting)
24155 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24156 this.selectRow(this.last+1, keepExisting);
24157 this.grid.getView().focusRow(this.last);
24162 * Selects the row that precedes the last selected row.
24163 * @param {Boolean} keepExisting (optional) True to keep existing selections
24165 selectPrevious : function(keepExisting){
24167 this.selectRow(this.last-1, keepExisting);
24168 this.grid.getView().focusRow(this.last);
24173 * Returns the selected records
24174 * @return {Array} Array of selected records
24176 getSelections : function(){
24177 return [].concat(this.selections.items);
24181 * Returns the first selected record.
24184 getSelected : function(){
24185 return this.selections.itemAt(0);
24190 * Clears all selections.
24192 clearSelections : function(fast)
24198 var ds = this.grid.store;
24199 var s = this.selections;
24200 s.each(function(r){
24201 this.deselectRow(ds.indexOfId(r.id));
24205 this.selections.clear();
24212 * Selects all rows.
24214 selectAll : function(){
24218 this.selections.clear();
24219 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24220 this.selectRow(i, true);
24225 * Returns True if there is a selection.
24226 * @return {Boolean}
24228 hasSelection : function(){
24229 return this.selections.length > 0;
24233 * Returns True if the specified row is selected.
24234 * @param {Number/Record} record The record or index of the record to check
24235 * @return {Boolean}
24237 isSelected : function(index){
24238 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24239 return (r && this.selections.key(r.id) ? true : false);
24243 * Returns True if the specified record id is selected.
24244 * @param {String} id The id of record to check
24245 * @return {Boolean}
24247 isIdSelected : function(id){
24248 return (this.selections.key(id) ? true : false);
24253 handleMouseDBClick : function(e, t){
24257 handleMouseDown : function(e, t)
24259 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24260 if(this.isLocked() || rowIndex < 0 ){
24263 if(e.shiftKey && this.last !== false){
24264 var last = this.last;
24265 this.selectRange(last, rowIndex, e.ctrlKey);
24266 this.last = last; // reset the last
24270 var isSelected = this.isSelected(rowIndex);
24271 //Roo.log("select row:" + rowIndex);
24273 this.deselectRow(rowIndex);
24275 this.selectRow(rowIndex, true);
24279 if(e.button !== 0 && isSelected){
24280 alert('rowIndex 2: ' + rowIndex);
24281 view.focusRow(rowIndex);
24282 }else if(e.ctrlKey && isSelected){
24283 this.deselectRow(rowIndex);
24284 }else if(!isSelected){
24285 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24286 view.focusRow(rowIndex);
24290 this.fireEvent("afterselectionchange", this);
24293 handleDragableRowClick : function(grid, rowIndex, e)
24295 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24296 this.selectRow(rowIndex, false);
24297 grid.view.focusRow(rowIndex);
24298 this.fireEvent("afterselectionchange", this);
24303 * Selects multiple rows.
24304 * @param {Array} rows Array of the indexes of the row to select
24305 * @param {Boolean} keepExisting (optional) True to keep existing selections
24307 selectRows : function(rows, keepExisting){
24309 this.clearSelections();
24311 for(var i = 0, len = rows.length; i < len; i++){
24312 this.selectRow(rows[i], true);
24317 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24318 * @param {Number} startRow The index of the first row in the range
24319 * @param {Number} endRow The index of the last row in the range
24320 * @param {Boolean} keepExisting (optional) True to retain existing selections
24322 selectRange : function(startRow, endRow, keepExisting){
24327 this.clearSelections();
24329 if(startRow <= endRow){
24330 for(var i = startRow; i <= endRow; i++){
24331 this.selectRow(i, true);
24334 for(var i = startRow; i >= endRow; i--){
24335 this.selectRow(i, true);
24341 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24342 * @param {Number} startRow The index of the first row in the range
24343 * @param {Number} endRow The index of the last row in the range
24345 deselectRange : function(startRow, endRow, preventViewNotify){
24349 for(var i = startRow; i <= endRow; i++){
24350 this.deselectRow(i, preventViewNotify);
24356 * @param {Number} row The index of the row to select
24357 * @param {Boolean} keepExisting (optional) True to keep existing selections
24359 selectRow : function(index, keepExisting, preventViewNotify)
24361 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24364 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24365 if(!keepExisting || this.singleSelect){
24366 this.clearSelections();
24369 var r = this.grid.store.getAt(index);
24370 //console.log('selectRow - record id :' + r.id);
24372 this.selections.add(r);
24373 this.last = this.lastActive = index;
24374 if(!preventViewNotify){
24375 var proxy = new Roo.Element(
24376 this.grid.getRowDom(index)
24378 proxy.addClass('bg-info info');
24380 this.fireEvent("rowselect", this, index, r);
24381 this.fireEvent("selectionchange", this);
24387 * @param {Number} row The index of the row to deselect
24389 deselectRow : function(index, preventViewNotify)
24394 if(this.last == index){
24397 if(this.lastActive == index){
24398 this.lastActive = false;
24401 var r = this.grid.store.getAt(index);
24406 this.selections.remove(r);
24407 //.console.log('deselectRow - record id :' + r.id);
24408 if(!preventViewNotify){
24410 var proxy = new Roo.Element(
24411 this.grid.getRowDom(index)
24413 proxy.removeClass('bg-info info');
24415 this.fireEvent("rowdeselect", this, index);
24416 this.fireEvent("selectionchange", this);
24420 restoreLast : function(){
24422 this.last = this._last;
24427 acceptsNav : function(row, col, cm){
24428 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24432 onEditorKey : function(field, e){
24433 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24438 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24440 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24442 }else if(k == e.ENTER && !e.ctrlKey){
24446 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24448 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24450 }else if(k == e.ESC){
24454 g.startEditing(newCell[0], newCell[1]);
24460 * Ext JS Library 1.1.1
24461 * Copyright(c) 2006-2007, Ext JS, LLC.
24463 * Originally Released Under LGPL - original licence link has changed is not relivant.
24466 * <script type="text/javascript">
24470 * @class Roo.bootstrap.PagingToolbar
24471 * @extends Roo.bootstrap.NavSimplebar
24472 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24474 * Create a new PagingToolbar
24475 * @param {Object} config The config object
24476 * @param {Roo.data.Store} store
24478 Roo.bootstrap.PagingToolbar = function(config)
24480 // old args format still supported... - xtype is prefered..
24481 // created from xtype...
24483 this.ds = config.dataSource;
24485 if (config.store && !this.ds) {
24486 this.store= Roo.factory(config.store, Roo.data);
24487 this.ds = this.store;
24488 this.ds.xmodule = this.xmodule || false;
24491 this.toolbarItems = [];
24492 if (config.items) {
24493 this.toolbarItems = config.items;
24496 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24501 this.bind(this.ds);
24504 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24508 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24510 * @cfg {Roo.data.Store} dataSource
24511 * The underlying data store providing the paged data
24514 * @cfg {String/HTMLElement/Element} container
24515 * container The id or element that will contain the toolbar
24518 * @cfg {Boolean} displayInfo
24519 * True to display the displayMsg (defaults to false)
24522 * @cfg {Number} pageSize
24523 * The number of records to display per page (defaults to 20)
24527 * @cfg {String} displayMsg
24528 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24530 displayMsg : 'Displaying {0} - {1} of {2}',
24532 * @cfg {String} emptyMsg
24533 * The message to display when no records are found (defaults to "No data to display")
24535 emptyMsg : 'No data to display',
24537 * Customizable piece of the default paging text (defaults to "Page")
24540 beforePageText : "Page",
24542 * Customizable piece of the default paging text (defaults to "of %0")
24545 afterPageText : "of {0}",
24547 * Customizable piece of the default paging text (defaults to "First Page")
24550 firstText : "First Page",
24552 * Customizable piece of the default paging text (defaults to "Previous Page")
24555 prevText : "Previous Page",
24557 * Customizable piece of the default paging text (defaults to "Next Page")
24560 nextText : "Next Page",
24562 * Customizable piece of the default paging text (defaults to "Last Page")
24565 lastText : "Last Page",
24567 * Customizable piece of the default paging text (defaults to "Refresh")
24570 refreshText : "Refresh",
24574 onRender : function(ct, position)
24576 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24577 this.navgroup.parentId = this.id;
24578 this.navgroup.onRender(this.el, null);
24579 // add the buttons to the navgroup
24581 if(this.displayInfo){
24582 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24583 this.displayEl = this.el.select('.x-paging-info', true).first();
24584 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24585 // this.displayEl = navel.el.select('span',true).first();
24591 Roo.each(_this.buttons, function(e){ // this might need to use render????
24592 Roo.factory(e).render(_this.el);
24596 Roo.each(_this.toolbarItems, function(e) {
24597 _this.navgroup.addItem(e);
24601 this.first = this.navgroup.addItem({
24602 tooltip: this.firstText,
24604 icon : 'fa fa-backward',
24606 preventDefault: true,
24607 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24610 this.prev = this.navgroup.addItem({
24611 tooltip: this.prevText,
24613 icon : 'fa fa-step-backward',
24615 preventDefault: true,
24616 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24618 //this.addSeparator();
24621 var field = this.navgroup.addItem( {
24623 cls : 'x-paging-position',
24625 html : this.beforePageText +
24626 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24627 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24630 this.field = field.el.select('input', true).first();
24631 this.field.on("keydown", this.onPagingKeydown, this);
24632 this.field.on("focus", function(){this.dom.select();});
24635 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24636 //this.field.setHeight(18);
24637 //this.addSeparator();
24638 this.next = this.navgroup.addItem({
24639 tooltip: this.nextText,
24641 html : ' <i class="fa fa-step-forward">',
24643 preventDefault: true,
24644 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24646 this.last = this.navgroup.addItem({
24647 tooltip: this.lastText,
24648 icon : 'fa fa-forward',
24651 preventDefault: true,
24652 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24654 //this.addSeparator();
24655 this.loading = this.navgroup.addItem({
24656 tooltip: this.refreshText,
24657 icon: 'fa fa-refresh',
24658 preventDefault: true,
24659 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24665 updateInfo : function(){
24666 if(this.displayEl){
24667 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24668 var msg = count == 0 ?
24672 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24674 this.displayEl.update(msg);
24679 onLoad : function(ds, r, o)
24681 this.cursor = o.params.start ? o.params.start : 0;
24683 var d = this.getPageData(),
24688 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24689 this.field.dom.value = ap;
24690 this.first.setDisabled(ap == 1);
24691 this.prev.setDisabled(ap == 1);
24692 this.next.setDisabled(ap == ps);
24693 this.last.setDisabled(ap == ps);
24694 this.loading.enable();
24699 getPageData : function(){
24700 var total = this.ds.getTotalCount();
24703 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24704 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24709 onLoadError : function(){
24710 this.loading.enable();
24714 onPagingKeydown : function(e){
24715 var k = e.getKey();
24716 var d = this.getPageData();
24718 var v = this.field.dom.value, pageNum;
24719 if(!v || isNaN(pageNum = parseInt(v, 10))){
24720 this.field.dom.value = d.activePage;
24723 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24724 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24727 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))
24729 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24730 this.field.dom.value = pageNum;
24731 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24734 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24736 var v = this.field.dom.value, pageNum;
24737 var increment = (e.shiftKey) ? 10 : 1;
24738 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24741 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24742 this.field.dom.value = d.activePage;
24745 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24747 this.field.dom.value = parseInt(v, 10) + increment;
24748 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24749 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24756 beforeLoad : function(){
24758 this.loading.disable();
24763 onClick : function(which){
24772 ds.load({params:{start: 0, limit: this.pageSize}});
24775 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24778 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24781 var total = ds.getTotalCount();
24782 var extra = total % this.pageSize;
24783 var lastStart = extra ? (total - extra) : total-this.pageSize;
24784 ds.load({params:{start: lastStart, limit: this.pageSize}});
24787 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24793 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24794 * @param {Roo.data.Store} store The data store to unbind
24796 unbind : function(ds){
24797 ds.un("beforeload", this.beforeLoad, this);
24798 ds.un("load", this.onLoad, this);
24799 ds.un("loadexception", this.onLoadError, this);
24800 ds.un("remove", this.updateInfo, this);
24801 ds.un("add", this.updateInfo, this);
24802 this.ds = undefined;
24806 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24807 * @param {Roo.data.Store} store The data store to bind
24809 bind : function(ds){
24810 ds.on("beforeload", this.beforeLoad, this);
24811 ds.on("load", this.onLoad, this);
24812 ds.on("loadexception", this.onLoadError, this);
24813 ds.on("remove", this.updateInfo, this);
24814 ds.on("add", this.updateInfo, this);
24825 * @class Roo.bootstrap.MessageBar
24826 * @extends Roo.bootstrap.Component
24827 * Bootstrap MessageBar class
24828 * @cfg {String} html contents of the MessageBar
24829 * @cfg {String} weight (info | success | warning | danger) default info
24830 * @cfg {String} beforeClass insert the bar before the given class
24831 * @cfg {Boolean} closable (true | false) default false
24832 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24835 * Create a new Element
24836 * @param {Object} config The config object
24839 Roo.bootstrap.MessageBar = function(config){
24840 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24843 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24849 beforeClass: 'bootstrap-sticky-wrap',
24851 getAutoCreate : function(){
24855 cls: 'alert alert-dismissable alert-' + this.weight,
24860 html: this.html || ''
24866 cfg.cls += ' alert-messages-fixed';
24880 onRender : function(ct, position)
24882 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24885 var cfg = Roo.apply({}, this.getAutoCreate());
24889 cfg.cls += ' ' + this.cls;
24892 cfg.style = this.style;
24894 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24896 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24899 this.el.select('>button.close').on('click', this.hide, this);
24905 if (!this.rendered) {
24911 this.fireEvent('show', this);
24917 if (!this.rendered) {
24923 this.fireEvent('hide', this);
24926 update : function()
24928 // var e = this.el.dom.firstChild;
24930 // if(this.closable){
24931 // e = e.nextSibling;
24934 // e.data = this.html || '';
24936 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24952 * @class Roo.bootstrap.Graph
24953 * @extends Roo.bootstrap.Component
24954 * Bootstrap Graph class
24958 @cfg {String} graphtype bar | vbar | pie
24959 @cfg {number} g_x coodinator | centre x (pie)
24960 @cfg {number} g_y coodinator | centre y (pie)
24961 @cfg {number} g_r radius (pie)
24962 @cfg {number} g_height height of the chart (respected by all elements in the set)
24963 @cfg {number} g_width width of the chart (respected by all elements in the set)
24964 @cfg {Object} title The title of the chart
24967 -opts (object) options for the chart
24969 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24970 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24972 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.
24973 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24975 o stretch (boolean)
24977 -opts (object) options for the pie
24980 o startAngle (number)
24981 o endAngle (number)
24985 * Create a new Input
24986 * @param {Object} config The config object
24989 Roo.bootstrap.Graph = function(config){
24990 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24996 * The img click event for the img.
24997 * @param {Roo.EventObject} e
25003 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25014 //g_colors: this.colors,
25021 getAutoCreate : function(){
25032 onRender : function(ct,position){
25035 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25037 if (typeof(Raphael) == 'undefined') {
25038 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25042 this.raphael = Raphael(this.el.dom);
25044 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25045 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25046 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25047 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25049 r.text(160, 10, "Single Series Chart").attr(txtattr);
25050 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25051 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25052 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25054 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25055 r.barchart(330, 10, 300, 220, data1);
25056 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25057 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25060 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25061 // r.barchart(30, 30, 560, 250, xdata, {
25062 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25063 // axis : "0 0 1 1",
25064 // axisxlabels : xdata
25065 // //yvalues : cols,
25068 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25070 // this.load(null,xdata,{
25071 // axis : "0 0 1 1",
25072 // axisxlabels : xdata
25077 load : function(graphtype,xdata,opts)
25079 this.raphael.clear();
25081 graphtype = this.graphtype;
25086 var r = this.raphael,
25087 fin = function () {
25088 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25090 fout = function () {
25091 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25093 pfin = function() {
25094 this.sector.stop();
25095 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25098 this.label[0].stop();
25099 this.label[0].attr({ r: 7.5 });
25100 this.label[1].attr({ "font-weight": 800 });
25103 pfout = function() {
25104 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25107 this.label[0].animate({ r: 5 }, 500, "bounce");
25108 this.label[1].attr({ "font-weight": 400 });
25114 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25117 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25120 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25121 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25123 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25130 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25135 setTitle: function(o)
25140 initEvents: function() {
25143 this.el.on('click', this.onClick, this);
25147 onClick : function(e)
25149 Roo.log('img onclick');
25150 this.fireEvent('click', this, e);
25162 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25165 * @class Roo.bootstrap.dash.NumberBox
25166 * @extends Roo.bootstrap.Component
25167 * Bootstrap NumberBox class
25168 * @cfg {String} headline Box headline
25169 * @cfg {String} content Box content
25170 * @cfg {String} icon Box icon
25171 * @cfg {String} footer Footer text
25172 * @cfg {String} fhref Footer href
25175 * Create a new NumberBox
25176 * @param {Object} config The config object
25180 Roo.bootstrap.dash.NumberBox = function(config){
25181 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25185 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25194 getAutoCreate : function(){
25198 cls : 'small-box ',
25206 cls : 'roo-headline',
25207 html : this.headline
25211 cls : 'roo-content',
25212 html : this.content
25226 cls : 'ion ' + this.icon
25235 cls : 'small-box-footer',
25236 href : this.fhref || '#',
25240 cfg.cn.push(footer);
25247 onRender : function(ct,position){
25248 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25255 setHeadline: function (value)
25257 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25260 setFooter: function (value, href)
25262 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25265 this.el.select('a.small-box-footer',true).first().attr('href', href);
25270 setContent: function (value)
25272 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25275 initEvents: function()
25289 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25292 * @class Roo.bootstrap.dash.TabBox
25293 * @extends Roo.bootstrap.Component
25294 * Bootstrap TabBox class
25295 * @cfg {String} title Title of the TabBox
25296 * @cfg {String} icon Icon of the TabBox
25297 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25298 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25301 * Create a new TabBox
25302 * @param {Object} config The config object
25306 Roo.bootstrap.dash.TabBox = function(config){
25307 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25312 * When a pane is added
25313 * @param {Roo.bootstrap.dash.TabPane} pane
25317 * @event activatepane
25318 * When a pane is activated
25319 * @param {Roo.bootstrap.dash.TabPane} pane
25321 "activatepane" : true
25329 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25334 tabScrollable : false,
25336 getChildContainer : function()
25338 return this.el.select('.tab-content', true).first();
25341 getAutoCreate : function(){
25345 cls: 'pull-left header',
25353 cls: 'fa ' + this.icon
25359 cls: 'nav nav-tabs pull-right',
25365 if(this.tabScrollable){
25372 cls: 'nav nav-tabs pull-right',
25383 cls: 'nav-tabs-custom',
25388 cls: 'tab-content no-padding',
25396 initEvents : function()
25398 //Roo.log('add add pane handler');
25399 this.on('addpane', this.onAddPane, this);
25402 * Updates the box title
25403 * @param {String} html to set the title to.
25405 setTitle : function(value)
25407 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25409 onAddPane : function(pane)
25411 this.panes.push(pane);
25412 //Roo.log('addpane');
25414 // tabs are rendere left to right..
25415 if(!this.showtabs){
25419 var ctr = this.el.select('.nav-tabs', true).first();
25422 var existing = ctr.select('.nav-tab',true);
25423 var qty = existing.getCount();;
25426 var tab = ctr.createChild({
25428 cls : 'nav-tab' + (qty ? '' : ' active'),
25436 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25439 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25441 pane.el.addClass('active');
25446 onTabClick : function(ev,un,ob,pane)
25448 //Roo.log('tab - prev default');
25449 ev.preventDefault();
25452 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25453 pane.tab.addClass('active');
25454 //Roo.log(pane.title);
25455 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25456 // technically we should have a deactivate event.. but maybe add later.
25457 // and it should not de-activate the selected tab...
25458 this.fireEvent('activatepane', pane);
25459 pane.el.addClass('active');
25460 pane.fireEvent('activate');
25465 getActivePane : function()
25468 Roo.each(this.panes, function(p) {
25469 if(p.el.hasClass('active')){
25490 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25492 * @class Roo.bootstrap.TabPane
25493 * @extends Roo.bootstrap.Component
25494 * Bootstrap TabPane class
25495 * @cfg {Boolean} active (false | true) Default false
25496 * @cfg {String} title title of panel
25500 * Create a new TabPane
25501 * @param {Object} config The config object
25504 Roo.bootstrap.dash.TabPane = function(config){
25505 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25511 * When a pane is activated
25512 * @param {Roo.bootstrap.dash.TabPane} pane
25519 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25524 // the tabBox that this is attached to.
25527 getAutoCreate : function()
25535 cfg.cls += ' active';
25540 initEvents : function()
25542 //Roo.log('trigger add pane handler');
25543 this.parent().fireEvent('addpane', this)
25547 * Updates the tab title
25548 * @param {String} html to set the title to.
25550 setTitle: function(str)
25556 this.tab.select('a', true).first().dom.innerHTML = str;
25573 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25576 * @class Roo.bootstrap.menu.Menu
25577 * @extends Roo.bootstrap.Component
25578 * Bootstrap Menu class - container for Menu
25579 * @cfg {String} html Text of the menu
25580 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25581 * @cfg {String} icon Font awesome icon
25582 * @cfg {String} pos Menu align to (top | bottom) default bottom
25586 * Create a new Menu
25587 * @param {Object} config The config object
25591 Roo.bootstrap.menu.Menu = function(config){
25592 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25596 * @event beforeshow
25597 * Fires before this menu is displayed
25598 * @param {Roo.bootstrap.menu.Menu} this
25602 * @event beforehide
25603 * Fires before this menu is hidden
25604 * @param {Roo.bootstrap.menu.Menu} this
25609 * Fires after this menu is displayed
25610 * @param {Roo.bootstrap.menu.Menu} this
25615 * Fires after this menu is hidden
25616 * @param {Roo.bootstrap.menu.Menu} this
25621 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25622 * @param {Roo.bootstrap.menu.Menu} this
25623 * @param {Roo.EventObject} e
25630 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25634 weight : 'default',
25639 getChildContainer : function() {
25640 if(this.isSubMenu){
25644 return this.el.select('ul.dropdown-menu', true).first();
25647 getAutoCreate : function()
25652 cls : 'roo-menu-text',
25660 cls : 'fa ' + this.icon
25671 cls : 'dropdown-button btn btn-' + this.weight,
25676 cls : 'dropdown-toggle btn btn-' + this.weight,
25686 cls : 'dropdown-menu'
25692 if(this.pos == 'top'){
25693 cfg.cls += ' dropup';
25696 if(this.isSubMenu){
25699 cls : 'dropdown-menu'
25706 onRender : function(ct, position)
25708 this.isSubMenu = ct.hasClass('dropdown-submenu');
25710 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25713 initEvents : function()
25715 if(this.isSubMenu){
25719 this.hidden = true;
25721 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25722 this.triggerEl.on('click', this.onTriggerPress, this);
25724 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25725 this.buttonEl.on('click', this.onClick, this);
25731 if(this.isSubMenu){
25735 return this.el.select('ul.dropdown-menu', true).first();
25738 onClick : function(e)
25740 this.fireEvent("click", this, e);
25743 onTriggerPress : function(e)
25745 if (this.isVisible()) {
25752 isVisible : function(){
25753 return !this.hidden;
25758 this.fireEvent("beforeshow", this);
25760 this.hidden = false;
25761 this.el.addClass('open');
25763 Roo.get(document).on("mouseup", this.onMouseUp, this);
25765 this.fireEvent("show", this);
25772 this.fireEvent("beforehide", this);
25774 this.hidden = true;
25775 this.el.removeClass('open');
25777 Roo.get(document).un("mouseup", this.onMouseUp);
25779 this.fireEvent("hide", this);
25782 onMouseUp : function()
25796 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25799 * @class Roo.bootstrap.menu.Item
25800 * @extends Roo.bootstrap.Component
25801 * Bootstrap MenuItem class
25802 * @cfg {Boolean} submenu (true | false) default false
25803 * @cfg {String} html text of the item
25804 * @cfg {String} href the link
25805 * @cfg {Boolean} disable (true | false) default false
25806 * @cfg {Boolean} preventDefault (true | false) default true
25807 * @cfg {String} icon Font awesome icon
25808 * @cfg {String} pos Submenu align to (left | right) default right
25812 * Create a new Item
25813 * @param {Object} config The config object
25817 Roo.bootstrap.menu.Item = function(config){
25818 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25822 * Fires when the mouse is hovering over this menu
25823 * @param {Roo.bootstrap.menu.Item} this
25824 * @param {Roo.EventObject} e
25829 * Fires when the mouse exits this menu
25830 * @param {Roo.bootstrap.menu.Item} this
25831 * @param {Roo.EventObject} e
25837 * The raw click event for the entire grid.
25838 * @param {Roo.EventObject} e
25844 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25849 preventDefault: true,
25854 getAutoCreate : function()
25859 cls : 'roo-menu-item-text',
25867 cls : 'fa ' + this.icon
25876 href : this.href || '#',
25883 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25887 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25889 if(this.pos == 'left'){
25890 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25897 initEvents : function()
25899 this.el.on('mouseover', this.onMouseOver, this);
25900 this.el.on('mouseout', this.onMouseOut, this);
25902 this.el.select('a', true).first().on('click', this.onClick, this);
25906 onClick : function(e)
25908 if(this.preventDefault){
25909 e.preventDefault();
25912 this.fireEvent("click", this, e);
25915 onMouseOver : function(e)
25917 if(this.submenu && this.pos == 'left'){
25918 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25921 this.fireEvent("mouseover", this, e);
25924 onMouseOut : function(e)
25926 this.fireEvent("mouseout", this, e);
25938 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25941 * @class Roo.bootstrap.menu.Separator
25942 * @extends Roo.bootstrap.Component
25943 * Bootstrap Separator class
25946 * Create a new Separator
25947 * @param {Object} config The config object
25951 Roo.bootstrap.menu.Separator = function(config){
25952 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25955 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25957 getAutoCreate : function(){
25978 * @class Roo.bootstrap.Tooltip
25979 * Bootstrap Tooltip class
25980 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25981 * to determine which dom element triggers the tooltip.
25983 * It needs to add support for additional attributes like tooltip-position
25986 * Create a new Toolti
25987 * @param {Object} config The config object
25990 Roo.bootstrap.Tooltip = function(config){
25991 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25993 this.alignment = Roo.bootstrap.Tooltip.alignment;
25995 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25996 this.alignment = config.alignment;
26001 Roo.apply(Roo.bootstrap.Tooltip, {
26003 * @function init initialize tooltip monitoring.
26007 currentTip : false,
26008 currentRegion : false,
26014 Roo.get(document).on('mouseover', this.enter ,this);
26015 Roo.get(document).on('mouseout', this.leave, this);
26018 this.currentTip = new Roo.bootstrap.Tooltip();
26021 enter : function(ev)
26023 var dom = ev.getTarget();
26025 //Roo.log(['enter',dom]);
26026 var el = Roo.fly(dom);
26027 if (this.currentEl) {
26029 //Roo.log(this.currentEl);
26030 //Roo.log(this.currentEl.contains(dom));
26031 if (this.currentEl == el) {
26034 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26040 if (this.currentTip.el) {
26041 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26045 if(!el || el.dom == document){
26051 // you can not look for children, as if el is the body.. then everythign is the child..
26052 if (!el.attr('tooltip')) { //
26053 if (!el.select("[tooltip]").elements.length) {
26056 // is the mouse over this child...?
26057 bindEl = el.select("[tooltip]").first();
26058 var xy = ev.getXY();
26059 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26060 //Roo.log("not in region.");
26063 //Roo.log("child element over..");
26066 this.currentEl = bindEl;
26067 this.currentTip.bind(bindEl);
26068 this.currentRegion = Roo.lib.Region.getRegion(dom);
26069 this.currentTip.enter();
26072 leave : function(ev)
26074 var dom = ev.getTarget();
26075 //Roo.log(['leave',dom]);
26076 if (!this.currentEl) {
26081 if (dom != this.currentEl.dom) {
26084 var xy = ev.getXY();
26085 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26088 // only activate leave if mouse cursor is outside... bounding box..
26093 if (this.currentTip) {
26094 this.currentTip.leave();
26096 //Roo.log('clear currentEl');
26097 this.currentEl = false;
26102 'left' : ['r-l', [-2,0], 'right'],
26103 'right' : ['l-r', [2,0], 'left'],
26104 'bottom' : ['t-b', [0,2], 'top'],
26105 'top' : [ 'b-t', [0,-2], 'bottom']
26111 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26116 delay : null, // can be { show : 300 , hide: 500}
26120 hoverState : null, //???
26122 placement : 'bottom',
26126 getAutoCreate : function(){
26133 cls : 'tooltip-arrow'
26136 cls : 'tooltip-inner'
26143 bind : function(el)
26149 enter : function () {
26151 if (this.timeout != null) {
26152 clearTimeout(this.timeout);
26155 this.hoverState = 'in';
26156 //Roo.log("enter - show");
26157 if (!this.delay || !this.delay.show) {
26162 this.timeout = setTimeout(function () {
26163 if (_t.hoverState == 'in') {
26166 }, this.delay.show);
26170 clearTimeout(this.timeout);
26172 this.hoverState = 'out';
26173 if (!this.delay || !this.delay.hide) {
26179 this.timeout = setTimeout(function () {
26180 //Roo.log("leave - timeout");
26182 if (_t.hoverState == 'out') {
26184 Roo.bootstrap.Tooltip.currentEl = false;
26189 show : function (msg)
26192 this.render(document.body);
26195 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26197 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26199 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26201 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26203 var placement = typeof this.placement == 'function' ?
26204 this.placement.call(this, this.el, on_el) :
26207 var autoToken = /\s?auto?\s?/i;
26208 var autoPlace = autoToken.test(placement);
26210 placement = placement.replace(autoToken, '') || 'top';
26214 //this.el.setXY([0,0]);
26216 //this.el.dom.style.display='block';
26218 //this.el.appendTo(on_el);
26220 var p = this.getPosition();
26221 var box = this.el.getBox();
26227 var align = this.alignment[placement];
26229 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26231 if(placement == 'top' || placement == 'bottom'){
26233 placement = 'right';
26236 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26237 placement = 'left';
26240 var scroll = Roo.select('body', true).first().getScroll();
26242 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26246 align = this.alignment[placement];
26249 this.el.alignTo(this.bindEl, align[0],align[1]);
26250 //var arrow = this.el.select('.arrow',true).first();
26251 //arrow.set(align[2],
26253 this.el.addClass(placement);
26255 this.el.addClass('in fade');
26257 this.hoverState = null;
26259 if (this.el.hasClass('fade')) {
26270 //this.el.setXY([0,0]);
26271 this.el.removeClass('in');
26287 * @class Roo.bootstrap.LocationPicker
26288 * @extends Roo.bootstrap.Component
26289 * Bootstrap LocationPicker class
26290 * @cfg {Number} latitude Position when init default 0
26291 * @cfg {Number} longitude Position when init default 0
26292 * @cfg {Number} zoom default 15
26293 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26294 * @cfg {Boolean} mapTypeControl default false
26295 * @cfg {Boolean} disableDoubleClickZoom default false
26296 * @cfg {Boolean} scrollwheel default true
26297 * @cfg {Boolean} streetViewControl default false
26298 * @cfg {Number} radius default 0
26299 * @cfg {String} locationName
26300 * @cfg {Boolean} draggable default true
26301 * @cfg {Boolean} enableAutocomplete default false
26302 * @cfg {Boolean} enableReverseGeocode default true
26303 * @cfg {String} markerTitle
26306 * Create a new LocationPicker
26307 * @param {Object} config The config object
26311 Roo.bootstrap.LocationPicker = function(config){
26313 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26318 * Fires when the picker initialized.
26319 * @param {Roo.bootstrap.LocationPicker} this
26320 * @param {Google Location} location
26324 * @event positionchanged
26325 * Fires when the picker position changed.
26326 * @param {Roo.bootstrap.LocationPicker} this
26327 * @param {Google Location} location
26329 positionchanged : true,
26332 * Fires when the map resize.
26333 * @param {Roo.bootstrap.LocationPicker} this
26338 * Fires when the map show.
26339 * @param {Roo.bootstrap.LocationPicker} this
26344 * Fires when the map hide.
26345 * @param {Roo.bootstrap.LocationPicker} this
26350 * Fires when click the map.
26351 * @param {Roo.bootstrap.LocationPicker} this
26352 * @param {Map event} e
26356 * @event mapRightClick
26357 * Fires when right click the map.
26358 * @param {Roo.bootstrap.LocationPicker} this
26359 * @param {Map event} e
26361 mapRightClick : true,
26363 * @event markerClick
26364 * Fires when click the marker.
26365 * @param {Roo.bootstrap.LocationPicker} this
26366 * @param {Map event} e
26368 markerClick : true,
26370 * @event markerRightClick
26371 * Fires when right click the marker.
26372 * @param {Roo.bootstrap.LocationPicker} this
26373 * @param {Map event} e
26375 markerRightClick : true,
26377 * @event OverlayViewDraw
26378 * Fires when OverlayView Draw
26379 * @param {Roo.bootstrap.LocationPicker} this
26381 OverlayViewDraw : true,
26383 * @event OverlayViewOnAdd
26384 * Fires when OverlayView Draw
26385 * @param {Roo.bootstrap.LocationPicker} this
26387 OverlayViewOnAdd : true,
26389 * @event OverlayViewOnRemove
26390 * Fires when OverlayView Draw
26391 * @param {Roo.bootstrap.LocationPicker} this
26393 OverlayViewOnRemove : true,
26395 * @event OverlayViewShow
26396 * Fires when OverlayView Draw
26397 * @param {Roo.bootstrap.LocationPicker} this
26398 * @param {Pixel} cpx
26400 OverlayViewShow : true,
26402 * @event OverlayViewHide
26403 * Fires when OverlayView Draw
26404 * @param {Roo.bootstrap.LocationPicker} this
26406 OverlayViewHide : true,
26408 * @event loadexception
26409 * Fires when load google lib failed.
26410 * @param {Roo.bootstrap.LocationPicker} this
26412 loadexception : true
26417 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26419 gMapContext: false,
26425 mapTypeControl: false,
26426 disableDoubleClickZoom: false,
26428 streetViewControl: false,
26432 enableAutocomplete: false,
26433 enableReverseGeocode: true,
26436 getAutoCreate: function()
26441 cls: 'roo-location-picker'
26447 initEvents: function(ct, position)
26449 if(!this.el.getWidth() || this.isApplied()){
26453 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26458 initial: function()
26460 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26461 this.fireEvent('loadexception', this);
26465 if(!this.mapTypeId){
26466 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26469 this.gMapContext = this.GMapContext();
26471 this.initOverlayView();
26473 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26477 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26478 _this.setPosition(_this.gMapContext.marker.position);
26481 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26482 _this.fireEvent('mapClick', this, event);
26486 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26487 _this.fireEvent('mapRightClick', this, event);
26491 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26492 _this.fireEvent('markerClick', this, event);
26496 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26497 _this.fireEvent('markerRightClick', this, event);
26501 this.setPosition(this.gMapContext.location);
26503 this.fireEvent('initial', this, this.gMapContext.location);
26506 initOverlayView: function()
26510 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26514 _this.fireEvent('OverlayViewDraw', _this);
26519 _this.fireEvent('OverlayViewOnAdd', _this);
26522 onRemove: function()
26524 _this.fireEvent('OverlayViewOnRemove', _this);
26527 show: function(cpx)
26529 _this.fireEvent('OverlayViewShow', _this, cpx);
26534 _this.fireEvent('OverlayViewHide', _this);
26540 fromLatLngToContainerPixel: function(event)
26542 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26545 isApplied: function()
26547 return this.getGmapContext() == false ? false : true;
26550 getGmapContext: function()
26552 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26555 GMapContext: function()
26557 var position = new google.maps.LatLng(this.latitude, this.longitude);
26559 var _map = new google.maps.Map(this.el.dom, {
26562 mapTypeId: this.mapTypeId,
26563 mapTypeControl: this.mapTypeControl,
26564 disableDoubleClickZoom: this.disableDoubleClickZoom,
26565 scrollwheel: this.scrollwheel,
26566 streetViewControl: this.streetViewControl,
26567 locationName: this.locationName,
26568 draggable: this.draggable,
26569 enableAutocomplete: this.enableAutocomplete,
26570 enableReverseGeocode: this.enableReverseGeocode
26573 var _marker = new google.maps.Marker({
26574 position: position,
26576 title: this.markerTitle,
26577 draggable: this.draggable
26584 location: position,
26585 radius: this.radius,
26586 locationName: this.locationName,
26587 addressComponents: {
26588 formatted_address: null,
26589 addressLine1: null,
26590 addressLine2: null,
26592 streetNumber: null,
26596 stateOrProvince: null
26599 domContainer: this.el.dom,
26600 geodecoder: new google.maps.Geocoder()
26604 drawCircle: function(center, radius, options)
26606 if (this.gMapContext.circle != null) {
26607 this.gMapContext.circle.setMap(null);
26611 options = Roo.apply({}, options, {
26612 strokeColor: "#0000FF",
26613 strokeOpacity: .35,
26615 fillColor: "#0000FF",
26619 options.map = this.gMapContext.map;
26620 options.radius = radius;
26621 options.center = center;
26622 this.gMapContext.circle = new google.maps.Circle(options);
26623 return this.gMapContext.circle;
26629 setPosition: function(location)
26631 this.gMapContext.location = location;
26632 this.gMapContext.marker.setPosition(location);
26633 this.gMapContext.map.panTo(location);
26634 this.drawCircle(location, this.gMapContext.radius, {});
26638 if (this.gMapContext.settings.enableReverseGeocode) {
26639 this.gMapContext.geodecoder.geocode({
26640 latLng: this.gMapContext.location
26641 }, function(results, status) {
26643 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26644 _this.gMapContext.locationName = results[0].formatted_address;
26645 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26647 _this.fireEvent('positionchanged', this, location);
26654 this.fireEvent('positionchanged', this, location);
26659 google.maps.event.trigger(this.gMapContext.map, "resize");
26661 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26663 this.fireEvent('resize', this);
26666 setPositionByLatLng: function(latitude, longitude)
26668 this.setPosition(new google.maps.LatLng(latitude, longitude));
26671 getCurrentPosition: function()
26674 latitude: this.gMapContext.location.lat(),
26675 longitude: this.gMapContext.location.lng()
26679 getAddressName: function()
26681 return this.gMapContext.locationName;
26684 getAddressComponents: function()
26686 return this.gMapContext.addressComponents;
26689 address_component_from_google_geocode: function(address_components)
26693 for (var i = 0; i < address_components.length; i++) {
26694 var component = address_components[i];
26695 if (component.types.indexOf("postal_code") >= 0) {
26696 result.postalCode = component.short_name;
26697 } else if (component.types.indexOf("street_number") >= 0) {
26698 result.streetNumber = component.short_name;
26699 } else if (component.types.indexOf("route") >= 0) {
26700 result.streetName = component.short_name;
26701 } else if (component.types.indexOf("neighborhood") >= 0) {
26702 result.city = component.short_name;
26703 } else if (component.types.indexOf("locality") >= 0) {
26704 result.city = component.short_name;
26705 } else if (component.types.indexOf("sublocality") >= 0) {
26706 result.district = component.short_name;
26707 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26708 result.stateOrProvince = component.short_name;
26709 } else if (component.types.indexOf("country") >= 0) {
26710 result.country = component.short_name;
26714 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26715 result.addressLine2 = "";
26719 setZoomLevel: function(zoom)
26721 this.gMapContext.map.setZoom(zoom);
26734 this.fireEvent('show', this);
26745 this.fireEvent('hide', this);
26750 Roo.apply(Roo.bootstrap.LocationPicker, {
26752 OverlayView : function(map, options)
26754 options = options || {};
26768 * @class Roo.bootstrap.Alert
26769 * @extends Roo.bootstrap.Component
26770 * Bootstrap Alert class
26771 * @cfg {String} title The title of alert
26772 * @cfg {String} html The content of alert
26773 * @cfg {String} weight ( success | info | warning | danger )
26774 * @cfg {String} faicon font-awesomeicon
26777 * Create a new alert
26778 * @param {Object} config The config object
26782 Roo.bootstrap.Alert = function(config){
26783 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26787 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26794 getAutoCreate : function()
26803 cls : 'roo-alert-icon'
26808 cls : 'roo-alert-title',
26813 cls : 'roo-alert-text',
26820 cfg.cn[0].cls += ' fa ' + this.faicon;
26824 cfg.cls += ' alert-' + this.weight;
26830 initEvents: function()
26832 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26835 setTitle : function(str)
26837 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26840 setText : function(str)
26842 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26845 setWeight : function(weight)
26848 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26851 this.weight = weight;
26853 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26856 setIcon : function(icon)
26859 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26862 this.faicon = icon;
26864 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26885 * @class Roo.bootstrap.UploadCropbox
26886 * @extends Roo.bootstrap.Component
26887 * Bootstrap UploadCropbox class
26888 * @cfg {String} emptyText show when image has been loaded
26889 * @cfg {String} rotateNotify show when image too small to rotate
26890 * @cfg {Number} errorTimeout default 3000
26891 * @cfg {Number} minWidth default 300
26892 * @cfg {Number} minHeight default 300
26893 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26894 * @cfg {Boolean} isDocument (true|false) default false
26895 * @cfg {String} url action url
26896 * @cfg {String} paramName default 'imageUpload'
26897 * @cfg {String} method default POST
26898 * @cfg {Boolean} loadMask (true|false) default true
26899 * @cfg {Boolean} loadingText default 'Loading...'
26902 * Create a new UploadCropbox
26903 * @param {Object} config The config object
26906 Roo.bootstrap.UploadCropbox = function(config){
26907 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26911 * @event beforeselectfile
26912 * Fire before select file
26913 * @param {Roo.bootstrap.UploadCropbox} this
26915 "beforeselectfile" : true,
26918 * Fire after initEvent
26919 * @param {Roo.bootstrap.UploadCropbox} this
26924 * Fire after initEvent
26925 * @param {Roo.bootstrap.UploadCropbox} this
26926 * @param {String} data
26931 * Fire when preparing the file data
26932 * @param {Roo.bootstrap.UploadCropbox} this
26933 * @param {Object} file
26938 * Fire when get exception
26939 * @param {Roo.bootstrap.UploadCropbox} this
26940 * @param {XMLHttpRequest} xhr
26942 "exception" : true,
26944 * @event beforeloadcanvas
26945 * Fire before load the canvas
26946 * @param {Roo.bootstrap.UploadCropbox} this
26947 * @param {String} src
26949 "beforeloadcanvas" : true,
26952 * Fire when trash image
26953 * @param {Roo.bootstrap.UploadCropbox} this
26958 * Fire when download the image
26959 * @param {Roo.bootstrap.UploadCropbox} this
26963 * @event footerbuttonclick
26964 * Fire when footerbuttonclick
26965 * @param {Roo.bootstrap.UploadCropbox} this
26966 * @param {String} type
26968 "footerbuttonclick" : true,
26972 * @param {Roo.bootstrap.UploadCropbox} this
26977 * Fire when rotate the image
26978 * @param {Roo.bootstrap.UploadCropbox} this
26979 * @param {String} pos
26984 * Fire when inspect the file
26985 * @param {Roo.bootstrap.UploadCropbox} this
26986 * @param {Object} file
26991 * Fire when xhr upload the file
26992 * @param {Roo.bootstrap.UploadCropbox} this
26993 * @param {Object} data
26998 * Fire when arrange the file data
26999 * @param {Roo.bootstrap.UploadCropbox} this
27000 * @param {Object} formData
27005 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27008 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27010 emptyText : 'Click to upload image',
27011 rotateNotify : 'Image is too small to rotate',
27012 errorTimeout : 3000,
27026 cropType : 'image/jpeg',
27028 canvasLoaded : false,
27029 isDocument : false,
27031 paramName : 'imageUpload',
27033 loadingText : 'Loading...',
27036 getAutoCreate : function()
27040 cls : 'roo-upload-cropbox',
27044 cls : 'roo-upload-cropbox-selector',
27049 cls : 'roo-upload-cropbox-body',
27050 style : 'cursor:pointer',
27054 cls : 'roo-upload-cropbox-preview'
27058 cls : 'roo-upload-cropbox-thumb'
27062 cls : 'roo-upload-cropbox-empty-notify',
27063 html : this.emptyText
27067 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27068 html : this.rotateNotify
27074 cls : 'roo-upload-cropbox-footer',
27077 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27087 onRender : function(ct, position)
27089 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27091 if (this.buttons.length) {
27093 Roo.each(this.buttons, function(bb) {
27095 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27097 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27103 this.maskEl = this.el;
27107 initEvents : function()
27109 this.urlAPI = (window.createObjectURL && window) ||
27110 (window.URL && URL.revokeObjectURL && URL) ||
27111 (window.webkitURL && webkitURL);
27113 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27114 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27116 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27117 this.selectorEl.hide();
27119 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27120 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27122 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27123 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27124 this.thumbEl.hide();
27126 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27127 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27129 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27130 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27131 this.errorEl.hide();
27133 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27134 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27135 this.footerEl.hide();
27137 this.setThumbBoxSize();
27143 this.fireEvent('initial', this);
27150 window.addEventListener("resize", function() { _this.resize(); } );
27152 this.bodyEl.on('click', this.beforeSelectFile, this);
27155 this.bodyEl.on('touchstart', this.onTouchStart, this);
27156 this.bodyEl.on('touchmove', this.onTouchMove, this);
27157 this.bodyEl.on('touchend', this.onTouchEnd, this);
27161 this.bodyEl.on('mousedown', this.onMouseDown, this);
27162 this.bodyEl.on('mousemove', this.onMouseMove, this);
27163 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27164 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27165 Roo.get(document).on('mouseup', this.onMouseUp, this);
27168 this.selectorEl.on('change', this.onFileSelected, this);
27174 this.baseScale = 1;
27176 this.baseRotate = 1;
27177 this.dragable = false;
27178 this.pinching = false;
27181 this.cropData = false;
27182 this.notifyEl.dom.innerHTML = this.emptyText;
27184 this.selectorEl.dom.value = '';
27188 resize : function()
27190 if(this.fireEvent('resize', this) != false){
27191 this.setThumbBoxPosition();
27192 this.setCanvasPosition();
27196 onFooterButtonClick : function(e, el, o, type)
27199 case 'rotate-left' :
27200 this.onRotateLeft(e);
27202 case 'rotate-right' :
27203 this.onRotateRight(e);
27206 this.beforeSelectFile(e);
27221 this.fireEvent('footerbuttonclick', this, type);
27224 beforeSelectFile : function(e)
27226 e.preventDefault();
27228 if(this.fireEvent('beforeselectfile', this) != false){
27229 this.selectorEl.dom.click();
27233 onFileSelected : function(e)
27235 e.preventDefault();
27237 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27241 var file = this.selectorEl.dom.files[0];
27243 if(this.fireEvent('inspect', this, file) != false){
27244 this.prepare(file);
27249 trash : function(e)
27251 this.fireEvent('trash', this);
27254 download : function(e)
27256 this.fireEvent('download', this);
27259 loadCanvas : function(src)
27261 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27265 this.imageEl = document.createElement('img');
27269 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27271 this.imageEl.src = src;
27275 onLoadCanvas : function()
27277 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27278 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27280 this.bodyEl.un('click', this.beforeSelectFile, this);
27282 this.notifyEl.hide();
27283 this.thumbEl.show();
27284 this.footerEl.show();
27286 this.baseRotateLevel();
27288 if(this.isDocument){
27289 this.setThumbBoxSize();
27292 this.setThumbBoxPosition();
27294 this.baseScaleLevel();
27300 this.canvasLoaded = true;
27303 this.maskEl.unmask();
27308 setCanvasPosition : function()
27310 if(!this.canvasEl){
27314 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27315 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27317 this.previewEl.setLeft(pw);
27318 this.previewEl.setTop(ph);
27322 onMouseDown : function(e)
27326 this.dragable = true;
27327 this.pinching = false;
27329 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27330 this.dragable = false;
27334 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27335 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27339 onMouseMove : function(e)
27343 if(!this.canvasLoaded){
27347 if (!this.dragable){
27351 var minX = Math.ceil(this.thumbEl.getLeft(true));
27352 var minY = Math.ceil(this.thumbEl.getTop(true));
27354 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27355 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27357 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27358 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27360 x = x - this.mouseX;
27361 y = y - this.mouseY;
27363 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27364 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27366 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27367 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27369 this.previewEl.setLeft(bgX);
27370 this.previewEl.setTop(bgY);
27372 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27373 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27376 onMouseUp : function(e)
27380 this.dragable = false;
27383 onMouseWheel : function(e)
27387 this.startScale = this.scale;
27389 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27391 if(!this.zoomable()){
27392 this.scale = this.startScale;
27401 zoomable : function()
27403 var minScale = this.thumbEl.getWidth() / this.minWidth;
27405 if(this.minWidth < this.minHeight){
27406 minScale = this.thumbEl.getHeight() / this.minHeight;
27409 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27410 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27414 (this.rotate == 0 || this.rotate == 180) &&
27416 width > this.imageEl.OriginWidth ||
27417 height > this.imageEl.OriginHeight ||
27418 (width < this.minWidth && height < this.minHeight)
27426 (this.rotate == 90 || this.rotate == 270) &&
27428 width > this.imageEl.OriginWidth ||
27429 height > this.imageEl.OriginHeight ||
27430 (width < this.minHeight && height < this.minWidth)
27437 !this.isDocument &&
27438 (this.rotate == 0 || this.rotate == 180) &&
27440 width < this.minWidth ||
27441 width > this.imageEl.OriginWidth ||
27442 height < this.minHeight ||
27443 height > this.imageEl.OriginHeight
27450 !this.isDocument &&
27451 (this.rotate == 90 || this.rotate == 270) &&
27453 width < this.minHeight ||
27454 width > this.imageEl.OriginWidth ||
27455 height < this.minWidth ||
27456 height > this.imageEl.OriginHeight
27466 onRotateLeft : function(e)
27468 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27470 var minScale = this.thumbEl.getWidth() / this.minWidth;
27472 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27473 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27475 this.startScale = this.scale;
27477 while (this.getScaleLevel() < minScale){
27479 this.scale = this.scale + 1;
27481 if(!this.zoomable()){
27486 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27487 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27492 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27499 this.scale = this.startScale;
27501 this.onRotateFail();
27506 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27508 if(this.isDocument){
27509 this.setThumbBoxSize();
27510 this.setThumbBoxPosition();
27511 this.setCanvasPosition();
27516 this.fireEvent('rotate', this, 'left');
27520 onRotateRight : function(e)
27522 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27524 var minScale = this.thumbEl.getWidth() / this.minWidth;
27526 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27527 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27529 this.startScale = this.scale;
27531 while (this.getScaleLevel() < minScale){
27533 this.scale = this.scale + 1;
27535 if(!this.zoomable()){
27540 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27541 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27546 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27553 this.scale = this.startScale;
27555 this.onRotateFail();
27560 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27562 if(this.isDocument){
27563 this.setThumbBoxSize();
27564 this.setThumbBoxPosition();
27565 this.setCanvasPosition();
27570 this.fireEvent('rotate', this, 'right');
27573 onRotateFail : function()
27575 this.errorEl.show(true);
27579 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27584 this.previewEl.dom.innerHTML = '';
27586 var canvasEl = document.createElement("canvas");
27588 var contextEl = canvasEl.getContext("2d");
27590 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27591 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27592 var center = this.imageEl.OriginWidth / 2;
27594 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27595 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27596 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27597 center = this.imageEl.OriginHeight / 2;
27600 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27602 contextEl.translate(center, center);
27603 contextEl.rotate(this.rotate * Math.PI / 180);
27605 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27607 this.canvasEl = document.createElement("canvas");
27609 this.contextEl = this.canvasEl.getContext("2d");
27611 switch (this.rotate) {
27614 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27615 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27617 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27622 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27623 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27625 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27626 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);
27630 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27635 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27636 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27638 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27639 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27643 this.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);
27648 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27649 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27651 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27652 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27656 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);
27663 this.previewEl.appendChild(this.canvasEl);
27665 this.setCanvasPosition();
27670 if(!this.canvasLoaded){
27674 var imageCanvas = document.createElement("canvas");
27676 var imageContext = imageCanvas.getContext("2d");
27678 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27679 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27681 var center = imageCanvas.width / 2;
27683 imageContext.translate(center, center);
27685 imageContext.rotate(this.rotate * Math.PI / 180);
27687 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27689 var canvas = document.createElement("canvas");
27691 var context = canvas.getContext("2d");
27693 canvas.width = this.minWidth;
27694 canvas.height = this.minHeight;
27696 switch (this.rotate) {
27699 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27700 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27702 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27703 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27705 var targetWidth = this.minWidth - 2 * x;
27706 var targetHeight = this.minHeight - 2 * y;
27710 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27711 scale = targetWidth / width;
27714 if(x > 0 && y == 0){
27715 scale = targetHeight / height;
27718 if(x > 0 && y > 0){
27719 scale = targetWidth / width;
27721 if(width < height){
27722 scale = targetHeight / height;
27726 context.scale(scale, scale);
27728 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27729 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27731 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27732 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27734 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27739 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27740 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27742 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27743 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27745 var targetWidth = this.minWidth - 2 * x;
27746 var targetHeight = this.minHeight - 2 * y;
27750 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27751 scale = targetWidth / width;
27754 if(x > 0 && y == 0){
27755 scale = targetHeight / height;
27758 if(x > 0 && y > 0){
27759 scale = targetWidth / width;
27761 if(width < height){
27762 scale = targetHeight / height;
27766 context.scale(scale, scale);
27768 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27769 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27771 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27772 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27774 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27776 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27781 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27782 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27784 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27785 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27787 var targetWidth = this.minWidth - 2 * x;
27788 var targetHeight = this.minHeight - 2 * y;
27792 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27793 scale = targetWidth / width;
27796 if(x > 0 && y == 0){
27797 scale = targetHeight / height;
27800 if(x > 0 && y > 0){
27801 scale = targetWidth / width;
27803 if(width < height){
27804 scale = targetHeight / height;
27808 context.scale(scale, scale);
27810 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27811 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27813 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27814 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27816 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27817 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27819 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27824 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27825 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27827 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27828 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27830 var targetWidth = this.minWidth - 2 * x;
27831 var targetHeight = this.minHeight - 2 * y;
27835 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27836 scale = targetWidth / width;
27839 if(x > 0 && y == 0){
27840 scale = targetHeight / height;
27843 if(x > 0 && y > 0){
27844 scale = targetWidth / width;
27846 if(width < height){
27847 scale = targetHeight / height;
27851 context.scale(scale, scale);
27853 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27854 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27856 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27857 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27859 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27861 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27868 this.cropData = canvas.toDataURL(this.cropType);
27870 if(this.fireEvent('crop', this, this.cropData) !== false){
27871 this.process(this.file, this.cropData);
27878 setThumbBoxSize : function()
27882 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27883 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27884 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27886 this.minWidth = width;
27887 this.minHeight = height;
27889 if(this.rotate == 90 || this.rotate == 270){
27890 this.minWidth = height;
27891 this.minHeight = width;
27896 width = Math.ceil(this.minWidth * height / this.minHeight);
27898 if(this.minWidth > this.minHeight){
27900 height = Math.ceil(this.minHeight * width / this.minWidth);
27903 this.thumbEl.setStyle({
27904 width : width + 'px',
27905 height : height + 'px'
27912 setThumbBoxPosition : function()
27914 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27915 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27917 this.thumbEl.setLeft(x);
27918 this.thumbEl.setTop(y);
27922 baseRotateLevel : function()
27924 this.baseRotate = 1;
27927 typeof(this.exif) != 'undefined' &&
27928 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27929 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27931 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27934 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27938 baseScaleLevel : function()
27942 if(this.isDocument){
27944 if(this.baseRotate == 6 || this.baseRotate == 8){
27946 height = this.thumbEl.getHeight();
27947 this.baseScale = height / this.imageEl.OriginWidth;
27949 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27950 width = this.thumbEl.getWidth();
27951 this.baseScale = width / this.imageEl.OriginHeight;
27957 height = this.thumbEl.getHeight();
27958 this.baseScale = height / this.imageEl.OriginHeight;
27960 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27961 width = this.thumbEl.getWidth();
27962 this.baseScale = width / this.imageEl.OriginWidth;
27968 if(this.baseRotate == 6 || this.baseRotate == 8){
27970 width = this.thumbEl.getHeight();
27971 this.baseScale = width / this.imageEl.OriginHeight;
27973 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27974 height = this.thumbEl.getWidth();
27975 this.baseScale = height / this.imageEl.OriginHeight;
27978 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27979 height = this.thumbEl.getWidth();
27980 this.baseScale = height / this.imageEl.OriginHeight;
27982 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27983 width = this.thumbEl.getHeight();
27984 this.baseScale = width / this.imageEl.OriginWidth;
27991 width = this.thumbEl.getWidth();
27992 this.baseScale = width / this.imageEl.OriginWidth;
27994 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27995 height = this.thumbEl.getHeight();
27996 this.baseScale = height / this.imageEl.OriginHeight;
27999 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28001 height = this.thumbEl.getHeight();
28002 this.baseScale = height / this.imageEl.OriginHeight;
28004 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28005 width = this.thumbEl.getWidth();
28006 this.baseScale = width / this.imageEl.OriginWidth;
28014 getScaleLevel : function()
28016 return this.baseScale * Math.pow(1.1, this.scale);
28019 onTouchStart : function(e)
28021 if(!this.canvasLoaded){
28022 this.beforeSelectFile(e);
28026 var touches = e.browserEvent.touches;
28032 if(touches.length == 1){
28033 this.onMouseDown(e);
28037 if(touches.length != 2){
28043 for(var i = 0, finger; finger = touches[i]; i++){
28044 coords.push(finger.pageX, finger.pageY);
28047 var x = Math.pow(coords[0] - coords[2], 2);
28048 var y = Math.pow(coords[1] - coords[3], 2);
28050 this.startDistance = Math.sqrt(x + y);
28052 this.startScale = this.scale;
28054 this.pinching = true;
28055 this.dragable = false;
28059 onTouchMove : function(e)
28061 if(!this.pinching && !this.dragable){
28065 var touches = e.browserEvent.touches;
28072 this.onMouseMove(e);
28078 for(var i = 0, finger; finger = touches[i]; i++){
28079 coords.push(finger.pageX, finger.pageY);
28082 var x = Math.pow(coords[0] - coords[2], 2);
28083 var y = Math.pow(coords[1] - coords[3], 2);
28085 this.endDistance = Math.sqrt(x + y);
28087 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28089 if(!this.zoomable()){
28090 this.scale = this.startScale;
28098 onTouchEnd : function(e)
28100 this.pinching = false;
28101 this.dragable = false;
28105 process : function(file, crop)
28108 this.maskEl.mask(this.loadingText);
28111 this.xhr = new XMLHttpRequest();
28113 file.xhr = this.xhr;
28115 this.xhr.open(this.method, this.url, true);
28118 "Accept": "application/json",
28119 "Cache-Control": "no-cache",
28120 "X-Requested-With": "XMLHttpRequest"
28123 for (var headerName in headers) {
28124 var headerValue = headers[headerName];
28126 this.xhr.setRequestHeader(headerName, headerValue);
28132 this.xhr.onload = function()
28134 _this.xhrOnLoad(_this.xhr);
28137 this.xhr.onerror = function()
28139 _this.xhrOnError(_this.xhr);
28142 var formData = new FormData();
28144 formData.append('returnHTML', 'NO');
28147 formData.append('crop', crop);
28150 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28151 formData.append(this.paramName, file, file.name);
28154 if(typeof(file.filename) != 'undefined'){
28155 formData.append('filename', file.filename);
28158 if(typeof(file.mimetype) != 'undefined'){
28159 formData.append('mimetype', file.mimetype);
28162 if(this.fireEvent('arrange', this, formData) != false){
28163 this.xhr.send(formData);
28167 xhrOnLoad : function(xhr)
28170 this.maskEl.unmask();
28173 if (xhr.readyState !== 4) {
28174 this.fireEvent('exception', this, xhr);
28178 var response = Roo.decode(xhr.responseText);
28180 if(!response.success){
28181 this.fireEvent('exception', this, xhr);
28185 var response = Roo.decode(xhr.responseText);
28187 this.fireEvent('upload', this, response);
28191 xhrOnError : function()
28194 this.maskEl.unmask();
28197 Roo.log('xhr on error');
28199 var response = Roo.decode(xhr.responseText);
28205 prepare : function(file)
28208 this.maskEl.mask(this.loadingText);
28214 if(typeof(file) === 'string'){
28215 this.loadCanvas(file);
28219 if(!file || !this.urlAPI){
28224 this.cropType = file.type;
28228 if(this.fireEvent('prepare', this, this.file) != false){
28230 var reader = new FileReader();
28232 reader.onload = function (e) {
28233 if (e.target.error) {
28234 Roo.log(e.target.error);
28238 var buffer = e.target.result,
28239 dataView = new DataView(buffer),
28241 maxOffset = dataView.byteLength - 4,
28245 if (dataView.getUint16(0) === 0xffd8) {
28246 while (offset < maxOffset) {
28247 markerBytes = dataView.getUint16(offset);
28249 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28250 markerLength = dataView.getUint16(offset + 2) + 2;
28251 if (offset + markerLength > dataView.byteLength) {
28252 Roo.log('Invalid meta data: Invalid segment size.');
28256 if(markerBytes == 0xffe1){
28257 _this.parseExifData(
28264 offset += markerLength;
28274 var url = _this.urlAPI.createObjectURL(_this.file);
28276 _this.loadCanvas(url);
28281 reader.readAsArrayBuffer(this.file);
28287 parseExifData : function(dataView, offset, length)
28289 var tiffOffset = offset + 10,
28293 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28294 // No Exif data, might be XMP data instead
28298 // Check for the ASCII code for "Exif" (0x45786966):
28299 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28300 // No Exif data, might be XMP data instead
28303 if (tiffOffset + 8 > dataView.byteLength) {
28304 Roo.log('Invalid Exif data: Invalid segment size.');
28307 // Check for the two null bytes:
28308 if (dataView.getUint16(offset + 8) !== 0x0000) {
28309 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28312 // Check the byte alignment:
28313 switch (dataView.getUint16(tiffOffset)) {
28315 littleEndian = true;
28318 littleEndian = false;
28321 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28324 // Check for the TIFF tag marker (0x002A):
28325 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28326 Roo.log('Invalid Exif data: Missing TIFF marker.');
28329 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28330 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28332 this.parseExifTags(
28335 tiffOffset + dirOffset,
28340 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28345 if (dirOffset + 6 > dataView.byteLength) {
28346 Roo.log('Invalid Exif data: Invalid directory offset.');
28349 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28350 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28351 if (dirEndOffset + 4 > dataView.byteLength) {
28352 Roo.log('Invalid Exif data: Invalid directory size.');
28355 for (i = 0; i < tagsNumber; i += 1) {
28359 dirOffset + 2 + 12 * i, // tag offset
28363 // Return the offset to the next directory:
28364 return dataView.getUint32(dirEndOffset, littleEndian);
28367 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28369 var tag = dataView.getUint16(offset, littleEndian);
28371 this.exif[tag] = this.getExifValue(
28375 dataView.getUint16(offset + 2, littleEndian), // tag type
28376 dataView.getUint32(offset + 4, littleEndian), // tag length
28381 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28383 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28392 Roo.log('Invalid Exif data: Invalid tag type.');
28396 tagSize = tagType.size * length;
28397 // Determine if the value is contained in the dataOffset bytes,
28398 // or if the value at the dataOffset is a pointer to the actual data:
28399 dataOffset = tagSize > 4 ?
28400 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28401 if (dataOffset + tagSize > dataView.byteLength) {
28402 Roo.log('Invalid Exif data: Invalid data offset.');
28405 if (length === 1) {
28406 return tagType.getValue(dataView, dataOffset, littleEndian);
28409 for (i = 0; i < length; i += 1) {
28410 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28413 if (tagType.ascii) {
28415 // Concatenate the chars:
28416 for (i = 0; i < values.length; i += 1) {
28418 // Ignore the terminating NULL byte(s):
28419 if (c === '\u0000') {
28431 Roo.apply(Roo.bootstrap.UploadCropbox, {
28433 'Orientation': 0x0112
28437 1: 0, //'top-left',
28439 3: 180, //'bottom-right',
28440 // 4: 'bottom-left',
28442 6: 90, //'right-top',
28443 // 7: 'right-bottom',
28444 8: 270 //'left-bottom'
28448 // byte, 8-bit unsigned int:
28450 getValue: function (dataView, dataOffset) {
28451 return dataView.getUint8(dataOffset);
28455 // ascii, 8-bit byte:
28457 getValue: function (dataView, dataOffset) {
28458 return String.fromCharCode(dataView.getUint8(dataOffset));
28463 // short, 16 bit int:
28465 getValue: function (dataView, dataOffset, littleEndian) {
28466 return dataView.getUint16(dataOffset, littleEndian);
28470 // long, 32 bit int:
28472 getValue: function (dataView, dataOffset, littleEndian) {
28473 return dataView.getUint32(dataOffset, littleEndian);
28477 // rational = two long values, first is numerator, second is denominator:
28479 getValue: function (dataView, dataOffset, littleEndian) {
28480 return dataView.getUint32(dataOffset, littleEndian) /
28481 dataView.getUint32(dataOffset + 4, littleEndian);
28485 // slong, 32 bit signed int:
28487 getValue: function (dataView, dataOffset, littleEndian) {
28488 return dataView.getInt32(dataOffset, littleEndian);
28492 // srational, two slongs, first is numerator, second is denominator:
28494 getValue: function (dataView, dataOffset, littleEndian) {
28495 return dataView.getInt32(dataOffset, littleEndian) /
28496 dataView.getInt32(dataOffset + 4, littleEndian);
28506 cls : 'btn-group roo-upload-cropbox-rotate-left',
28507 action : 'rotate-left',
28511 cls : 'btn btn-default',
28512 html : '<i class="fa fa-undo"></i>'
28518 cls : 'btn-group roo-upload-cropbox-picture',
28519 action : 'picture',
28523 cls : 'btn btn-default',
28524 html : '<i class="fa fa-picture-o"></i>'
28530 cls : 'btn-group roo-upload-cropbox-rotate-right',
28531 action : 'rotate-right',
28535 cls : 'btn btn-default',
28536 html : '<i class="fa fa-repeat"></i>'
28544 cls : 'btn-group roo-upload-cropbox-rotate-left',
28545 action : 'rotate-left',
28549 cls : 'btn btn-default',
28550 html : '<i class="fa fa-undo"></i>'
28556 cls : 'btn-group roo-upload-cropbox-download',
28557 action : 'download',
28561 cls : 'btn btn-default',
28562 html : '<i class="fa fa-download"></i>'
28568 cls : 'btn-group roo-upload-cropbox-crop',
28573 cls : 'btn btn-default',
28574 html : '<i class="fa fa-crop"></i>'
28580 cls : 'btn-group roo-upload-cropbox-trash',
28585 cls : 'btn btn-default',
28586 html : '<i class="fa fa-trash"></i>'
28592 cls : 'btn-group roo-upload-cropbox-rotate-right',
28593 action : 'rotate-right',
28597 cls : 'btn btn-default',
28598 html : '<i class="fa fa-repeat"></i>'
28606 cls : 'btn-group roo-upload-cropbox-rotate-left',
28607 action : 'rotate-left',
28611 cls : 'btn btn-default',
28612 html : '<i class="fa fa-undo"></i>'
28618 cls : 'btn-group roo-upload-cropbox-rotate-right',
28619 action : 'rotate-right',
28623 cls : 'btn btn-default',
28624 html : '<i class="fa fa-repeat"></i>'
28637 * @class Roo.bootstrap.DocumentManager
28638 * @extends Roo.bootstrap.Component
28639 * Bootstrap DocumentManager class
28640 * @cfg {String} paramName default 'imageUpload'
28641 * @cfg {String} toolTipName default 'filename'
28642 * @cfg {String} method default POST
28643 * @cfg {String} url action url
28644 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28645 * @cfg {Boolean} multiple multiple upload default true
28646 * @cfg {Number} thumbSize default 300
28647 * @cfg {String} fieldLabel
28648 * @cfg {Number} labelWidth default 4
28649 * @cfg {String} labelAlign (left|top) default left
28650 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28651 * @cfg {Number} labellg set the width of label (1-12)
28652 * @cfg {Number} labelmd set the width of label (1-12)
28653 * @cfg {Number} labelsm set the width of label (1-12)
28654 * @cfg {Number} labelxs set the width of label (1-12)
28657 * Create a new DocumentManager
28658 * @param {Object} config The config object
28661 Roo.bootstrap.DocumentManager = function(config){
28662 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28665 this.delegates = [];
28670 * Fire when initial the DocumentManager
28671 * @param {Roo.bootstrap.DocumentManager} this
28676 * inspect selected file
28677 * @param {Roo.bootstrap.DocumentManager} this
28678 * @param {File} file
28683 * Fire when xhr load exception
28684 * @param {Roo.bootstrap.DocumentManager} this
28685 * @param {XMLHttpRequest} xhr
28687 "exception" : true,
28689 * @event afterupload
28690 * Fire when xhr load exception
28691 * @param {Roo.bootstrap.DocumentManager} this
28692 * @param {XMLHttpRequest} xhr
28694 "afterupload" : true,
28697 * prepare the form data
28698 * @param {Roo.bootstrap.DocumentManager} this
28699 * @param {Object} formData
28704 * Fire when remove the file
28705 * @param {Roo.bootstrap.DocumentManager} this
28706 * @param {Object} file
28711 * Fire after refresh the file
28712 * @param {Roo.bootstrap.DocumentManager} this
28717 * Fire after click the image
28718 * @param {Roo.bootstrap.DocumentManager} this
28719 * @param {Object} file
28724 * Fire when upload a image and editable set to true
28725 * @param {Roo.bootstrap.DocumentManager} this
28726 * @param {Object} file
28730 * @event beforeselectfile
28731 * Fire before select file
28732 * @param {Roo.bootstrap.DocumentManager} this
28734 "beforeselectfile" : true,
28737 * Fire before process file
28738 * @param {Roo.bootstrap.DocumentManager} this
28739 * @param {Object} file
28743 * @event previewrendered
28744 * Fire when preview rendered
28745 * @param {Roo.bootstrap.DocumentManager} this
28746 * @param {Object} file
28748 "previewrendered" : true,
28751 "previewResize" : true
28756 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28765 paramName : 'imageUpload',
28766 toolTipName : 'filename',
28769 labelAlign : 'left',
28779 getAutoCreate : function()
28781 var managerWidget = {
28783 cls : 'roo-document-manager',
28787 cls : 'roo-document-manager-selector',
28792 cls : 'roo-document-manager-uploader',
28796 cls : 'roo-document-manager-upload-btn',
28797 html : '<i class="fa fa-plus"></i>'
28808 cls : 'column col-md-12',
28813 if(this.fieldLabel.length){
28818 cls : 'column col-md-12',
28819 html : this.fieldLabel
28823 cls : 'column col-md-12',
28828 if(this.labelAlign == 'left'){
28833 html : this.fieldLabel
28842 if(this.labelWidth > 12){
28843 content[0].style = "width: " + this.labelWidth + 'px';
28846 if(this.labelWidth < 13 && this.labelmd == 0){
28847 this.labelmd = this.labelWidth;
28850 if(this.labellg > 0){
28851 content[0].cls += ' col-lg-' + this.labellg;
28852 content[1].cls += ' col-lg-' + (12 - this.labellg);
28855 if(this.labelmd > 0){
28856 content[0].cls += ' col-md-' + this.labelmd;
28857 content[1].cls += ' col-md-' + (12 - this.labelmd);
28860 if(this.labelsm > 0){
28861 content[0].cls += ' col-sm-' + this.labelsm;
28862 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28865 if(this.labelxs > 0){
28866 content[0].cls += ' col-xs-' + this.labelxs;
28867 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28875 cls : 'row clearfix',
28883 initEvents : function()
28885 this.managerEl = this.el.select('.roo-document-manager', true).first();
28886 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28888 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28889 this.selectorEl.hide();
28892 this.selectorEl.attr('multiple', 'multiple');
28895 this.selectorEl.on('change', this.onFileSelected, this);
28897 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28898 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28900 this.uploader.on('click', this.onUploaderClick, this);
28902 this.renderProgressDialog();
28906 window.addEventListener("resize", function() { _this.refresh(); } );
28908 this.fireEvent('initial', this);
28911 renderProgressDialog : function()
28915 this.progressDialog = new Roo.bootstrap.Modal({
28916 cls : 'roo-document-manager-progress-dialog',
28917 allow_close : false,
28927 btnclick : function() {
28928 _this.uploadCancel();
28934 this.progressDialog.render(Roo.get(document.body));
28936 this.progress = new Roo.bootstrap.Progress({
28937 cls : 'roo-document-manager-progress',
28942 this.progress.render(this.progressDialog.getChildContainer());
28944 this.progressBar = new Roo.bootstrap.ProgressBar({
28945 cls : 'roo-document-manager-progress-bar',
28948 aria_valuemax : 12,
28952 this.progressBar.render(this.progress.getChildContainer());
28955 onUploaderClick : function(e)
28957 e.preventDefault();
28959 if(this.fireEvent('beforeselectfile', this) != false){
28960 this.selectorEl.dom.click();
28965 onFileSelected : function(e)
28967 e.preventDefault();
28969 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28973 Roo.each(this.selectorEl.dom.files, function(file){
28974 if(this.fireEvent('inspect', this, file) != false){
28975 this.files.push(file);
28985 this.selectorEl.dom.value = '';
28987 if(!this.files || !this.files.length){
28991 if(this.boxes > 0 && this.files.length > this.boxes){
28992 this.files = this.files.slice(0, this.boxes);
28995 this.uploader.show();
28997 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28998 this.uploader.hide();
29007 Roo.each(this.files, function(file){
29009 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29010 var f = this.renderPreview(file);
29015 if(file.type.indexOf('image') != -1){
29016 this.delegates.push(
29018 _this.process(file);
29019 }).createDelegate(this)
29027 _this.process(file);
29028 }).createDelegate(this)
29033 this.files = files;
29035 this.delegates = this.delegates.concat(docs);
29037 if(!this.delegates.length){
29042 this.progressBar.aria_valuemax = this.delegates.length;
29049 arrange : function()
29051 if(!this.delegates.length){
29052 this.progressDialog.hide();
29057 var delegate = this.delegates.shift();
29059 this.progressDialog.show();
29061 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29063 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29068 refresh : function()
29070 this.uploader.show();
29072 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29073 this.uploader.hide();
29076 Roo.isTouch ? this.closable(false) : this.closable(true);
29078 this.fireEvent('refresh', this);
29081 onRemove : function(e, el, o)
29083 e.preventDefault();
29085 this.fireEvent('remove', this, o);
29089 remove : function(o)
29093 Roo.each(this.files, function(file){
29094 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29103 this.files = files;
29110 Roo.each(this.files, function(file){
29115 file.target.remove();
29124 onClick : function(e, el, o)
29126 e.preventDefault();
29128 this.fireEvent('click', this, o);
29132 closable : function(closable)
29134 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29136 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29148 xhrOnLoad : function(xhr)
29150 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29154 if (xhr.readyState !== 4) {
29156 this.fireEvent('exception', this, xhr);
29160 var response = Roo.decode(xhr.responseText);
29162 if(!response.success){
29164 this.fireEvent('exception', this, xhr);
29168 var file = this.renderPreview(response.data);
29170 this.files.push(file);
29174 this.fireEvent('afterupload', this, xhr);
29178 xhrOnError : function(xhr)
29180 Roo.log('xhr on error');
29182 var response = Roo.decode(xhr.responseText);
29189 process : function(file)
29191 if(this.fireEvent('process', this, file) !== false){
29192 if(this.editable && file.type.indexOf('image') != -1){
29193 this.fireEvent('edit', this, file);
29197 this.uploadStart(file, false);
29204 uploadStart : function(file, crop)
29206 this.xhr = new XMLHttpRequest();
29208 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29213 file.xhr = this.xhr;
29215 this.managerEl.createChild({
29217 cls : 'roo-document-manager-loading',
29221 tooltip : file.name,
29222 cls : 'roo-document-manager-thumb',
29223 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29229 this.xhr.open(this.method, this.url, true);
29232 "Accept": "application/json",
29233 "Cache-Control": "no-cache",
29234 "X-Requested-With": "XMLHttpRequest"
29237 for (var headerName in headers) {
29238 var headerValue = headers[headerName];
29240 this.xhr.setRequestHeader(headerName, headerValue);
29246 this.xhr.onload = function()
29248 _this.xhrOnLoad(_this.xhr);
29251 this.xhr.onerror = function()
29253 _this.xhrOnError(_this.xhr);
29256 var formData = new FormData();
29258 formData.append('returnHTML', 'NO');
29261 formData.append('crop', crop);
29264 formData.append(this.paramName, file, file.name);
29271 if(this.fireEvent('prepare', this, formData, options) != false){
29273 if(options.manually){
29277 this.xhr.send(formData);
29281 this.uploadCancel();
29284 uploadCancel : function()
29290 this.delegates = [];
29292 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29299 renderPreview : function(file)
29301 if(typeof(file.target) != 'undefined' && file.target){
29305 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29307 var previewEl = this.managerEl.createChild({
29309 cls : 'roo-document-manager-preview',
29313 tooltip : file[this.toolTipName],
29314 cls : 'roo-document-manager-thumb',
29315 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29320 html : '<i class="fa fa-times-circle"></i>'
29325 var close = previewEl.select('button.close', true).first();
29327 close.on('click', this.onRemove, this, file);
29329 file.target = previewEl;
29331 var image = previewEl.select('img', true).first();
29335 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29337 image.on('click', this.onClick, this, file);
29339 this.fireEvent('previewrendered', this, file);
29345 onPreviewLoad : function(file, image)
29347 if(typeof(file.target) == 'undefined' || !file.target){
29351 var width = image.dom.naturalWidth || image.dom.width;
29352 var height = image.dom.naturalHeight || image.dom.height;
29354 if(!this.previewResize) {
29358 if(width > height){
29359 file.target.addClass('wide');
29363 file.target.addClass('tall');
29368 uploadFromSource : function(file, crop)
29370 this.xhr = new XMLHttpRequest();
29372 this.managerEl.createChild({
29374 cls : 'roo-document-manager-loading',
29378 tooltip : file.name,
29379 cls : 'roo-document-manager-thumb',
29380 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29386 this.xhr.open(this.method, this.url, true);
29389 "Accept": "application/json",
29390 "Cache-Control": "no-cache",
29391 "X-Requested-With": "XMLHttpRequest"
29394 for (var headerName in headers) {
29395 var headerValue = headers[headerName];
29397 this.xhr.setRequestHeader(headerName, headerValue);
29403 this.xhr.onload = function()
29405 _this.xhrOnLoad(_this.xhr);
29408 this.xhr.onerror = function()
29410 _this.xhrOnError(_this.xhr);
29413 var formData = new FormData();
29415 formData.append('returnHTML', 'NO');
29417 formData.append('crop', crop);
29419 if(typeof(file.filename) != 'undefined'){
29420 formData.append('filename', file.filename);
29423 if(typeof(file.mimetype) != 'undefined'){
29424 formData.append('mimetype', file.mimetype);
29429 if(this.fireEvent('prepare', this, formData) != false){
29430 this.xhr.send(formData);
29440 * @class Roo.bootstrap.DocumentViewer
29441 * @extends Roo.bootstrap.Component
29442 * Bootstrap DocumentViewer class
29443 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29444 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29447 * Create a new DocumentViewer
29448 * @param {Object} config The config object
29451 Roo.bootstrap.DocumentViewer = function(config){
29452 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29457 * Fire after initEvent
29458 * @param {Roo.bootstrap.DocumentViewer} this
29464 * @param {Roo.bootstrap.DocumentViewer} this
29469 * Fire after download button
29470 * @param {Roo.bootstrap.DocumentViewer} this
29475 * Fire after trash button
29476 * @param {Roo.bootstrap.DocumentViewer} this
29483 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29485 showDownload : true,
29489 getAutoCreate : function()
29493 cls : 'roo-document-viewer',
29497 cls : 'roo-document-viewer-body',
29501 cls : 'roo-document-viewer-thumb',
29505 cls : 'roo-document-viewer-image'
29513 cls : 'roo-document-viewer-footer',
29516 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29520 cls : 'btn-group roo-document-viewer-download',
29524 cls : 'btn btn-default',
29525 html : '<i class="fa fa-download"></i>'
29531 cls : 'btn-group roo-document-viewer-trash',
29535 cls : 'btn btn-default',
29536 html : '<i class="fa fa-trash"></i>'
29549 initEvents : function()
29551 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29552 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29554 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29555 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29557 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29558 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29560 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29561 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29563 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29564 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29566 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29567 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29569 this.bodyEl.on('click', this.onClick, this);
29570 this.downloadBtn.on('click', this.onDownload, this);
29571 this.trashBtn.on('click', this.onTrash, this);
29573 this.downloadBtn.hide();
29574 this.trashBtn.hide();
29576 if(this.showDownload){
29577 this.downloadBtn.show();
29580 if(this.showTrash){
29581 this.trashBtn.show();
29584 if(!this.showDownload && !this.showTrash) {
29585 this.footerEl.hide();
29590 initial : function()
29592 this.fireEvent('initial', this);
29596 onClick : function(e)
29598 e.preventDefault();
29600 this.fireEvent('click', this);
29603 onDownload : function(e)
29605 e.preventDefault();
29607 this.fireEvent('download', this);
29610 onTrash : function(e)
29612 e.preventDefault();
29614 this.fireEvent('trash', this);
29626 * @class Roo.bootstrap.NavProgressBar
29627 * @extends Roo.bootstrap.Component
29628 * Bootstrap NavProgressBar class
29631 * Create a new nav progress bar
29632 * @param {Object} config The config object
29635 Roo.bootstrap.NavProgressBar = function(config){
29636 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29638 this.bullets = this.bullets || [];
29640 // Roo.bootstrap.NavProgressBar.register(this);
29644 * Fires when the active item changes
29645 * @param {Roo.bootstrap.NavProgressBar} this
29646 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29647 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29654 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29659 getAutoCreate : function()
29661 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29665 cls : 'roo-navigation-bar-group',
29669 cls : 'roo-navigation-top-bar'
29673 cls : 'roo-navigation-bullets-bar',
29677 cls : 'roo-navigation-bar'
29684 cls : 'roo-navigation-bottom-bar'
29694 initEvents: function()
29699 onRender : function(ct, position)
29701 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29703 if(this.bullets.length){
29704 Roo.each(this.bullets, function(b){
29713 addItem : function(cfg)
29715 var item = new Roo.bootstrap.NavProgressItem(cfg);
29717 item.parentId = this.id;
29718 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29721 var top = new Roo.bootstrap.Element({
29723 cls : 'roo-navigation-bar-text'
29726 var bottom = new Roo.bootstrap.Element({
29728 cls : 'roo-navigation-bar-text'
29731 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29732 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29734 var topText = new Roo.bootstrap.Element({
29736 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29739 var bottomText = new Roo.bootstrap.Element({
29741 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29744 topText.onRender(top.el, null);
29745 bottomText.onRender(bottom.el, null);
29748 item.bottomEl = bottom;
29751 this.barItems.push(item);
29756 getActive : function()
29758 var active = false;
29760 Roo.each(this.barItems, function(v){
29762 if (!v.isActive()) {
29774 setActiveItem : function(item)
29778 Roo.each(this.barItems, function(v){
29779 if (v.rid == item.rid) {
29783 if (v.isActive()) {
29784 v.setActive(false);
29789 item.setActive(true);
29791 this.fireEvent('changed', this, item, prev);
29794 getBarItem: function(rid)
29798 Roo.each(this.barItems, function(e) {
29799 if (e.rid != rid) {
29810 indexOfItem : function(item)
29814 Roo.each(this.barItems, function(v, i){
29816 if (v.rid != item.rid) {
29827 setActiveNext : function()
29829 var i = this.indexOfItem(this.getActive());
29831 if (i > this.barItems.length) {
29835 this.setActiveItem(this.barItems[i+1]);
29838 setActivePrev : function()
29840 var i = this.indexOfItem(this.getActive());
29846 this.setActiveItem(this.barItems[i-1]);
29849 format : function()
29851 if(!this.barItems.length){
29855 var width = 100 / this.barItems.length;
29857 Roo.each(this.barItems, function(i){
29858 i.el.setStyle('width', width + '%');
29859 i.topEl.el.setStyle('width', width + '%');
29860 i.bottomEl.el.setStyle('width', width + '%');
29869 * Nav Progress Item
29874 * @class Roo.bootstrap.NavProgressItem
29875 * @extends Roo.bootstrap.Component
29876 * Bootstrap NavProgressItem class
29877 * @cfg {String} rid the reference id
29878 * @cfg {Boolean} active (true|false) Is item active default false
29879 * @cfg {Boolean} disabled (true|false) Is item active default false
29880 * @cfg {String} html
29881 * @cfg {String} position (top|bottom) text position default bottom
29882 * @cfg {String} icon show icon instead of number
29885 * Create a new NavProgressItem
29886 * @param {Object} config The config object
29888 Roo.bootstrap.NavProgressItem = function(config){
29889 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29894 * The raw click event for the entire grid.
29895 * @param {Roo.bootstrap.NavProgressItem} this
29896 * @param {Roo.EventObject} e
29903 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29909 position : 'bottom',
29912 getAutoCreate : function()
29914 var iconCls = 'roo-navigation-bar-item-icon';
29916 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29920 cls: 'roo-navigation-bar-item',
29930 cfg.cls += ' active';
29933 cfg.cls += ' disabled';
29939 disable : function()
29941 this.setDisabled(true);
29944 enable : function()
29946 this.setDisabled(false);
29949 initEvents: function()
29951 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29953 this.iconEl.on('click', this.onClick, this);
29956 onClick : function(e)
29958 e.preventDefault();
29964 if(this.fireEvent('click', this, e) === false){
29968 this.parent().setActiveItem(this);
29971 isActive: function ()
29973 return this.active;
29976 setActive : function(state)
29978 if(this.active == state){
29982 this.active = state;
29985 this.el.addClass('active');
29989 this.el.removeClass('active');
29994 setDisabled : function(state)
29996 if(this.disabled == state){
30000 this.disabled = state;
30003 this.el.addClass('disabled');
30007 this.el.removeClass('disabled');
30010 tooltipEl : function()
30012 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30025 * @class Roo.bootstrap.FieldLabel
30026 * @extends Roo.bootstrap.Component
30027 * Bootstrap FieldLabel class
30028 * @cfg {String} html contents of the element
30029 * @cfg {String} tag tag of the element default label
30030 * @cfg {String} cls class of the element
30031 * @cfg {String} target label target
30032 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30033 * @cfg {String} invalidClass default "text-warning"
30034 * @cfg {String} validClass default "text-success"
30035 * @cfg {String} iconTooltip default "This field is required"
30036 * @cfg {String} indicatorpos (left|right) default left
30039 * Create a new FieldLabel
30040 * @param {Object} config The config object
30043 Roo.bootstrap.FieldLabel = function(config){
30044 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30049 * Fires after the field has been marked as invalid.
30050 * @param {Roo.form.FieldLabel} this
30051 * @param {String} msg The validation message
30056 * Fires after the field has been validated with no errors.
30057 * @param {Roo.form.FieldLabel} this
30063 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30070 invalidClass : 'has-warning',
30071 validClass : 'has-success',
30072 iconTooltip : 'This field is required',
30073 indicatorpos : 'left',
30075 getAutoCreate : function(){
30078 if (!this.allowBlank) {
30084 cls : 'roo-bootstrap-field-label ' + this.cls,
30089 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30090 tooltip : this.iconTooltip
30099 if(this.indicatorpos == 'right'){
30102 cls : 'roo-bootstrap-field-label ' + this.cls,
30111 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30112 tooltip : this.iconTooltip
30121 initEvents: function()
30123 Roo.bootstrap.Element.superclass.initEvents.call(this);
30125 this.indicator = this.indicatorEl();
30127 if(this.indicator){
30128 this.indicator.removeClass('visible');
30129 this.indicator.addClass('invisible');
30132 Roo.bootstrap.FieldLabel.register(this);
30135 indicatorEl : function()
30137 var indicator = this.el.select('i.roo-required-indicator',true).first();
30148 * Mark this field as valid
30150 markValid : function()
30152 if(this.indicator){
30153 this.indicator.removeClass('visible');
30154 this.indicator.addClass('invisible');
30157 this.el.removeClass(this.invalidClass);
30159 this.el.addClass(this.validClass);
30161 this.fireEvent('valid', this);
30165 * Mark this field as invalid
30166 * @param {String} msg The validation message
30168 markInvalid : function(msg)
30170 if(this.indicator){
30171 this.indicator.removeClass('invisible');
30172 this.indicator.addClass('visible');
30175 this.el.removeClass(this.validClass);
30177 this.el.addClass(this.invalidClass);
30179 this.fireEvent('invalid', this, msg);
30185 Roo.apply(Roo.bootstrap.FieldLabel, {
30190 * register a FieldLabel Group
30191 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30193 register : function(label)
30195 if(this.groups.hasOwnProperty(label.target)){
30199 this.groups[label.target] = label;
30203 * fetch a FieldLabel Group based on the target
30204 * @param {string} target
30205 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30207 get: function(target) {
30208 if (typeof(this.groups[target]) == 'undefined') {
30212 return this.groups[target] ;
30221 * page DateSplitField.
30227 * @class Roo.bootstrap.DateSplitField
30228 * @extends Roo.bootstrap.Component
30229 * Bootstrap DateSplitField class
30230 * @cfg {string} fieldLabel - the label associated
30231 * @cfg {Number} labelWidth set the width of label (0-12)
30232 * @cfg {String} labelAlign (top|left)
30233 * @cfg {Boolean} dayAllowBlank (true|false) default false
30234 * @cfg {Boolean} monthAllowBlank (true|false) default false
30235 * @cfg {Boolean} yearAllowBlank (true|false) default false
30236 * @cfg {string} dayPlaceholder
30237 * @cfg {string} monthPlaceholder
30238 * @cfg {string} yearPlaceholder
30239 * @cfg {string} dayFormat default 'd'
30240 * @cfg {string} monthFormat default 'm'
30241 * @cfg {string} yearFormat default 'Y'
30242 * @cfg {Number} labellg set the width of label (1-12)
30243 * @cfg {Number} labelmd set the width of label (1-12)
30244 * @cfg {Number} labelsm set the width of label (1-12)
30245 * @cfg {Number} labelxs set the width of label (1-12)
30249 * Create a new DateSplitField
30250 * @param {Object} config The config object
30253 Roo.bootstrap.DateSplitField = function(config){
30254 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30260 * getting the data of years
30261 * @param {Roo.bootstrap.DateSplitField} this
30262 * @param {Object} years
30267 * getting the data of days
30268 * @param {Roo.bootstrap.DateSplitField} this
30269 * @param {Object} days
30274 * Fires after the field has been marked as invalid.
30275 * @param {Roo.form.Field} this
30276 * @param {String} msg The validation message
30281 * Fires after the field has been validated with no errors.
30282 * @param {Roo.form.Field} this
30288 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30291 labelAlign : 'top',
30293 dayAllowBlank : false,
30294 monthAllowBlank : false,
30295 yearAllowBlank : false,
30296 dayPlaceholder : '',
30297 monthPlaceholder : '',
30298 yearPlaceholder : '',
30302 isFormField : true,
30308 getAutoCreate : function()
30312 cls : 'row roo-date-split-field-group',
30317 cls : 'form-hidden-field roo-date-split-field-group-value',
30323 var labelCls = 'col-md-12';
30324 var contentCls = 'col-md-4';
30326 if(this.fieldLabel){
30330 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30334 html : this.fieldLabel
30339 if(this.labelAlign == 'left'){
30341 if(this.labelWidth > 12){
30342 label.style = "width: " + this.labelWidth + 'px';
30345 if(this.labelWidth < 13 && this.labelmd == 0){
30346 this.labelmd = this.labelWidth;
30349 if(this.labellg > 0){
30350 labelCls = ' col-lg-' + this.labellg;
30351 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30354 if(this.labelmd > 0){
30355 labelCls = ' col-md-' + this.labelmd;
30356 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30359 if(this.labelsm > 0){
30360 labelCls = ' col-sm-' + this.labelsm;
30361 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30364 if(this.labelxs > 0){
30365 labelCls = ' col-xs-' + this.labelxs;
30366 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30370 label.cls += ' ' + labelCls;
30372 cfg.cn.push(label);
30375 Roo.each(['day', 'month', 'year'], function(t){
30378 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30385 inputEl: function ()
30387 return this.el.select('.roo-date-split-field-group-value', true).first();
30390 onRender : function(ct, position)
30394 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30396 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30398 this.dayField = new Roo.bootstrap.ComboBox({
30399 allowBlank : this.dayAllowBlank,
30400 alwaysQuery : true,
30401 displayField : 'value',
30404 forceSelection : true,
30406 placeholder : this.dayPlaceholder,
30407 selectOnFocus : true,
30408 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30409 triggerAction : 'all',
30411 valueField : 'value',
30412 store : new Roo.data.SimpleStore({
30413 data : (function() {
30415 _this.fireEvent('days', _this, days);
30418 fields : [ 'value' ]
30421 select : function (_self, record, index)
30423 _this.setValue(_this.getValue());
30428 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30430 this.monthField = new Roo.bootstrap.MonthField({
30431 after : '<i class=\"fa fa-calendar\"></i>',
30432 allowBlank : this.monthAllowBlank,
30433 placeholder : this.monthPlaceholder,
30436 render : function (_self)
30438 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30439 e.preventDefault();
30443 select : function (_self, oldvalue, newvalue)
30445 _this.setValue(_this.getValue());
30450 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30452 this.yearField = new Roo.bootstrap.ComboBox({
30453 allowBlank : this.yearAllowBlank,
30454 alwaysQuery : true,
30455 displayField : 'value',
30458 forceSelection : true,
30460 placeholder : this.yearPlaceholder,
30461 selectOnFocus : true,
30462 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30463 triggerAction : 'all',
30465 valueField : 'value',
30466 store : new Roo.data.SimpleStore({
30467 data : (function() {
30469 _this.fireEvent('years', _this, years);
30472 fields : [ 'value' ]
30475 select : function (_self, record, index)
30477 _this.setValue(_this.getValue());
30482 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30485 setValue : function(v, format)
30487 this.inputEl.dom.value = v;
30489 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30491 var d = Date.parseDate(v, f);
30498 this.setDay(d.format(this.dayFormat));
30499 this.setMonth(d.format(this.monthFormat));
30500 this.setYear(d.format(this.yearFormat));
30507 setDay : function(v)
30509 this.dayField.setValue(v);
30510 this.inputEl.dom.value = this.getValue();
30515 setMonth : function(v)
30517 this.monthField.setValue(v, true);
30518 this.inputEl.dom.value = this.getValue();
30523 setYear : function(v)
30525 this.yearField.setValue(v);
30526 this.inputEl.dom.value = this.getValue();
30531 getDay : function()
30533 return this.dayField.getValue();
30536 getMonth : function()
30538 return this.monthField.getValue();
30541 getYear : function()
30543 return this.yearField.getValue();
30546 getValue : function()
30548 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30550 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30560 this.inputEl.dom.value = '';
30565 validate : function()
30567 var d = this.dayField.validate();
30568 var m = this.monthField.validate();
30569 var y = this.yearField.validate();
30574 (!this.dayAllowBlank && !d) ||
30575 (!this.monthAllowBlank && !m) ||
30576 (!this.yearAllowBlank && !y)
30581 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30590 this.markInvalid();
30595 markValid : function()
30598 var label = this.el.select('label', true).first();
30599 var icon = this.el.select('i.fa-star', true).first();
30605 this.fireEvent('valid', this);
30609 * Mark this field as invalid
30610 * @param {String} msg The validation message
30612 markInvalid : function(msg)
30615 var label = this.el.select('label', true).first();
30616 var icon = this.el.select('i.fa-star', true).first();
30618 if(label && !icon){
30619 this.el.select('.roo-date-split-field-label', true).createChild({
30621 cls : 'text-danger fa fa-lg fa-star',
30622 tooltip : 'This field is required',
30623 style : 'margin-right:5px;'
30627 this.fireEvent('invalid', this, msg);
30630 clearInvalid : function()
30632 var label = this.el.select('label', true).first();
30633 var icon = this.el.select('i.fa-star', true).first();
30639 this.fireEvent('valid', this);
30642 getName: function()
30652 * http://masonry.desandro.com
30654 * The idea is to render all the bricks based on vertical width...
30656 * The original code extends 'outlayer' - we might need to use that....
30662 * @class Roo.bootstrap.LayoutMasonry
30663 * @extends Roo.bootstrap.Component
30664 * Bootstrap Layout Masonry class
30667 * Create a new Element
30668 * @param {Object} config The config object
30671 Roo.bootstrap.LayoutMasonry = function(config){
30673 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30677 Roo.bootstrap.LayoutMasonry.register(this);
30683 * Fire after layout the items
30684 * @param {Roo.bootstrap.LayoutMasonry} this
30685 * @param {Roo.EventObject} e
30692 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30695 * @cfg {Boolean} isLayoutInstant = no animation?
30697 isLayoutInstant : false, // needed?
30700 * @cfg {Number} boxWidth width of the columns
30705 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30710 * @cfg {Number} padWidth padding below box..
30715 * @cfg {Number} gutter gutter width..
30720 * @cfg {Number} maxCols maximum number of columns
30726 * @cfg {Boolean} isAutoInitial defalut true
30728 isAutoInitial : true,
30733 * @cfg {Boolean} isHorizontal defalut false
30735 isHorizontal : false,
30737 currentSize : null,
30743 bricks: null, //CompositeElement
30747 _isLayoutInited : false,
30749 // isAlternative : false, // only use for vertical layout...
30752 * @cfg {Number} alternativePadWidth padding below box..
30754 alternativePadWidth : 50,
30756 selectedBrick : [],
30758 getAutoCreate : function(){
30760 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30764 cls: 'blog-masonary-wrapper ' + this.cls,
30766 cls : 'mas-boxes masonary'
30773 getChildContainer: function( )
30775 if (this.boxesEl) {
30776 return this.boxesEl;
30779 this.boxesEl = this.el.select('.mas-boxes').first();
30781 return this.boxesEl;
30785 initEvents : function()
30789 if(this.isAutoInitial){
30790 Roo.log('hook children rendered');
30791 this.on('childrenrendered', function() {
30792 Roo.log('children rendered');
30798 initial : function()
30800 this.selectedBrick = [];
30802 this.currentSize = this.el.getBox(true);
30804 Roo.EventManager.onWindowResize(this.resize, this);
30806 if(!this.isAutoInitial){
30814 //this.layout.defer(500,this);
30818 resize : function()
30820 var cs = this.el.getBox(true);
30823 this.currentSize.width == cs.width &&
30824 this.currentSize.x == cs.x &&
30825 this.currentSize.height == cs.height &&
30826 this.currentSize.y == cs.y
30828 Roo.log("no change in with or X or Y");
30832 this.currentSize = cs;
30838 layout : function()
30840 this._resetLayout();
30842 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30844 this.layoutItems( isInstant );
30846 this._isLayoutInited = true;
30848 this.fireEvent('layout', this);
30852 _resetLayout : function()
30854 if(this.isHorizontal){
30855 this.horizontalMeasureColumns();
30859 this.verticalMeasureColumns();
30863 verticalMeasureColumns : function()
30865 this.getContainerWidth();
30867 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30868 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30872 var boxWidth = this.boxWidth + this.padWidth;
30874 if(this.containerWidth < this.boxWidth){
30875 boxWidth = this.containerWidth
30878 var containerWidth = this.containerWidth;
30880 var cols = Math.floor(containerWidth / boxWidth);
30882 this.cols = Math.max( cols, 1 );
30884 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30886 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30888 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30890 this.colWidth = boxWidth + avail - this.padWidth;
30892 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30893 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30896 horizontalMeasureColumns : function()
30898 this.getContainerWidth();
30900 var boxWidth = this.boxWidth;
30902 if(this.containerWidth < boxWidth){
30903 boxWidth = this.containerWidth;
30906 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30908 this.el.setHeight(boxWidth);
30912 getContainerWidth : function()
30914 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30917 layoutItems : function( isInstant )
30919 Roo.log(this.bricks);
30921 var items = Roo.apply([], this.bricks);
30923 if(this.isHorizontal){
30924 this._horizontalLayoutItems( items , isInstant );
30928 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30929 // this._verticalAlternativeLayoutItems( items , isInstant );
30933 this._verticalLayoutItems( items , isInstant );
30937 _verticalLayoutItems : function ( items , isInstant)
30939 if ( !items || !items.length ) {
30944 ['xs', 'xs', 'xs', 'tall'],
30945 ['xs', 'xs', 'tall'],
30946 ['xs', 'xs', 'sm'],
30947 ['xs', 'xs', 'xs'],
30953 ['sm', 'xs', 'xs'],
30957 ['tall', 'xs', 'xs', 'xs'],
30958 ['tall', 'xs', 'xs'],
30970 Roo.each(items, function(item, k){
30972 switch (item.size) {
30973 // these layouts take up a full box,
30984 boxes.push([item]);
31007 var filterPattern = function(box, length)
31015 var pattern = box.slice(0, length);
31019 Roo.each(pattern, function(i){
31020 format.push(i.size);
31023 Roo.each(standard, function(s){
31025 if(String(s) != String(format)){
31034 if(!match && length == 1){
31039 filterPattern(box, length - 1);
31043 queue.push(pattern);
31045 box = box.slice(length, box.length);
31047 filterPattern(box, 4);
31053 Roo.each(boxes, function(box, k){
31059 if(box.length == 1){
31064 filterPattern(box, 4);
31068 this._processVerticalLayoutQueue( queue, isInstant );
31072 // _verticalAlternativeLayoutItems : function( items , isInstant )
31074 // if ( !items || !items.length ) {
31078 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31082 _horizontalLayoutItems : function ( items , isInstant)
31084 if ( !items || !items.length || items.length < 3) {
31090 var eItems = items.slice(0, 3);
31092 items = items.slice(3, items.length);
31095 ['xs', 'xs', 'xs', 'wide'],
31096 ['xs', 'xs', 'wide'],
31097 ['xs', 'xs', 'sm'],
31098 ['xs', 'xs', 'xs'],
31104 ['sm', 'xs', 'xs'],
31108 ['wide', 'xs', 'xs', 'xs'],
31109 ['wide', 'xs', 'xs'],
31122 Roo.each(items, function(item, k){
31124 switch (item.size) {
31135 boxes.push([item]);
31159 var filterPattern = function(box, length)
31167 var pattern = box.slice(0, length);
31171 Roo.each(pattern, function(i){
31172 format.push(i.size);
31175 Roo.each(standard, function(s){
31177 if(String(s) != String(format)){
31186 if(!match && length == 1){
31191 filterPattern(box, length - 1);
31195 queue.push(pattern);
31197 box = box.slice(length, box.length);
31199 filterPattern(box, 4);
31205 Roo.each(boxes, function(box, k){
31211 if(box.length == 1){
31216 filterPattern(box, 4);
31223 var pos = this.el.getBox(true);
31227 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31229 var hit_end = false;
31231 Roo.each(queue, function(box){
31235 Roo.each(box, function(b){
31237 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31247 Roo.each(box, function(b){
31249 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31252 mx = Math.max(mx, b.x);
31256 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31260 Roo.each(box, function(b){
31262 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31276 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31279 /** Sets position of item in DOM
31280 * @param {Element} item
31281 * @param {Number} x - horizontal position
31282 * @param {Number} y - vertical position
31283 * @param {Boolean} isInstant - disables transitions
31285 _processVerticalLayoutQueue : function( queue, isInstant )
31287 var pos = this.el.getBox(true);
31292 for (var i = 0; i < this.cols; i++){
31296 Roo.each(queue, function(box, k){
31298 var col = k % this.cols;
31300 Roo.each(box, function(b,kk){
31302 b.el.position('absolute');
31304 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31305 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31307 if(b.size == 'md-left' || b.size == 'md-right'){
31308 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31309 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31312 b.el.setWidth(width);
31313 b.el.setHeight(height);
31315 b.el.select('iframe',true).setSize(width,height);
31319 for (var i = 0; i < this.cols; i++){
31321 if(maxY[i] < maxY[col]){
31326 col = Math.min(col, i);
31330 x = pos.x + col * (this.colWidth + this.padWidth);
31334 var positions = [];
31336 switch (box.length){
31338 positions = this.getVerticalOneBoxColPositions(x, y, box);
31341 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31344 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31347 positions = this.getVerticalFourBoxColPositions(x, y, box);
31353 Roo.each(box, function(b,kk){
31355 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31357 var sz = b.el.getSize();
31359 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31367 for (var i = 0; i < this.cols; i++){
31368 mY = Math.max(mY, maxY[i]);
31371 this.el.setHeight(mY - pos.y);
31375 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31377 // var pos = this.el.getBox(true);
31380 // var maxX = pos.right;
31382 // var maxHeight = 0;
31384 // Roo.each(items, function(item, k){
31388 // item.el.position('absolute');
31390 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31392 // item.el.setWidth(width);
31394 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31396 // item.el.setHeight(height);
31399 // item.el.setXY([x, y], isInstant ? false : true);
31401 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31404 // y = y + height + this.alternativePadWidth;
31406 // maxHeight = maxHeight + height + this.alternativePadWidth;
31410 // this.el.setHeight(maxHeight);
31414 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31416 var pos = this.el.getBox(true);
31421 var maxX = pos.right;
31423 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31425 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31427 Roo.each(queue, function(box, k){
31429 Roo.each(box, function(b, kk){
31431 b.el.position('absolute');
31433 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31434 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31436 if(b.size == 'md-left' || b.size == 'md-right'){
31437 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31438 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31441 b.el.setWidth(width);
31442 b.el.setHeight(height);
31450 var positions = [];
31452 switch (box.length){
31454 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31457 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31460 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31463 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31469 Roo.each(box, function(b,kk){
31471 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31473 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31481 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31483 Roo.each(eItems, function(b,k){
31485 b.size = (k == 0) ? 'sm' : 'xs';
31486 b.x = (k == 0) ? 2 : 1;
31487 b.y = (k == 0) ? 2 : 1;
31489 b.el.position('absolute');
31491 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31493 b.el.setWidth(width);
31495 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31497 b.el.setHeight(height);
31501 var positions = [];
31504 x : maxX - this.unitWidth * 2 - this.gutter,
31509 x : maxX - this.unitWidth,
31510 y : minY + (this.unitWidth + this.gutter) * 2
31514 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31518 Roo.each(eItems, function(b,k){
31520 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31526 getVerticalOneBoxColPositions : function(x, y, box)
31530 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31532 if(box[0].size == 'md-left'){
31536 if(box[0].size == 'md-right'){
31541 x : x + (this.unitWidth + this.gutter) * rand,
31548 getVerticalTwoBoxColPositions : function(x, y, box)
31552 if(box[0].size == 'xs'){
31556 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31560 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31574 x : x + (this.unitWidth + this.gutter) * 2,
31575 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31582 getVerticalThreeBoxColPositions : function(x, y, box)
31586 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31594 x : x + (this.unitWidth + this.gutter) * 1,
31599 x : x + (this.unitWidth + this.gutter) * 2,
31607 if(box[0].size == 'xs' && box[1].size == 'xs'){
31616 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31620 x : x + (this.unitWidth + this.gutter) * 1,
31634 x : x + (this.unitWidth + this.gutter) * 2,
31639 x : x + (this.unitWidth + this.gutter) * 2,
31640 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31647 getVerticalFourBoxColPositions : function(x, y, box)
31651 if(box[0].size == 'xs'){
31660 y : y + (this.unitHeight + this.gutter) * 1
31665 y : y + (this.unitHeight + this.gutter) * 2
31669 x : x + (this.unitWidth + this.gutter) * 1,
31683 x : x + (this.unitWidth + this.gutter) * 2,
31688 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31689 y : y + (this.unitHeight + this.gutter) * 1
31693 x : x + (this.unitWidth + this.gutter) * 2,
31694 y : y + (this.unitWidth + this.gutter) * 2
31701 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31705 if(box[0].size == 'md-left'){
31707 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31714 if(box[0].size == 'md-right'){
31716 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31717 y : minY + (this.unitWidth + this.gutter) * 1
31723 var rand = Math.floor(Math.random() * (4 - box[0].y));
31726 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31727 y : minY + (this.unitWidth + this.gutter) * rand
31734 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31738 if(box[0].size == 'xs'){
31741 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31746 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31747 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31755 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31760 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31761 y : minY + (this.unitWidth + this.gutter) * 2
31768 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31772 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31775 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31780 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31781 y : minY + (this.unitWidth + this.gutter) * 1
31785 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31786 y : minY + (this.unitWidth + this.gutter) * 2
31793 if(box[0].size == 'xs' && box[1].size == 'xs'){
31796 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31801 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31806 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31807 y : minY + (this.unitWidth + this.gutter) * 1
31815 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31820 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31821 y : minY + (this.unitWidth + this.gutter) * 2
31825 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31826 y : minY + (this.unitWidth + this.gutter) * 2
31833 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31837 if(box[0].size == 'xs'){
31840 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
31850 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),
31855 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31856 y : minY + (this.unitWidth + this.gutter) * 1
31864 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31869 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
31875 y : minY + (this.unitWidth + this.gutter) * 2
31879 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),
31880 y : minY + (this.unitWidth + this.gutter) * 2
31888 * remove a Masonry Brick
31889 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31891 removeBrick : function(brick_id)
31897 for (var i = 0; i<this.bricks.length; i++) {
31898 if (this.bricks[i].id == brick_id) {
31899 this.bricks.splice(i,1);
31900 this.el.dom.removeChild(Roo.get(brick_id).dom);
31907 * adds a Masonry Brick
31908 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31910 addBrick : function(cfg)
31912 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31913 //this.register(cn);
31914 cn.parentId = this.id;
31915 cn.onRender(this.el, null);
31920 * register a Masonry Brick
31921 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31924 register : function(brick)
31926 this.bricks.push(brick);
31927 brick.masonryId = this.id;
31931 * clear all the Masonry Brick
31933 clearAll : function()
31936 //this.getChildContainer().dom.innerHTML = "";
31937 this.el.dom.innerHTML = '';
31940 getSelected : function()
31942 if (!this.selectedBrick) {
31946 return this.selectedBrick;
31950 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31954 * register a Masonry Layout
31955 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31958 register : function(layout)
31960 this.groups[layout.id] = layout;
31963 * fetch a Masonry Layout based on the masonry layout ID
31964 * @param {string} the masonry layout to add
31965 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31968 get: function(layout_id) {
31969 if (typeof(this.groups[layout_id]) == 'undefined') {
31972 return this.groups[layout_id] ;
31984 * http://masonry.desandro.com
31986 * The idea is to render all the bricks based on vertical width...
31988 * The original code extends 'outlayer' - we might need to use that....
31994 * @class Roo.bootstrap.LayoutMasonryAuto
31995 * @extends Roo.bootstrap.Component
31996 * Bootstrap Layout Masonry class
31999 * Create a new Element
32000 * @param {Object} config The config object
32003 Roo.bootstrap.LayoutMasonryAuto = function(config){
32004 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32007 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32010 * @cfg {Boolean} isFitWidth - resize the width..
32012 isFitWidth : false, // options..
32014 * @cfg {Boolean} isOriginLeft = left align?
32016 isOriginLeft : true,
32018 * @cfg {Boolean} isOriginTop = top align?
32020 isOriginTop : false,
32022 * @cfg {Boolean} isLayoutInstant = no animation?
32024 isLayoutInstant : false, // needed?
32026 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32028 isResizingContainer : true,
32030 * @cfg {Number} columnWidth width of the columns
32036 * @cfg {Number} maxCols maximum number of columns
32041 * @cfg {Number} padHeight padding below box..
32047 * @cfg {Boolean} isAutoInitial defalut true
32050 isAutoInitial : true,
32056 initialColumnWidth : 0,
32057 currentSize : null,
32059 colYs : null, // array.
32066 bricks: null, //CompositeElement
32067 cols : 0, // array?
32068 // element : null, // wrapped now this.el
32069 _isLayoutInited : null,
32072 getAutoCreate : function(){
32076 cls: 'blog-masonary-wrapper ' + this.cls,
32078 cls : 'mas-boxes masonary'
32085 getChildContainer: function( )
32087 if (this.boxesEl) {
32088 return this.boxesEl;
32091 this.boxesEl = this.el.select('.mas-boxes').first();
32093 return this.boxesEl;
32097 initEvents : function()
32101 if(this.isAutoInitial){
32102 Roo.log('hook children rendered');
32103 this.on('childrenrendered', function() {
32104 Roo.log('children rendered');
32111 initial : function()
32113 this.reloadItems();
32115 this.currentSize = this.el.getBox(true);
32117 /// was window resize... - let's see if this works..
32118 Roo.EventManager.onWindowResize(this.resize, this);
32120 if(!this.isAutoInitial){
32125 this.layout.defer(500,this);
32128 reloadItems: function()
32130 this.bricks = this.el.select('.masonry-brick', true);
32132 this.bricks.each(function(b) {
32133 //Roo.log(b.getSize());
32134 if (!b.attr('originalwidth')) {
32135 b.attr('originalwidth', b.getSize().width);
32140 Roo.log(this.bricks.elements.length);
32143 resize : function()
32146 var cs = this.el.getBox(true);
32148 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32149 Roo.log("no change in with or X");
32152 this.currentSize = cs;
32156 layout : function()
32159 this._resetLayout();
32160 //this._manageStamps();
32162 // don't animate first layout
32163 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32164 this.layoutItems( isInstant );
32166 // flag for initalized
32167 this._isLayoutInited = true;
32170 layoutItems : function( isInstant )
32172 //var items = this._getItemsForLayout( this.items );
32173 // original code supports filtering layout items.. we just ignore it..
32175 this._layoutItems( this.bricks , isInstant );
32177 this._postLayout();
32179 _layoutItems : function ( items , isInstant)
32181 //this.fireEvent( 'layout', this, items );
32184 if ( !items || !items.elements.length ) {
32185 // no items, emit event with empty array
32190 items.each(function(item) {
32191 Roo.log("layout item");
32193 // get x/y object from method
32194 var position = this._getItemLayoutPosition( item );
32196 position.item = item;
32197 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32198 queue.push( position );
32201 this._processLayoutQueue( queue );
32203 /** Sets position of item in DOM
32204 * @param {Element} item
32205 * @param {Number} x - horizontal position
32206 * @param {Number} y - vertical position
32207 * @param {Boolean} isInstant - disables transitions
32209 _processLayoutQueue : function( queue )
32211 for ( var i=0, len = queue.length; i < len; i++ ) {
32212 var obj = queue[i];
32213 obj.item.position('absolute');
32214 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32220 * Any logic you want to do after each layout,
32221 * i.e. size the container
32223 _postLayout : function()
32225 this.resizeContainer();
32228 resizeContainer : function()
32230 if ( !this.isResizingContainer ) {
32233 var size = this._getContainerSize();
32235 this.el.setSize(size.width,size.height);
32236 this.boxesEl.setSize(size.width,size.height);
32242 _resetLayout : function()
32244 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32245 this.colWidth = this.el.getWidth();
32246 //this.gutter = this.el.getWidth();
32248 this.measureColumns();
32254 this.colYs.push( 0 );
32260 measureColumns : function()
32262 this.getContainerWidth();
32263 // if columnWidth is 0, default to outerWidth of first item
32264 if ( !this.columnWidth ) {
32265 var firstItem = this.bricks.first();
32266 Roo.log(firstItem);
32267 this.columnWidth = this.containerWidth;
32268 if (firstItem && firstItem.attr('originalwidth') ) {
32269 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32271 // columnWidth fall back to item of first element
32272 Roo.log("set column width?");
32273 this.initialColumnWidth = this.columnWidth ;
32275 // if first elem has no width, default to size of container
32280 if (this.initialColumnWidth) {
32281 this.columnWidth = this.initialColumnWidth;
32286 // column width is fixed at the top - however if container width get's smaller we should
32289 // this bit calcs how man columns..
32291 var columnWidth = this.columnWidth += this.gutter;
32293 // calculate columns
32294 var containerWidth = this.containerWidth + this.gutter;
32296 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32297 // fix rounding errors, typically with gutters
32298 var excess = columnWidth - containerWidth % columnWidth;
32301 // if overshoot is less than a pixel, round up, otherwise floor it
32302 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32303 cols = Math[ mathMethod ]( cols );
32304 this.cols = Math.max( cols, 1 );
32305 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32307 // padding positioning..
32308 var totalColWidth = this.cols * this.columnWidth;
32309 var padavail = this.containerWidth - totalColWidth;
32310 // so for 2 columns - we need 3 'pads'
32312 var padNeeded = (1+this.cols) * this.padWidth;
32314 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32316 this.columnWidth += padExtra
32317 //this.padWidth = Math.floor(padavail / ( this.cols));
32319 // adjust colum width so that padding is fixed??
32321 // we have 3 columns ... total = width * 3
32322 // we have X left over... that should be used by
32324 //if (this.expandC) {
32332 getContainerWidth : function()
32334 /* // container is parent if fit width
32335 var container = this.isFitWidth ? this.element.parentNode : this.element;
32336 // check that this.size and size are there
32337 // IE8 triggers resize on body size change, so they might not be
32339 var size = getSize( container ); //FIXME
32340 this.containerWidth = size && size.innerWidth; //FIXME
32343 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32347 _getItemLayoutPosition : function( item ) // what is item?
32349 // we resize the item to our columnWidth..
32351 item.setWidth(this.columnWidth);
32352 item.autoBoxAdjust = false;
32354 var sz = item.getSize();
32356 // how many columns does this brick span
32357 var remainder = this.containerWidth % this.columnWidth;
32359 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32360 // round if off by 1 pixel, otherwise use ceil
32361 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32362 colSpan = Math.min( colSpan, this.cols );
32364 // normally this should be '1' as we dont' currently allow multi width columns..
32366 var colGroup = this._getColGroup( colSpan );
32367 // get the minimum Y value from the columns
32368 var minimumY = Math.min.apply( Math, colGroup );
32369 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32371 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32373 // position the brick
32375 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32376 y: this.currentSize.y + minimumY + this.padHeight
32380 // apply setHeight to necessary columns
32381 var setHeight = minimumY + sz.height + this.padHeight;
32382 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32384 var setSpan = this.cols + 1 - colGroup.length;
32385 for ( var i = 0; i < setSpan; i++ ) {
32386 this.colYs[ shortColIndex + i ] = setHeight ;
32393 * @param {Number} colSpan - number of columns the element spans
32394 * @returns {Array} colGroup
32396 _getColGroup : function( colSpan )
32398 if ( colSpan < 2 ) {
32399 // if brick spans only one column, use all the column Ys
32404 // how many different places could this brick fit horizontally
32405 var groupCount = this.cols + 1 - colSpan;
32406 // for each group potential horizontal position
32407 for ( var i = 0; i < groupCount; i++ ) {
32408 // make an array of colY values for that one group
32409 var groupColYs = this.colYs.slice( i, i + colSpan );
32410 // and get the max value of the array
32411 colGroup[i] = Math.max.apply( Math, groupColYs );
32416 _manageStamp : function( stamp )
32418 var stampSize = stamp.getSize();
32419 var offset = stamp.getBox();
32420 // get the columns that this stamp affects
32421 var firstX = this.isOriginLeft ? offset.x : offset.right;
32422 var lastX = firstX + stampSize.width;
32423 var firstCol = Math.floor( firstX / this.columnWidth );
32424 firstCol = Math.max( 0, firstCol );
32426 var lastCol = Math.floor( lastX / this.columnWidth );
32427 // lastCol should not go over if multiple of columnWidth #425
32428 lastCol -= lastX % this.columnWidth ? 0 : 1;
32429 lastCol = Math.min( this.cols - 1, lastCol );
32431 // set colYs to bottom of the stamp
32432 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32435 for ( var i = firstCol; i <= lastCol; i++ ) {
32436 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32441 _getContainerSize : function()
32443 this.maxY = Math.max.apply( Math, this.colYs );
32448 if ( this.isFitWidth ) {
32449 size.width = this._getContainerFitWidth();
32455 _getContainerFitWidth : function()
32457 var unusedCols = 0;
32458 // count unused columns
32461 if ( this.colYs[i] !== 0 ) {
32466 // fit container to columns that have been used
32467 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32470 needsResizeLayout : function()
32472 var previousWidth = this.containerWidth;
32473 this.getContainerWidth();
32474 return previousWidth !== this.containerWidth;
32489 * @class Roo.bootstrap.MasonryBrick
32490 * @extends Roo.bootstrap.Component
32491 * Bootstrap MasonryBrick class
32494 * Create a new MasonryBrick
32495 * @param {Object} config The config object
32498 Roo.bootstrap.MasonryBrick = function(config){
32500 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32502 Roo.bootstrap.MasonryBrick.register(this);
32508 * When a MasonryBrick is clcik
32509 * @param {Roo.bootstrap.MasonryBrick} this
32510 * @param {Roo.EventObject} e
32516 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32519 * @cfg {String} title
32523 * @cfg {String} html
32527 * @cfg {String} bgimage
32531 * @cfg {String} videourl
32535 * @cfg {String} cls
32539 * @cfg {String} href
32543 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32548 * @cfg {String} placetitle (center|bottom)
32553 * @cfg {Boolean} isFitContainer defalut true
32555 isFitContainer : true,
32558 * @cfg {Boolean} preventDefault defalut false
32560 preventDefault : false,
32563 * @cfg {Boolean} inverse defalut false
32565 maskInverse : false,
32567 getAutoCreate : function()
32569 if(!this.isFitContainer){
32570 return this.getSplitAutoCreate();
32573 var cls = 'masonry-brick masonry-brick-full';
32575 if(this.href.length){
32576 cls += ' masonry-brick-link';
32579 if(this.bgimage.length){
32580 cls += ' masonry-brick-image';
32583 if(this.maskInverse){
32584 cls += ' mask-inverse';
32587 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32588 cls += ' enable-mask';
32592 cls += ' masonry-' + this.size + '-brick';
32595 if(this.placetitle.length){
32597 switch (this.placetitle) {
32599 cls += ' masonry-center-title';
32602 cls += ' masonry-bottom-title';
32609 if(!this.html.length && !this.bgimage.length){
32610 cls += ' masonry-center-title';
32613 if(!this.html.length && this.bgimage.length){
32614 cls += ' masonry-bottom-title';
32619 cls += ' ' + this.cls;
32623 tag: (this.href.length) ? 'a' : 'div',
32628 cls: 'masonry-brick-mask'
32632 cls: 'masonry-brick-paragraph',
32638 if(this.href.length){
32639 cfg.href = this.href;
32642 var cn = cfg.cn[1].cn;
32644 if(this.title.length){
32647 cls: 'masonry-brick-title',
32652 if(this.html.length){
32655 cls: 'masonry-brick-text',
32660 if (!this.title.length && !this.html.length) {
32661 cfg.cn[1].cls += ' hide';
32664 if(this.bgimage.length){
32667 cls: 'masonry-brick-image-view',
32672 if(this.videourl.length){
32673 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32674 // youtube support only?
32677 cls: 'masonry-brick-image-view',
32680 allowfullscreen : true
32688 getSplitAutoCreate : function()
32690 var cls = 'masonry-brick masonry-brick-split';
32692 if(this.href.length){
32693 cls += ' masonry-brick-link';
32696 if(this.bgimage.length){
32697 cls += ' masonry-brick-image';
32701 cls += ' masonry-' + this.size + '-brick';
32704 switch (this.placetitle) {
32706 cls += ' masonry-center-title';
32709 cls += ' masonry-bottom-title';
32712 if(!this.bgimage.length){
32713 cls += ' masonry-center-title';
32716 if(this.bgimage.length){
32717 cls += ' masonry-bottom-title';
32723 cls += ' ' + this.cls;
32727 tag: (this.href.length) ? 'a' : 'div',
32732 cls: 'masonry-brick-split-head',
32736 cls: 'masonry-brick-paragraph',
32743 cls: 'masonry-brick-split-body',
32749 if(this.href.length){
32750 cfg.href = this.href;
32753 if(this.title.length){
32754 cfg.cn[0].cn[0].cn.push({
32756 cls: 'masonry-brick-title',
32761 if(this.html.length){
32762 cfg.cn[1].cn.push({
32764 cls: 'masonry-brick-text',
32769 if(this.bgimage.length){
32770 cfg.cn[0].cn.push({
32772 cls: 'masonry-brick-image-view',
32777 if(this.videourl.length){
32778 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32779 // youtube support only?
32780 cfg.cn[0].cn.cn.push({
32782 cls: 'masonry-brick-image-view',
32785 allowfullscreen : true
32792 initEvents: function()
32794 switch (this.size) {
32827 this.el.on('touchstart', this.onTouchStart, this);
32828 this.el.on('touchmove', this.onTouchMove, this);
32829 this.el.on('touchend', this.onTouchEnd, this);
32830 this.el.on('contextmenu', this.onContextMenu, this);
32832 this.el.on('mouseenter' ,this.enter, this);
32833 this.el.on('mouseleave', this.leave, this);
32834 this.el.on('click', this.onClick, this);
32837 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32838 this.parent().bricks.push(this);
32843 onClick: function(e, el)
32845 var time = this.endTimer - this.startTimer;
32846 // Roo.log(e.preventDefault());
32849 e.preventDefault();
32854 if(!this.preventDefault){
32858 e.preventDefault();
32860 if (this.activeClass != '') {
32861 this.selectBrick();
32864 this.fireEvent('click', this, e);
32867 enter: function(e, el)
32869 e.preventDefault();
32871 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32875 if(this.bgimage.length && this.html.length){
32876 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32880 leave: function(e, el)
32882 e.preventDefault();
32884 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32888 if(this.bgimage.length && this.html.length){
32889 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32893 onTouchStart: function(e, el)
32895 // e.preventDefault();
32897 this.touchmoved = false;
32899 if(!this.isFitContainer){
32903 if(!this.bgimage.length || !this.html.length){
32907 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32909 this.timer = new Date().getTime();
32913 onTouchMove: function(e, el)
32915 this.touchmoved = true;
32918 onContextMenu : function(e,el)
32920 e.preventDefault();
32921 e.stopPropagation();
32925 onTouchEnd: function(e, el)
32927 // e.preventDefault();
32929 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32936 if(!this.bgimage.length || !this.html.length){
32938 if(this.href.length){
32939 window.location.href = this.href;
32945 if(!this.isFitContainer){
32949 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32951 window.location.href = this.href;
32954 //selection on single brick only
32955 selectBrick : function() {
32957 if (!this.parentId) {
32961 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32962 var index = m.selectedBrick.indexOf(this.id);
32965 m.selectedBrick.splice(index,1);
32966 this.el.removeClass(this.activeClass);
32970 for(var i = 0; i < m.selectedBrick.length; i++) {
32971 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32972 b.el.removeClass(b.activeClass);
32975 m.selectedBrick = [];
32977 m.selectedBrick.push(this.id);
32978 this.el.addClass(this.activeClass);
32982 isSelected : function(){
32983 return this.el.hasClass(this.activeClass);
32988 Roo.apply(Roo.bootstrap.MasonryBrick, {
32991 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32993 * register a Masonry Brick
32994 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32997 register : function(brick)
32999 //this.groups[brick.id] = brick;
33000 this.groups.add(brick.id, brick);
33003 * fetch a masonry brick based on the masonry brick ID
33004 * @param {string} the masonry brick to add
33005 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33008 get: function(brick_id)
33010 // if (typeof(this.groups[brick_id]) == 'undefined') {
33013 // return this.groups[brick_id] ;
33015 if(this.groups.key(brick_id)) {
33016 return this.groups.key(brick_id);
33034 * @class Roo.bootstrap.Brick
33035 * @extends Roo.bootstrap.Component
33036 * Bootstrap Brick class
33039 * Create a new Brick
33040 * @param {Object} config The config object
33043 Roo.bootstrap.Brick = function(config){
33044 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33050 * When a Brick is click
33051 * @param {Roo.bootstrap.Brick} this
33052 * @param {Roo.EventObject} e
33058 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33061 * @cfg {String} title
33065 * @cfg {String} html
33069 * @cfg {String} bgimage
33073 * @cfg {String} cls
33077 * @cfg {String} href
33081 * @cfg {String} video
33085 * @cfg {Boolean} square
33089 getAutoCreate : function()
33091 var cls = 'roo-brick';
33093 if(this.href.length){
33094 cls += ' roo-brick-link';
33097 if(this.bgimage.length){
33098 cls += ' roo-brick-image';
33101 if(!this.html.length && !this.bgimage.length){
33102 cls += ' roo-brick-center-title';
33105 if(!this.html.length && this.bgimage.length){
33106 cls += ' roo-brick-bottom-title';
33110 cls += ' ' + this.cls;
33114 tag: (this.href.length) ? 'a' : 'div',
33119 cls: 'roo-brick-paragraph',
33125 if(this.href.length){
33126 cfg.href = this.href;
33129 var cn = cfg.cn[0].cn;
33131 if(this.title.length){
33134 cls: 'roo-brick-title',
33139 if(this.html.length){
33142 cls: 'roo-brick-text',
33149 if(this.bgimage.length){
33152 cls: 'roo-brick-image-view',
33160 initEvents: function()
33162 if(this.title.length || this.html.length){
33163 this.el.on('mouseenter' ,this.enter, this);
33164 this.el.on('mouseleave', this.leave, this);
33167 Roo.EventManager.onWindowResize(this.resize, this);
33169 if(this.bgimage.length){
33170 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33171 this.imageEl.on('load', this.onImageLoad, this);
33178 onImageLoad : function()
33183 resize : function()
33185 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33187 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33189 if(this.bgimage.length){
33190 var image = this.el.select('.roo-brick-image-view', true).first();
33192 image.setWidth(paragraph.getWidth());
33195 image.setHeight(paragraph.getWidth());
33198 this.el.setHeight(image.getHeight());
33199 paragraph.setHeight(image.getHeight());
33205 enter: function(e, el)
33207 e.preventDefault();
33209 if(this.bgimage.length){
33210 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33211 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33215 leave: function(e, el)
33217 e.preventDefault();
33219 if(this.bgimage.length){
33220 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33221 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33236 * @class Roo.bootstrap.NumberField
33237 * @extends Roo.bootstrap.Input
33238 * Bootstrap NumberField class
33244 * Create a new NumberField
33245 * @param {Object} config The config object
33248 Roo.bootstrap.NumberField = function(config){
33249 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33252 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33255 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33257 allowDecimals : true,
33259 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33261 decimalSeparator : ".",
33263 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33265 decimalPrecision : 2,
33267 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33269 allowNegative : true,
33272 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33276 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33278 minValue : Number.NEGATIVE_INFINITY,
33280 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33282 maxValue : Number.MAX_VALUE,
33284 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33286 minText : "The minimum value for this field is {0}",
33288 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33290 maxText : "The maximum value for this field is {0}",
33292 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33293 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33295 nanText : "{0} is not a valid number",
33297 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33299 thousandsDelimiter : false,
33301 * @cfg {String} valueAlign alignment of value
33303 valueAlign : "left",
33305 getAutoCreate : function()
33307 var hiddenInput = {
33311 cls: 'hidden-number-input'
33315 hiddenInput.name = this.name;
33320 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33322 this.name = hiddenInput.name;
33324 if(cfg.cn.length > 0) {
33325 cfg.cn.push(hiddenInput);
33332 initEvents : function()
33334 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33336 var allowed = "0123456789";
33338 if(this.allowDecimals){
33339 allowed += this.decimalSeparator;
33342 if(this.allowNegative){
33346 if(this.thousandsDelimiter) {
33350 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33352 var keyPress = function(e){
33354 var k = e.getKey();
33356 var c = e.getCharCode();
33359 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33360 allowed.indexOf(String.fromCharCode(c)) === -1
33366 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33370 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33375 this.el.on("keypress", keyPress, this);
33378 validateValue : function(value)
33381 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33385 var num = this.parseValue(value);
33388 this.markInvalid(String.format(this.nanText, value));
33392 if(num < this.minValue){
33393 this.markInvalid(String.format(this.minText, this.minValue));
33397 if(num > this.maxValue){
33398 this.markInvalid(String.format(this.maxText, this.maxValue));
33405 getValue : function()
33407 var v = this.hiddenEl().getValue();
33409 return this.fixPrecision(this.parseValue(v));
33412 parseValue : function(value)
33414 if(this.thousandsDelimiter) {
33416 r = new RegExp(",", "g");
33417 value = value.replace(r, "");
33420 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33421 return isNaN(value) ? '' : value;
33424 fixPrecision : function(value)
33426 if(this.thousandsDelimiter) {
33428 r = new RegExp(",", "g");
33429 value = value.replace(r, "");
33432 var nan = isNaN(value);
33434 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33435 return nan ? '' : value;
33437 return parseFloat(value).toFixed(this.decimalPrecision);
33440 setValue : function(v)
33442 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33448 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33450 this.inputEl().dom.value = (v == '') ? '' :
33451 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33453 if(!this.allowZero && v === '0') {
33454 this.hiddenEl().dom.value = '';
33455 this.inputEl().dom.value = '';
33462 decimalPrecisionFcn : function(v)
33464 return Math.floor(v);
33467 beforeBlur : function()
33469 var v = this.parseValue(this.getRawValue());
33471 if(v || v === 0 || v === ''){
33476 hiddenEl : function()
33478 return this.el.select('input.hidden-number-input',true).first();
33490 * @class Roo.bootstrap.DocumentSlider
33491 * @extends Roo.bootstrap.Component
33492 * Bootstrap DocumentSlider class
33495 * Create a new DocumentViewer
33496 * @param {Object} config The config object
33499 Roo.bootstrap.DocumentSlider = function(config){
33500 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33507 * Fire after initEvent
33508 * @param {Roo.bootstrap.DocumentSlider} this
33513 * Fire after update
33514 * @param {Roo.bootstrap.DocumentSlider} this
33520 * @param {Roo.bootstrap.DocumentSlider} this
33526 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33532 getAutoCreate : function()
33536 cls : 'roo-document-slider',
33540 cls : 'roo-document-slider-header',
33544 cls : 'roo-document-slider-header-title'
33550 cls : 'roo-document-slider-body',
33554 cls : 'roo-document-slider-prev',
33558 cls : 'fa fa-chevron-left'
33564 cls : 'roo-document-slider-thumb',
33568 cls : 'roo-document-slider-image'
33574 cls : 'roo-document-slider-next',
33578 cls : 'fa fa-chevron-right'
33590 initEvents : function()
33592 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33593 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33595 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33596 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33598 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33599 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33601 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33602 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33604 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33605 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33607 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33608 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33610 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33611 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33613 this.thumbEl.on('click', this.onClick, this);
33615 this.prevIndicator.on('click', this.prev, this);
33617 this.nextIndicator.on('click', this.next, this);
33621 initial : function()
33623 if(this.files.length){
33624 this.indicator = 1;
33628 this.fireEvent('initial', this);
33631 update : function()
33633 this.imageEl.attr('src', this.files[this.indicator - 1]);
33635 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33637 this.prevIndicator.show();
33639 if(this.indicator == 1){
33640 this.prevIndicator.hide();
33643 this.nextIndicator.show();
33645 if(this.indicator == this.files.length){
33646 this.nextIndicator.hide();
33649 this.thumbEl.scrollTo('top');
33651 this.fireEvent('update', this);
33654 onClick : function(e)
33656 e.preventDefault();
33658 this.fireEvent('click', this);
33663 e.preventDefault();
33665 this.indicator = Math.max(1, this.indicator - 1);
33672 e.preventDefault();
33674 this.indicator = Math.min(this.files.length, this.indicator + 1);
33688 * @class Roo.bootstrap.RadioSet
33689 * @extends Roo.bootstrap.Input
33690 * Bootstrap RadioSet class
33691 * @cfg {String} indicatorpos (left|right) default left
33692 * @cfg {Boolean} inline (true|false) inline the element (default true)
33693 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33695 * Create a new RadioSet
33696 * @param {Object} config The config object
33699 Roo.bootstrap.RadioSet = function(config){
33701 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33705 Roo.bootstrap.RadioSet.register(this);
33710 * Fires when the element is checked or unchecked.
33711 * @param {Roo.bootstrap.RadioSet} this This radio
33712 * @param {Roo.bootstrap.Radio} item The checked item
33717 * Fires when the element is click.
33718 * @param {Roo.bootstrap.RadioSet} this This radio set
33719 * @param {Roo.bootstrap.Radio} item The checked item
33720 * @param {Roo.EventObject} e The event object
33727 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33735 indicatorpos : 'left',
33737 getAutoCreate : function()
33741 cls : 'roo-radio-set-label',
33745 html : this.fieldLabel
33750 if(this.indicatorpos == 'left'){
33753 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33754 tooltip : 'This field is required'
33759 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33760 tooltip : 'This field is required'
33766 cls : 'roo-radio-set-items'
33769 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33771 if (align === 'left' && this.fieldLabel.length) {
33774 cls : "roo-radio-set-right",
33780 if(this.labelWidth > 12){
33781 label.style = "width: " + this.labelWidth + 'px';
33784 if(this.labelWidth < 13 && this.labelmd == 0){
33785 this.labelmd = this.labelWidth;
33788 if(this.labellg > 0){
33789 label.cls += ' col-lg-' + this.labellg;
33790 items.cls += ' col-lg-' + (12 - this.labellg);
33793 if(this.labelmd > 0){
33794 label.cls += ' col-md-' + this.labelmd;
33795 items.cls += ' col-md-' + (12 - this.labelmd);
33798 if(this.labelsm > 0){
33799 label.cls += ' col-sm-' + this.labelsm;
33800 items.cls += ' col-sm-' + (12 - this.labelsm);
33803 if(this.labelxs > 0){
33804 label.cls += ' col-xs-' + this.labelxs;
33805 items.cls += ' col-xs-' + (12 - this.labelxs);
33811 cls : 'roo-radio-set',
33815 cls : 'roo-radio-set-input',
33818 value : this.value ? this.value : ''
33825 if(this.weight.length){
33826 cfg.cls += ' roo-radio-' + this.weight;
33830 cfg.cls += ' roo-radio-set-inline';
33834 ['xs','sm','md','lg'].map(function(size){
33835 if (settings[size]) {
33836 cfg.cls += ' col-' + size + '-' + settings[size];
33844 initEvents : function()
33846 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33847 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33849 if(!this.fieldLabel.length){
33850 this.labelEl.hide();
33853 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33854 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33856 this.indicator = this.indicatorEl();
33858 if(this.indicator){
33859 this.indicator.addClass('invisible');
33862 this.originalValue = this.getValue();
33866 inputEl: function ()
33868 return this.el.select('.roo-radio-set-input', true).first();
33871 getChildContainer : function()
33873 return this.itemsEl;
33876 register : function(item)
33878 this.radioes.push(item);
33882 validate : function()
33884 if(this.getVisibilityEl().hasClass('hidden')){
33890 Roo.each(this.radioes, function(i){
33899 if(this.allowBlank) {
33903 if(this.disabled || valid){
33908 this.markInvalid();
33913 markValid : function()
33915 if(this.labelEl.isVisible(true)){
33916 this.indicatorEl().removeClass('visible');
33917 this.indicatorEl().addClass('invisible');
33920 this.el.removeClass([this.invalidClass, this.validClass]);
33921 this.el.addClass(this.validClass);
33923 this.fireEvent('valid', this);
33926 markInvalid : function(msg)
33928 if(this.allowBlank || this.disabled){
33932 if(this.labelEl.isVisible(true)){
33933 this.indicatorEl().removeClass('invisible');
33934 this.indicatorEl().addClass('visible');
33937 this.el.removeClass([this.invalidClass, this.validClass]);
33938 this.el.addClass(this.invalidClass);
33940 this.fireEvent('invalid', this, msg);
33944 setValue : function(v, suppressEvent)
33946 if(this.value === v){
33953 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33956 Roo.each(this.radioes, function(i){
33958 i.el.removeClass('checked');
33961 Roo.each(this.radioes, function(i){
33963 if(i.value === v || i.value.toString() === v.toString()){
33965 i.el.addClass('checked');
33967 if(suppressEvent !== true){
33968 this.fireEvent('check', this, i);
33979 clearInvalid : function(){
33981 if(!this.el || this.preventMark){
33985 this.el.removeClass([this.invalidClass]);
33987 this.fireEvent('valid', this);
33992 Roo.apply(Roo.bootstrap.RadioSet, {
33996 register : function(set)
33998 this.groups[set.name] = set;
34001 get: function(name)
34003 if (typeof(this.groups[name]) == 'undefined') {
34007 return this.groups[name] ;
34013 * Ext JS Library 1.1.1
34014 * Copyright(c) 2006-2007, Ext JS, LLC.
34016 * Originally Released Under LGPL - original licence link has changed is not relivant.
34019 * <script type="text/javascript">
34024 * @class Roo.bootstrap.SplitBar
34025 * @extends Roo.util.Observable
34026 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34030 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34031 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34032 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34033 split.minSize = 100;
34034 split.maxSize = 600;
34035 split.animate = true;
34036 split.on('moved', splitterMoved);
34039 * Create a new SplitBar
34040 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34041 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34042 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34043 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34044 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34045 position of the SplitBar).
34047 Roo.bootstrap.SplitBar = function(cfg){
34052 // dragElement : elm
34053 // resizingElement: el,
34055 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34056 // placement : Roo.bootstrap.SplitBar.LEFT ,
34057 // existingProxy ???
34060 this.el = Roo.get(cfg.dragElement, true);
34061 this.el.dom.unselectable = "on";
34063 this.resizingEl = Roo.get(cfg.resizingElement, true);
34067 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34068 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34071 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34074 * The minimum size of the resizing element. (Defaults to 0)
34080 * The maximum size of the resizing element. (Defaults to 2000)
34083 this.maxSize = 2000;
34086 * Whether to animate the transition to the new size
34089 this.animate = false;
34092 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34095 this.useShim = false;
34100 if(!cfg.existingProxy){
34102 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34104 this.proxy = Roo.get(cfg.existingProxy).dom;
34107 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34110 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34113 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34116 this.dragSpecs = {};
34119 * @private The adapter to use to positon and resize elements
34121 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34122 this.adapter.init(this);
34124 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34126 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34127 this.el.addClass("roo-splitbar-h");
34130 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34131 this.el.addClass("roo-splitbar-v");
34137 * Fires when the splitter is moved (alias for {@link #event-moved})
34138 * @param {Roo.bootstrap.SplitBar} this
34139 * @param {Number} newSize the new width or height
34144 * Fires when the splitter is moved
34145 * @param {Roo.bootstrap.SplitBar} this
34146 * @param {Number} newSize the new width or height
34150 * @event beforeresize
34151 * Fires before the splitter is dragged
34152 * @param {Roo.bootstrap.SplitBar} this
34154 "beforeresize" : true,
34156 "beforeapply" : true
34159 Roo.util.Observable.call(this);
34162 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34163 onStartProxyDrag : function(x, y){
34164 this.fireEvent("beforeresize", this);
34166 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34168 o.enableDisplayMode("block");
34169 // all splitbars share the same overlay
34170 Roo.bootstrap.SplitBar.prototype.overlay = o;
34172 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34173 this.overlay.show();
34174 Roo.get(this.proxy).setDisplayed("block");
34175 var size = this.adapter.getElementSize(this);
34176 this.activeMinSize = this.getMinimumSize();;
34177 this.activeMaxSize = this.getMaximumSize();;
34178 var c1 = size - this.activeMinSize;
34179 var c2 = Math.max(this.activeMaxSize - size, 0);
34180 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34181 this.dd.resetConstraints();
34182 this.dd.setXConstraint(
34183 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34184 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34186 this.dd.setYConstraint(0, 0);
34188 this.dd.resetConstraints();
34189 this.dd.setXConstraint(0, 0);
34190 this.dd.setYConstraint(
34191 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34192 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34195 this.dragSpecs.startSize = size;
34196 this.dragSpecs.startPoint = [x, y];
34197 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34201 * @private Called after the drag operation by the DDProxy
34203 onEndProxyDrag : function(e){
34204 Roo.get(this.proxy).setDisplayed(false);
34205 var endPoint = Roo.lib.Event.getXY(e);
34207 this.overlay.hide();
34210 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34211 newSize = this.dragSpecs.startSize +
34212 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34213 endPoint[0] - this.dragSpecs.startPoint[0] :
34214 this.dragSpecs.startPoint[0] - endPoint[0]
34217 newSize = this.dragSpecs.startSize +
34218 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34219 endPoint[1] - this.dragSpecs.startPoint[1] :
34220 this.dragSpecs.startPoint[1] - endPoint[1]
34223 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34224 if(newSize != this.dragSpecs.startSize){
34225 if(this.fireEvent('beforeapply', this, newSize) !== false){
34226 this.adapter.setElementSize(this, newSize);
34227 this.fireEvent("moved", this, newSize);
34228 this.fireEvent("resize", this, newSize);
34234 * Get the adapter this SplitBar uses
34235 * @return The adapter object
34237 getAdapter : function(){
34238 return this.adapter;
34242 * Set the adapter this SplitBar uses
34243 * @param {Object} adapter A SplitBar adapter object
34245 setAdapter : function(adapter){
34246 this.adapter = adapter;
34247 this.adapter.init(this);
34251 * Gets the minimum size for the resizing element
34252 * @return {Number} The minimum size
34254 getMinimumSize : function(){
34255 return this.minSize;
34259 * Sets the minimum size for the resizing element
34260 * @param {Number} minSize The minimum size
34262 setMinimumSize : function(minSize){
34263 this.minSize = minSize;
34267 * Gets the maximum size for the resizing element
34268 * @return {Number} The maximum size
34270 getMaximumSize : function(){
34271 return this.maxSize;
34275 * Sets the maximum size for the resizing element
34276 * @param {Number} maxSize The maximum size
34278 setMaximumSize : function(maxSize){
34279 this.maxSize = maxSize;
34283 * Sets the initialize size for the resizing element
34284 * @param {Number} size The initial size
34286 setCurrentSize : function(size){
34287 var oldAnimate = this.animate;
34288 this.animate = false;
34289 this.adapter.setElementSize(this, size);
34290 this.animate = oldAnimate;
34294 * Destroy this splitbar.
34295 * @param {Boolean} removeEl True to remove the element
34297 destroy : function(removeEl){
34299 this.shim.remove();
34302 this.proxy.parentNode.removeChild(this.proxy);
34310 * @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.
34312 Roo.bootstrap.SplitBar.createProxy = function(dir){
34313 var proxy = new Roo.Element(document.createElement("div"));
34314 proxy.unselectable();
34315 var cls = 'roo-splitbar-proxy';
34316 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34317 document.body.appendChild(proxy.dom);
34322 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34323 * Default Adapter. It assumes the splitter and resizing element are not positioned
34324 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34326 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34329 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34330 // do nothing for now
34331 init : function(s){
34335 * Called before drag operations to get the current size of the resizing element.
34336 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34338 getElementSize : function(s){
34339 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34340 return s.resizingEl.getWidth();
34342 return s.resizingEl.getHeight();
34347 * Called after drag operations to set the size of the resizing element.
34348 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34349 * @param {Number} newSize The new size to set
34350 * @param {Function} onComplete A function to be invoked when resizing is complete
34352 setElementSize : function(s, newSize, onComplete){
34353 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34355 s.resizingEl.setWidth(newSize);
34357 onComplete(s, newSize);
34360 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34365 s.resizingEl.setHeight(newSize);
34367 onComplete(s, newSize);
34370 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34377 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34378 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34379 * Adapter that moves the splitter element to align with the resized sizing element.
34380 * Used with an absolute positioned SplitBar.
34381 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34382 * document.body, make sure you assign an id to the body element.
34384 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34385 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34386 this.container = Roo.get(container);
34389 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34390 init : function(s){
34391 this.basic.init(s);
34394 getElementSize : function(s){
34395 return this.basic.getElementSize(s);
34398 setElementSize : function(s, newSize, onComplete){
34399 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34402 moveSplitter : function(s){
34403 var yes = Roo.bootstrap.SplitBar;
34404 switch(s.placement){
34406 s.el.setX(s.resizingEl.getRight());
34409 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34412 s.el.setY(s.resizingEl.getBottom());
34415 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34422 * Orientation constant - Create a vertical SplitBar
34426 Roo.bootstrap.SplitBar.VERTICAL = 1;
34429 * Orientation constant - Create a horizontal SplitBar
34433 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34436 * Placement constant - The resizing element is to the left of the splitter element
34440 Roo.bootstrap.SplitBar.LEFT = 1;
34443 * Placement constant - The resizing element is to the right of the splitter element
34447 Roo.bootstrap.SplitBar.RIGHT = 2;
34450 * Placement constant - The resizing element is positioned above the splitter element
34454 Roo.bootstrap.SplitBar.TOP = 3;
34457 * Placement constant - The resizing element is positioned under splitter element
34461 Roo.bootstrap.SplitBar.BOTTOM = 4;
34462 Roo.namespace("Roo.bootstrap.layout");/*
34464 * Ext JS Library 1.1.1
34465 * Copyright(c) 2006-2007, Ext JS, LLC.
34467 * Originally Released Under LGPL - original licence link has changed is not relivant.
34470 * <script type="text/javascript">
34474 * @class Roo.bootstrap.layout.Manager
34475 * @extends Roo.bootstrap.Component
34476 * Base class for layout managers.
34478 Roo.bootstrap.layout.Manager = function(config)
34480 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34486 /** false to disable window resize monitoring @type Boolean */
34487 this.monitorWindowResize = true;
34492 * Fires when a layout is performed.
34493 * @param {Roo.LayoutManager} this
34497 * @event regionresized
34498 * Fires when the user resizes a region.
34499 * @param {Roo.LayoutRegion} region The resized region
34500 * @param {Number} newSize The new size (width for east/west, height for north/south)
34502 "regionresized" : true,
34504 * @event regioncollapsed
34505 * Fires when a region is collapsed.
34506 * @param {Roo.LayoutRegion} region The collapsed region
34508 "regioncollapsed" : true,
34510 * @event regionexpanded
34511 * Fires when a region is expanded.
34512 * @param {Roo.LayoutRegion} region The expanded region
34514 "regionexpanded" : true
34516 this.updating = false;
34519 this.el = Roo.get(config.el);
34525 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34530 monitorWindowResize : true,
34536 onRender : function(ct, position)
34539 this.el = Roo.get(ct);
34542 //this.fireEvent('render',this);
34546 initEvents: function()
34550 // ie scrollbar fix
34551 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34552 document.body.scroll = "no";
34553 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34554 this.el.position('relative');
34556 this.id = this.el.id;
34557 this.el.addClass("roo-layout-container");
34558 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34559 if(this.el.dom != document.body ) {
34560 this.el.on('resize', this.layout,this);
34561 this.el.on('show', this.layout,this);
34567 * Returns true if this layout is currently being updated
34568 * @return {Boolean}
34570 isUpdating : function(){
34571 return this.updating;
34575 * Suspend the LayoutManager from doing auto-layouts while
34576 * making multiple add or remove calls
34578 beginUpdate : function(){
34579 this.updating = true;
34583 * Restore auto-layouts and optionally disable the manager from performing a layout
34584 * @param {Boolean} noLayout true to disable a layout update
34586 endUpdate : function(noLayout){
34587 this.updating = false;
34593 layout: function(){
34597 onRegionResized : function(region, newSize){
34598 this.fireEvent("regionresized", region, newSize);
34602 onRegionCollapsed : function(region){
34603 this.fireEvent("regioncollapsed", region);
34606 onRegionExpanded : function(region){
34607 this.fireEvent("regionexpanded", region);
34611 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34612 * performs box-model adjustments.
34613 * @return {Object} The size as an object {width: (the width), height: (the height)}
34615 getViewSize : function()
34618 if(this.el.dom != document.body){
34619 size = this.el.getSize();
34621 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34623 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34624 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34629 * Returns the Element this layout is bound to.
34630 * @return {Roo.Element}
34632 getEl : function(){
34637 * Returns the specified region.
34638 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34639 * @return {Roo.LayoutRegion}
34641 getRegion : function(target){
34642 return this.regions[target.toLowerCase()];
34645 onWindowResize : function(){
34646 if(this.monitorWindowResize){
34653 * Ext JS Library 1.1.1
34654 * Copyright(c) 2006-2007, Ext JS, LLC.
34656 * Originally Released Under LGPL - original licence link has changed is not relivant.
34659 * <script type="text/javascript">
34662 * @class Roo.bootstrap.layout.Border
34663 * @extends Roo.bootstrap.layout.Manager
34664 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34665 * please see: examples/bootstrap/nested.html<br><br>
34667 <b>The container the layout is rendered into can be either the body element or any other element.
34668 If it is not the body element, the container needs to either be an absolute positioned element,
34669 or you will need to add "position:relative" to the css of the container. You will also need to specify
34670 the container size if it is not the body element.</b>
34673 * Create a new Border
34674 * @param {Object} config Configuration options
34676 Roo.bootstrap.layout.Border = function(config){
34677 config = config || {};
34678 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34682 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34683 if(config[region]){
34684 config[region].region = region;
34685 this.addRegion(config[region]);
34691 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34693 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34695 * Creates and adds a new region if it doesn't already exist.
34696 * @param {String} target The target region key (north, south, east, west or center).
34697 * @param {Object} config The regions config object
34698 * @return {BorderLayoutRegion} The new region
34700 addRegion : function(config)
34702 if(!this.regions[config.region]){
34703 var r = this.factory(config);
34704 this.bindRegion(r);
34706 return this.regions[config.region];
34710 bindRegion : function(r){
34711 this.regions[r.config.region] = r;
34713 r.on("visibilitychange", this.layout, this);
34714 r.on("paneladded", this.layout, this);
34715 r.on("panelremoved", this.layout, this);
34716 r.on("invalidated", this.layout, this);
34717 r.on("resized", this.onRegionResized, this);
34718 r.on("collapsed", this.onRegionCollapsed, this);
34719 r.on("expanded", this.onRegionExpanded, this);
34723 * Performs a layout update.
34725 layout : function()
34727 if(this.updating) {
34731 // render all the rebions if they have not been done alreayd?
34732 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34733 if(this.regions[region] && !this.regions[region].bodyEl){
34734 this.regions[region].onRender(this.el)
34738 var size = this.getViewSize();
34739 var w = size.width;
34740 var h = size.height;
34745 //var x = 0, y = 0;
34747 var rs = this.regions;
34748 var north = rs["north"];
34749 var south = rs["south"];
34750 var west = rs["west"];
34751 var east = rs["east"];
34752 var center = rs["center"];
34753 //if(this.hideOnLayout){ // not supported anymore
34754 //c.el.setStyle("display", "none");
34756 if(north && north.isVisible()){
34757 var b = north.getBox();
34758 var m = north.getMargins();
34759 b.width = w - (m.left+m.right);
34762 centerY = b.height + b.y + m.bottom;
34763 centerH -= centerY;
34764 north.updateBox(this.safeBox(b));
34766 if(south && south.isVisible()){
34767 var b = south.getBox();
34768 var m = south.getMargins();
34769 b.width = w - (m.left+m.right);
34771 var totalHeight = (b.height + m.top + m.bottom);
34772 b.y = h - totalHeight + m.top;
34773 centerH -= totalHeight;
34774 south.updateBox(this.safeBox(b));
34776 if(west && west.isVisible()){
34777 var b = west.getBox();
34778 var m = west.getMargins();
34779 b.height = centerH - (m.top+m.bottom);
34781 b.y = centerY + m.top;
34782 var totalWidth = (b.width + m.left + m.right);
34783 centerX += totalWidth;
34784 centerW -= totalWidth;
34785 west.updateBox(this.safeBox(b));
34787 if(east && east.isVisible()){
34788 var b = east.getBox();
34789 var m = east.getMargins();
34790 b.height = centerH - (m.top+m.bottom);
34791 var totalWidth = (b.width + m.left + m.right);
34792 b.x = w - totalWidth + m.left;
34793 b.y = centerY + m.top;
34794 centerW -= totalWidth;
34795 east.updateBox(this.safeBox(b));
34798 var m = center.getMargins();
34800 x: centerX + m.left,
34801 y: centerY + m.top,
34802 width: centerW - (m.left+m.right),
34803 height: centerH - (m.top+m.bottom)
34805 //if(this.hideOnLayout){
34806 //center.el.setStyle("display", "block");
34808 center.updateBox(this.safeBox(centerBox));
34811 this.fireEvent("layout", this);
34815 safeBox : function(box){
34816 box.width = Math.max(0, box.width);
34817 box.height = Math.max(0, box.height);
34822 * Adds a ContentPanel (or subclass) to this layout.
34823 * @param {String} target The target region key (north, south, east, west or center).
34824 * @param {Roo.ContentPanel} panel The panel to add
34825 * @return {Roo.ContentPanel} The added panel
34827 add : function(target, panel){
34829 target = target.toLowerCase();
34830 return this.regions[target].add(panel);
34834 * Remove a ContentPanel (or subclass) to this layout.
34835 * @param {String} target The target region key (north, south, east, west or center).
34836 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34837 * @return {Roo.ContentPanel} The removed panel
34839 remove : function(target, panel){
34840 target = target.toLowerCase();
34841 return this.regions[target].remove(panel);
34845 * Searches all regions for a panel with the specified id
34846 * @param {String} panelId
34847 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34849 findPanel : function(panelId){
34850 var rs = this.regions;
34851 for(var target in rs){
34852 if(typeof rs[target] != "function"){
34853 var p = rs[target].getPanel(panelId);
34863 * Searches all regions for a panel with the specified id and activates (shows) it.
34864 * @param {String/ContentPanel} panelId The panels id or the panel itself
34865 * @return {Roo.ContentPanel} The shown panel or null
34867 showPanel : function(panelId) {
34868 var rs = this.regions;
34869 for(var target in rs){
34870 var r = rs[target];
34871 if(typeof r != "function"){
34872 if(r.hasPanel(panelId)){
34873 return r.showPanel(panelId);
34881 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34882 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34885 restoreState : function(provider){
34887 provider = Roo.state.Manager;
34889 var sm = new Roo.LayoutStateManager();
34890 sm.init(this, provider);
34896 * Adds a xtype elements to the layout.
34900 xtype : 'ContentPanel',
34907 xtype : 'NestedLayoutPanel',
34913 items : [ ... list of content panels or nested layout panels.. ]
34917 * @param {Object} cfg Xtype definition of item to add.
34919 addxtype : function(cfg)
34921 // basically accepts a pannel...
34922 // can accept a layout region..!?!?
34923 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34926 // theory? children can only be panels??
34928 //if (!cfg.xtype.match(/Panel$/)) {
34933 if (typeof(cfg.region) == 'undefined') {
34934 Roo.log("Failed to add Panel, region was not set");
34938 var region = cfg.region;
34944 xitems = cfg.items;
34951 case 'Content': // ContentPanel (el, cfg)
34952 case 'Scroll': // ContentPanel (el, cfg)
34954 cfg.autoCreate = true;
34955 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34957 // var el = this.el.createChild();
34958 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34961 this.add(region, ret);
34965 case 'TreePanel': // our new panel!
34966 cfg.el = this.el.createChild();
34967 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34968 this.add(region, ret);
34973 // create a new Layout (which is a Border Layout...
34975 var clayout = cfg.layout;
34976 clayout.el = this.el.createChild();
34977 clayout.items = clayout.items || [];
34981 // replace this exitems with the clayout ones..
34982 xitems = clayout.items;
34984 // force background off if it's in center...
34985 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34986 cfg.background = false;
34988 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34991 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34992 //console.log('adding nested layout panel ' + cfg.toSource());
34993 this.add(region, ret);
34994 nb = {}; /// find first...
34999 // needs grid and region
35001 //var el = this.getRegion(region).el.createChild();
35003 *var el = this.el.createChild();
35004 // create the grid first...
35005 cfg.grid.container = el;
35006 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35009 if (region == 'center' && this.active ) {
35010 cfg.background = false;
35013 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35015 this.add(region, ret);
35017 if (cfg.background) {
35018 // render grid on panel activation (if panel background)
35019 ret.on('activate', function(gp) {
35020 if (!gp.grid.rendered) {
35021 // gp.grid.render(el);
35025 // cfg.grid.render(el);
35031 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35032 // it was the old xcomponent building that caused this before.
35033 // espeically if border is the top element in the tree.
35043 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35045 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35046 this.add(region, ret);
35050 throw "Can not add '" + cfg.xtype + "' to Border";
35056 this.beginUpdate();
35060 Roo.each(xitems, function(i) {
35061 region = nb && i.region ? i.region : false;
35063 var add = ret.addxtype(i);
35066 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35067 if (!i.background) {
35068 abn[region] = nb[region] ;
35075 // make the last non-background panel active..
35076 //if (nb) { Roo.log(abn); }
35079 for(var r in abn) {
35080 region = this.getRegion(r);
35082 // tried using nb[r], but it does not work..
35084 region.showPanel(abn[r]);
35095 factory : function(cfg)
35098 var validRegions = Roo.bootstrap.layout.Border.regions;
35100 var target = cfg.region;
35103 var r = Roo.bootstrap.layout;
35107 return new r.North(cfg);
35109 return new r.South(cfg);
35111 return new r.East(cfg);
35113 return new r.West(cfg);
35115 return new r.Center(cfg);
35117 throw 'Layout region "'+target+'" not supported.';
35124 * Ext JS Library 1.1.1
35125 * Copyright(c) 2006-2007, Ext JS, LLC.
35127 * Originally Released Under LGPL - original licence link has changed is not relivant.
35130 * <script type="text/javascript">
35134 * @class Roo.bootstrap.layout.Basic
35135 * @extends Roo.util.Observable
35136 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35137 * and does not have a titlebar, tabs or any other features. All it does is size and position
35138 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35139 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35140 * @cfg {string} region the region that it inhabits..
35141 * @cfg {bool} skipConfig skip config?
35145 Roo.bootstrap.layout.Basic = function(config){
35147 this.mgr = config.mgr;
35149 this.position = config.region;
35151 var skipConfig = config.skipConfig;
35155 * @scope Roo.BasicLayoutRegion
35159 * @event beforeremove
35160 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35161 * @param {Roo.LayoutRegion} this
35162 * @param {Roo.ContentPanel} panel The panel
35163 * @param {Object} e The cancel event object
35165 "beforeremove" : true,
35167 * @event invalidated
35168 * Fires when the layout for this region is changed.
35169 * @param {Roo.LayoutRegion} this
35171 "invalidated" : true,
35173 * @event visibilitychange
35174 * Fires when this region is shown or hidden
35175 * @param {Roo.LayoutRegion} this
35176 * @param {Boolean} visibility true or false
35178 "visibilitychange" : true,
35180 * @event paneladded
35181 * Fires when a panel is added.
35182 * @param {Roo.LayoutRegion} this
35183 * @param {Roo.ContentPanel} panel The panel
35185 "paneladded" : true,
35187 * @event panelremoved
35188 * Fires when a panel is removed.
35189 * @param {Roo.LayoutRegion} this
35190 * @param {Roo.ContentPanel} panel The panel
35192 "panelremoved" : true,
35194 * @event beforecollapse
35195 * Fires when this region before collapse.
35196 * @param {Roo.LayoutRegion} this
35198 "beforecollapse" : true,
35201 * Fires when this region is collapsed.
35202 * @param {Roo.LayoutRegion} this
35204 "collapsed" : true,
35207 * Fires when this region is expanded.
35208 * @param {Roo.LayoutRegion} this
35213 * Fires when this region is slid into view.
35214 * @param {Roo.LayoutRegion} this
35216 "slideshow" : true,
35219 * Fires when this region slides out of view.
35220 * @param {Roo.LayoutRegion} this
35222 "slidehide" : true,
35224 * @event panelactivated
35225 * Fires when a panel is activated.
35226 * @param {Roo.LayoutRegion} this
35227 * @param {Roo.ContentPanel} panel The activated panel
35229 "panelactivated" : true,
35232 * Fires when the user resizes this region.
35233 * @param {Roo.LayoutRegion} this
35234 * @param {Number} newSize The new size (width for east/west, height for north/south)
35238 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35239 this.panels = new Roo.util.MixedCollection();
35240 this.panels.getKey = this.getPanelId.createDelegate(this);
35242 this.activePanel = null;
35243 // ensure listeners are added...
35245 if (config.listeners || config.events) {
35246 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35247 listeners : config.listeners || {},
35248 events : config.events || {}
35252 if(skipConfig !== true){
35253 this.applyConfig(config);
35257 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35259 getPanelId : function(p){
35263 applyConfig : function(config){
35264 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35265 this.config = config;
35270 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35271 * the width, for horizontal (north, south) the height.
35272 * @param {Number} newSize The new width or height
35274 resizeTo : function(newSize){
35275 var el = this.el ? this.el :
35276 (this.activePanel ? this.activePanel.getEl() : null);
35278 switch(this.position){
35281 el.setWidth(newSize);
35282 this.fireEvent("resized", this, newSize);
35286 el.setHeight(newSize);
35287 this.fireEvent("resized", this, newSize);
35293 getBox : function(){
35294 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35297 getMargins : function(){
35298 return this.margins;
35301 updateBox : function(box){
35303 var el = this.activePanel.getEl();
35304 el.dom.style.left = box.x + "px";
35305 el.dom.style.top = box.y + "px";
35306 this.activePanel.setSize(box.width, box.height);
35310 * Returns the container element for this region.
35311 * @return {Roo.Element}
35313 getEl : function(){
35314 return this.activePanel;
35318 * Returns true if this region is currently visible.
35319 * @return {Boolean}
35321 isVisible : function(){
35322 return this.activePanel ? true : false;
35325 setActivePanel : function(panel){
35326 panel = this.getPanel(panel);
35327 if(this.activePanel && this.activePanel != panel){
35328 this.activePanel.setActiveState(false);
35329 this.activePanel.getEl().setLeftTop(-10000,-10000);
35331 this.activePanel = panel;
35332 panel.setActiveState(true);
35334 panel.setSize(this.box.width, this.box.height);
35336 this.fireEvent("panelactivated", this, panel);
35337 this.fireEvent("invalidated");
35341 * Show the specified panel.
35342 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35343 * @return {Roo.ContentPanel} The shown panel or null
35345 showPanel : function(panel){
35346 panel = this.getPanel(panel);
35348 this.setActivePanel(panel);
35354 * Get the active panel for this region.
35355 * @return {Roo.ContentPanel} The active panel or null
35357 getActivePanel : function(){
35358 return this.activePanel;
35362 * Add the passed ContentPanel(s)
35363 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35364 * @return {Roo.ContentPanel} The panel added (if only one was added)
35366 add : function(panel){
35367 if(arguments.length > 1){
35368 for(var i = 0, len = arguments.length; i < len; i++) {
35369 this.add(arguments[i]);
35373 if(this.hasPanel(panel)){
35374 this.showPanel(panel);
35377 var el = panel.getEl();
35378 if(el.dom.parentNode != this.mgr.el.dom){
35379 this.mgr.el.dom.appendChild(el.dom);
35381 if(panel.setRegion){
35382 panel.setRegion(this);
35384 this.panels.add(panel);
35385 el.setStyle("position", "absolute");
35386 if(!panel.background){
35387 this.setActivePanel(panel);
35388 if(this.config.initialSize && this.panels.getCount()==1){
35389 this.resizeTo(this.config.initialSize);
35392 this.fireEvent("paneladded", this, panel);
35397 * Returns true if the panel is in this region.
35398 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35399 * @return {Boolean}
35401 hasPanel : function(panel){
35402 if(typeof panel == "object"){ // must be panel obj
35403 panel = panel.getId();
35405 return this.getPanel(panel) ? true : false;
35409 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35410 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35411 * @param {Boolean} preservePanel Overrides the config preservePanel option
35412 * @return {Roo.ContentPanel} The panel that was removed
35414 remove : function(panel, preservePanel){
35415 panel = this.getPanel(panel);
35420 this.fireEvent("beforeremove", this, panel, e);
35421 if(e.cancel === true){
35424 var panelId = panel.getId();
35425 this.panels.removeKey(panelId);
35430 * Returns the panel specified or null if it's not in this region.
35431 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35432 * @return {Roo.ContentPanel}
35434 getPanel : function(id){
35435 if(typeof id == "object"){ // must be panel obj
35438 return this.panels.get(id);
35442 * Returns this regions position (north/south/east/west/center).
35445 getPosition: function(){
35446 return this.position;
35450 * Ext JS Library 1.1.1
35451 * Copyright(c) 2006-2007, Ext JS, LLC.
35453 * Originally Released Under LGPL - original licence link has changed is not relivant.
35456 * <script type="text/javascript">
35460 * @class Roo.bootstrap.layout.Region
35461 * @extends Roo.bootstrap.layout.Basic
35462 * This class represents a region in a layout manager.
35464 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35465 * @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})
35466 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35467 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35468 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35469 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35470 * @cfg {String} title The title for the region (overrides panel titles)
35471 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35472 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35473 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35474 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35475 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35476 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35477 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35478 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35479 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35480 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35482 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35483 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35484 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35485 * @cfg {Number} width For East/West panels
35486 * @cfg {Number} height For North/South panels
35487 * @cfg {Boolean} split To show the splitter
35488 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35490 * @cfg {string} cls Extra CSS classes to add to region
35492 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35493 * @cfg {string} region the region that it inhabits..
35496 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35497 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35499 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35500 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35501 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35503 Roo.bootstrap.layout.Region = function(config)
35505 this.applyConfig(config);
35507 var mgr = config.mgr;
35508 var pos = config.region;
35509 config.skipConfig = true;
35510 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35513 this.onRender(mgr.el);
35516 this.visible = true;
35517 this.collapsed = false;
35518 this.unrendered_panels = [];
35521 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35523 position: '', // set by wrapper (eg. north/south etc..)
35524 unrendered_panels : null, // unrendered panels.
35525 createBody : function(){
35526 /** This region's body element
35527 * @type Roo.Element */
35528 this.bodyEl = this.el.createChild({
35530 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35534 onRender: function(ctr, pos)
35536 var dh = Roo.DomHelper;
35537 /** This region's container element
35538 * @type Roo.Element */
35539 this.el = dh.append(ctr.dom, {
35541 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35543 /** This region's title element
35544 * @type Roo.Element */
35546 this.titleEl = dh.append(this.el.dom,
35549 unselectable: "on",
35550 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35552 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35553 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35556 this.titleEl.enableDisplayMode();
35557 /** This region's title text element
35558 * @type HTMLElement */
35559 this.titleTextEl = this.titleEl.dom.firstChild;
35560 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35562 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35563 this.closeBtn.enableDisplayMode();
35564 this.closeBtn.on("click", this.closeClicked, this);
35565 this.closeBtn.hide();
35567 this.createBody(this.config);
35568 if(this.config.hideWhenEmpty){
35570 this.on("paneladded", this.validateVisibility, this);
35571 this.on("panelremoved", this.validateVisibility, this);
35573 if(this.autoScroll){
35574 this.bodyEl.setStyle("overflow", "auto");
35576 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35578 //if(c.titlebar !== false){
35579 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35580 this.titleEl.hide();
35582 this.titleEl.show();
35583 if(this.config.title){
35584 this.titleTextEl.innerHTML = this.config.title;
35588 if(this.config.collapsed){
35589 this.collapse(true);
35591 if(this.config.hidden){
35595 if (this.unrendered_panels && this.unrendered_panels.length) {
35596 for (var i =0;i< this.unrendered_panels.length; i++) {
35597 this.add(this.unrendered_panels[i]);
35599 this.unrendered_panels = null;
35605 applyConfig : function(c)
35608 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35609 var dh = Roo.DomHelper;
35610 if(c.titlebar !== false){
35611 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35612 this.collapseBtn.on("click", this.collapse, this);
35613 this.collapseBtn.enableDisplayMode();
35615 if(c.showPin === true || this.showPin){
35616 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35617 this.stickBtn.enableDisplayMode();
35618 this.stickBtn.on("click", this.expand, this);
35619 this.stickBtn.hide();
35624 /** This region's collapsed element
35625 * @type Roo.Element */
35628 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35629 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35632 if(c.floatable !== false){
35633 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35634 this.collapsedEl.on("click", this.collapseClick, this);
35637 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35638 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35639 id: "message", unselectable: "on", style:{"float":"left"}});
35640 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35642 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35643 this.expandBtn.on("click", this.expand, this);
35647 if(this.collapseBtn){
35648 this.collapseBtn.setVisible(c.collapsible == true);
35651 this.cmargins = c.cmargins || this.cmargins ||
35652 (this.position == "west" || this.position == "east" ?
35653 {top: 0, left: 2, right:2, bottom: 0} :
35654 {top: 2, left: 0, right:0, bottom: 2});
35656 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35659 this.bottomTabs = c.tabPosition != "top";
35661 this.autoScroll = c.autoScroll || false;
35666 this.duration = c.duration || .30;
35667 this.slideDuration = c.slideDuration || .45;
35672 * Returns true if this region is currently visible.
35673 * @return {Boolean}
35675 isVisible : function(){
35676 return this.visible;
35680 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35681 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35683 //setCollapsedTitle : function(title){
35684 // title = title || " ";
35685 // if(this.collapsedTitleTextEl){
35686 // this.collapsedTitleTextEl.innerHTML = title;
35690 getBox : function(){
35692 // if(!this.collapsed){
35693 b = this.el.getBox(false, true);
35695 // b = this.collapsedEl.getBox(false, true);
35700 getMargins : function(){
35701 return this.margins;
35702 //return this.collapsed ? this.cmargins : this.margins;
35705 highlight : function(){
35706 this.el.addClass("x-layout-panel-dragover");
35709 unhighlight : function(){
35710 this.el.removeClass("x-layout-panel-dragover");
35713 updateBox : function(box)
35715 if (!this.bodyEl) {
35716 return; // not rendered yet..
35720 if(!this.collapsed){
35721 this.el.dom.style.left = box.x + "px";
35722 this.el.dom.style.top = box.y + "px";
35723 this.updateBody(box.width, box.height);
35725 this.collapsedEl.dom.style.left = box.x + "px";
35726 this.collapsedEl.dom.style.top = box.y + "px";
35727 this.collapsedEl.setSize(box.width, box.height);
35730 this.tabs.autoSizeTabs();
35734 updateBody : function(w, h)
35737 this.el.setWidth(w);
35738 w -= this.el.getBorderWidth("rl");
35739 if(this.config.adjustments){
35740 w += this.config.adjustments[0];
35743 if(h !== null && h > 0){
35744 this.el.setHeight(h);
35745 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35746 h -= this.el.getBorderWidth("tb");
35747 if(this.config.adjustments){
35748 h += this.config.adjustments[1];
35750 this.bodyEl.setHeight(h);
35752 h = this.tabs.syncHeight(h);
35755 if(this.panelSize){
35756 w = w !== null ? w : this.panelSize.width;
35757 h = h !== null ? h : this.panelSize.height;
35759 if(this.activePanel){
35760 var el = this.activePanel.getEl();
35761 w = w !== null ? w : el.getWidth();
35762 h = h !== null ? h : el.getHeight();
35763 this.panelSize = {width: w, height: h};
35764 this.activePanel.setSize(w, h);
35766 if(Roo.isIE && this.tabs){
35767 this.tabs.el.repaint();
35772 * Returns the container element for this region.
35773 * @return {Roo.Element}
35775 getEl : function(){
35780 * Hides this region.
35783 //if(!this.collapsed){
35784 this.el.dom.style.left = "-2000px";
35787 // this.collapsedEl.dom.style.left = "-2000px";
35788 // this.collapsedEl.hide();
35790 this.visible = false;
35791 this.fireEvent("visibilitychange", this, false);
35795 * Shows this region if it was previously hidden.
35798 //if(!this.collapsed){
35801 // this.collapsedEl.show();
35803 this.visible = true;
35804 this.fireEvent("visibilitychange", this, true);
35807 closeClicked : function(){
35808 if(this.activePanel){
35809 this.remove(this.activePanel);
35813 collapseClick : function(e){
35815 e.stopPropagation();
35818 e.stopPropagation();
35824 * Collapses this region.
35825 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35828 collapse : function(skipAnim, skipCheck = false){
35829 if(this.collapsed) {
35833 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35835 this.collapsed = true;
35837 this.split.el.hide();
35839 if(this.config.animate && skipAnim !== true){
35840 this.fireEvent("invalidated", this);
35841 this.animateCollapse();
35843 this.el.setLocation(-20000,-20000);
35845 this.collapsedEl.show();
35846 this.fireEvent("collapsed", this);
35847 this.fireEvent("invalidated", this);
35853 animateCollapse : function(){
35858 * Expands this region if it was previously collapsed.
35859 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35860 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35863 expand : function(e, skipAnim){
35865 e.stopPropagation();
35867 if(!this.collapsed || this.el.hasActiveFx()) {
35871 this.afterSlideIn();
35874 this.collapsed = false;
35875 if(this.config.animate && skipAnim !== true){
35876 this.animateExpand();
35880 this.split.el.show();
35882 this.collapsedEl.setLocation(-2000,-2000);
35883 this.collapsedEl.hide();
35884 this.fireEvent("invalidated", this);
35885 this.fireEvent("expanded", this);
35889 animateExpand : function(){
35893 initTabs : function()
35895 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35897 var ts = new Roo.bootstrap.panel.Tabs({
35898 el: this.bodyEl.dom,
35899 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35900 disableTooltips: this.config.disableTabTips,
35901 toolbar : this.config.toolbar
35904 if(this.config.hideTabs){
35905 ts.stripWrap.setDisplayed(false);
35908 ts.resizeTabs = this.config.resizeTabs === true;
35909 ts.minTabWidth = this.config.minTabWidth || 40;
35910 ts.maxTabWidth = this.config.maxTabWidth || 250;
35911 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35912 ts.monitorResize = false;
35913 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35914 ts.bodyEl.addClass('roo-layout-tabs-body');
35915 this.panels.each(this.initPanelAsTab, this);
35918 initPanelAsTab : function(panel){
35919 var ti = this.tabs.addTab(
35923 this.config.closeOnTab && panel.isClosable(),
35926 if(panel.tabTip !== undefined){
35927 ti.setTooltip(panel.tabTip);
35929 ti.on("activate", function(){
35930 this.setActivePanel(panel);
35933 if(this.config.closeOnTab){
35934 ti.on("beforeclose", function(t, e){
35936 this.remove(panel);
35940 panel.tabItem = ti;
35945 updatePanelTitle : function(panel, title)
35947 if(this.activePanel == panel){
35948 this.updateTitle(title);
35951 var ti = this.tabs.getTab(panel.getEl().id);
35953 if(panel.tabTip !== undefined){
35954 ti.setTooltip(panel.tabTip);
35959 updateTitle : function(title){
35960 if(this.titleTextEl && !this.config.title){
35961 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35965 setActivePanel : function(panel)
35967 panel = this.getPanel(panel);
35968 if(this.activePanel && this.activePanel != panel){
35969 if(this.activePanel.setActiveState(false) === false){
35973 this.activePanel = panel;
35974 panel.setActiveState(true);
35975 if(this.panelSize){
35976 panel.setSize(this.panelSize.width, this.panelSize.height);
35979 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35981 this.updateTitle(panel.getTitle());
35983 this.fireEvent("invalidated", this);
35985 this.fireEvent("panelactivated", this, panel);
35989 * Shows the specified panel.
35990 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35991 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35993 showPanel : function(panel)
35995 panel = this.getPanel(panel);
35998 var tab = this.tabs.getTab(panel.getEl().id);
35999 if(tab.isHidden()){
36000 this.tabs.unhideTab(tab.id);
36004 this.setActivePanel(panel);
36011 * Get the active panel for this region.
36012 * @return {Roo.ContentPanel} The active panel or null
36014 getActivePanel : function(){
36015 return this.activePanel;
36018 validateVisibility : function(){
36019 if(this.panels.getCount() < 1){
36020 this.updateTitle(" ");
36021 this.closeBtn.hide();
36024 if(!this.isVisible()){
36031 * Adds the passed ContentPanel(s) to this region.
36032 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36033 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36035 add : function(panel)
36037 if(arguments.length > 1){
36038 for(var i = 0, len = arguments.length; i < len; i++) {
36039 this.add(arguments[i]);
36044 // if we have not been rendered yet, then we can not really do much of this..
36045 if (!this.bodyEl) {
36046 this.unrendered_panels.push(panel);
36053 if(this.hasPanel(panel)){
36054 this.showPanel(panel);
36057 panel.setRegion(this);
36058 this.panels.add(panel);
36059 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36060 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36061 // and hide them... ???
36062 this.bodyEl.dom.appendChild(panel.getEl().dom);
36063 if(panel.background !== true){
36064 this.setActivePanel(panel);
36066 this.fireEvent("paneladded", this, panel);
36073 this.initPanelAsTab(panel);
36077 if(panel.background !== true){
36078 this.tabs.activate(panel.getEl().id);
36080 this.fireEvent("paneladded", this, panel);
36085 * Hides the tab for the specified panel.
36086 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36088 hidePanel : function(panel){
36089 if(this.tabs && (panel = this.getPanel(panel))){
36090 this.tabs.hideTab(panel.getEl().id);
36095 * Unhides the tab for a previously hidden panel.
36096 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36098 unhidePanel : function(panel){
36099 if(this.tabs && (panel = this.getPanel(panel))){
36100 this.tabs.unhideTab(panel.getEl().id);
36104 clearPanels : function(){
36105 while(this.panels.getCount() > 0){
36106 this.remove(this.panels.first());
36111 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36112 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36113 * @param {Boolean} preservePanel Overrides the config preservePanel option
36114 * @return {Roo.ContentPanel} The panel that was removed
36116 remove : function(panel, preservePanel)
36118 panel = this.getPanel(panel);
36123 this.fireEvent("beforeremove", this, panel, e);
36124 if(e.cancel === true){
36127 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36128 var panelId = panel.getId();
36129 this.panels.removeKey(panelId);
36131 document.body.appendChild(panel.getEl().dom);
36134 this.tabs.removeTab(panel.getEl().id);
36135 }else if (!preservePanel){
36136 this.bodyEl.dom.removeChild(panel.getEl().dom);
36138 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36139 var p = this.panels.first();
36140 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36141 tempEl.appendChild(p.getEl().dom);
36142 this.bodyEl.update("");
36143 this.bodyEl.dom.appendChild(p.getEl().dom);
36145 this.updateTitle(p.getTitle());
36147 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36148 this.setActivePanel(p);
36150 panel.setRegion(null);
36151 if(this.activePanel == panel){
36152 this.activePanel = null;
36154 if(this.config.autoDestroy !== false && preservePanel !== true){
36155 try{panel.destroy();}catch(e){}
36157 this.fireEvent("panelremoved", this, panel);
36162 * Returns the TabPanel component used by this region
36163 * @return {Roo.TabPanel}
36165 getTabs : function(){
36169 createTool : function(parentEl, className){
36170 var btn = Roo.DomHelper.append(parentEl, {
36172 cls: "x-layout-tools-button",
36175 cls: "roo-layout-tools-button-inner " + className,
36179 btn.addClassOnOver("roo-layout-tools-button-over");
36184 * Ext JS Library 1.1.1
36185 * Copyright(c) 2006-2007, Ext JS, LLC.
36187 * Originally Released Under LGPL - original licence link has changed is not relivant.
36190 * <script type="text/javascript">
36196 * @class Roo.SplitLayoutRegion
36197 * @extends Roo.LayoutRegion
36198 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36200 Roo.bootstrap.layout.Split = function(config){
36201 this.cursor = config.cursor;
36202 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36205 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36207 splitTip : "Drag to resize.",
36208 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36209 useSplitTips : false,
36211 applyConfig : function(config){
36212 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36215 onRender : function(ctr,pos) {
36217 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36218 if(!this.config.split){
36223 var splitEl = Roo.DomHelper.append(ctr.dom, {
36225 id: this.el.id + "-split",
36226 cls: "roo-layout-split roo-layout-split-"+this.position,
36229 /** The SplitBar for this region
36230 * @type Roo.SplitBar */
36231 // does not exist yet...
36232 Roo.log([this.position, this.orientation]);
36234 this.split = new Roo.bootstrap.SplitBar({
36235 dragElement : splitEl,
36236 resizingElement: this.el,
36237 orientation : this.orientation
36240 this.split.on("moved", this.onSplitMove, this);
36241 this.split.useShim = this.config.useShim === true;
36242 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36243 if(this.useSplitTips){
36244 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36246 //if(config.collapsible){
36247 // this.split.el.on("dblclick", this.collapse, this);
36250 if(typeof this.config.minSize != "undefined"){
36251 this.split.minSize = this.config.minSize;
36253 if(typeof this.config.maxSize != "undefined"){
36254 this.split.maxSize = this.config.maxSize;
36256 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36257 this.hideSplitter();
36262 getHMaxSize : function(){
36263 var cmax = this.config.maxSize || 10000;
36264 var center = this.mgr.getRegion("center");
36265 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36268 getVMaxSize : function(){
36269 var cmax = this.config.maxSize || 10000;
36270 var center = this.mgr.getRegion("center");
36271 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36274 onSplitMove : function(split, newSize){
36275 this.fireEvent("resized", this, newSize);
36279 * Returns the {@link Roo.SplitBar} for this region.
36280 * @return {Roo.SplitBar}
36282 getSplitBar : function(){
36287 this.hideSplitter();
36288 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36291 hideSplitter : function(){
36293 this.split.el.setLocation(-2000,-2000);
36294 this.split.el.hide();
36300 this.split.el.show();
36302 Roo.bootstrap.layout.Split.superclass.show.call(this);
36305 beforeSlide: function(){
36306 if(Roo.isGecko){// firefox overflow auto bug workaround
36307 this.bodyEl.clip();
36309 this.tabs.bodyEl.clip();
36311 if(this.activePanel){
36312 this.activePanel.getEl().clip();
36314 if(this.activePanel.beforeSlide){
36315 this.activePanel.beforeSlide();
36321 afterSlide : function(){
36322 if(Roo.isGecko){// firefox overflow auto bug workaround
36323 this.bodyEl.unclip();
36325 this.tabs.bodyEl.unclip();
36327 if(this.activePanel){
36328 this.activePanel.getEl().unclip();
36329 if(this.activePanel.afterSlide){
36330 this.activePanel.afterSlide();
36336 initAutoHide : function(){
36337 if(this.autoHide !== false){
36338 if(!this.autoHideHd){
36339 var st = new Roo.util.DelayedTask(this.slideIn, this);
36340 this.autoHideHd = {
36341 "mouseout": function(e){
36342 if(!e.within(this.el, true)){
36346 "mouseover" : function(e){
36352 this.el.on(this.autoHideHd);
36356 clearAutoHide : function(){
36357 if(this.autoHide !== false){
36358 this.el.un("mouseout", this.autoHideHd.mouseout);
36359 this.el.un("mouseover", this.autoHideHd.mouseover);
36363 clearMonitor : function(){
36364 Roo.get(document).un("click", this.slideInIf, this);
36367 // these names are backwards but not changed for compat
36368 slideOut : function(){
36369 if(this.isSlid || this.el.hasActiveFx()){
36372 this.isSlid = true;
36373 if(this.collapseBtn){
36374 this.collapseBtn.hide();
36376 this.closeBtnState = this.closeBtn.getStyle('display');
36377 this.closeBtn.hide();
36379 this.stickBtn.show();
36382 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36383 this.beforeSlide();
36384 this.el.setStyle("z-index", 10001);
36385 this.el.slideIn(this.getSlideAnchor(), {
36386 callback: function(){
36388 this.initAutoHide();
36389 Roo.get(document).on("click", this.slideInIf, this);
36390 this.fireEvent("slideshow", this);
36397 afterSlideIn : function(){
36398 this.clearAutoHide();
36399 this.isSlid = false;
36400 this.clearMonitor();
36401 this.el.setStyle("z-index", "");
36402 if(this.collapseBtn){
36403 this.collapseBtn.show();
36405 this.closeBtn.setStyle('display', this.closeBtnState);
36407 this.stickBtn.hide();
36409 this.fireEvent("slidehide", this);
36412 slideIn : function(cb){
36413 if(!this.isSlid || this.el.hasActiveFx()){
36417 this.isSlid = false;
36418 this.beforeSlide();
36419 this.el.slideOut(this.getSlideAnchor(), {
36420 callback: function(){
36421 this.el.setLeftTop(-10000, -10000);
36423 this.afterSlideIn();
36431 slideInIf : function(e){
36432 if(!e.within(this.el)){
36437 animateCollapse : function(){
36438 this.beforeSlide();
36439 this.el.setStyle("z-index", 20000);
36440 var anchor = this.getSlideAnchor();
36441 this.el.slideOut(anchor, {
36442 callback : function(){
36443 this.el.setStyle("z-index", "");
36444 this.collapsedEl.slideIn(anchor, {duration:.3});
36446 this.el.setLocation(-10000,-10000);
36448 this.fireEvent("collapsed", this);
36455 animateExpand : function(){
36456 this.beforeSlide();
36457 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36458 this.el.setStyle("z-index", 20000);
36459 this.collapsedEl.hide({
36462 this.el.slideIn(this.getSlideAnchor(), {
36463 callback : function(){
36464 this.el.setStyle("z-index", "");
36467 this.split.el.show();
36469 this.fireEvent("invalidated", this);
36470 this.fireEvent("expanded", this);
36498 getAnchor : function(){
36499 return this.anchors[this.position];
36502 getCollapseAnchor : function(){
36503 return this.canchors[this.position];
36506 getSlideAnchor : function(){
36507 return this.sanchors[this.position];
36510 getAlignAdj : function(){
36511 var cm = this.cmargins;
36512 switch(this.position){
36528 getExpandAdj : function(){
36529 var c = this.collapsedEl, cm = this.cmargins;
36530 switch(this.position){
36532 return [-(cm.right+c.getWidth()+cm.left), 0];
36535 return [cm.right+c.getWidth()+cm.left, 0];
36538 return [0, -(cm.top+cm.bottom+c.getHeight())];
36541 return [0, cm.top+cm.bottom+c.getHeight()];
36547 * Ext JS Library 1.1.1
36548 * Copyright(c) 2006-2007, Ext JS, LLC.
36550 * Originally Released Under LGPL - original licence link has changed is not relivant.
36553 * <script type="text/javascript">
36556 * These classes are private internal classes
36558 Roo.bootstrap.layout.Center = function(config){
36559 config.region = "center";
36560 Roo.bootstrap.layout.Region.call(this, config);
36561 this.visible = true;
36562 this.minWidth = config.minWidth || 20;
36563 this.minHeight = config.minHeight || 20;
36566 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36568 // center panel can't be hidden
36572 // center panel can't be hidden
36575 getMinWidth: function(){
36576 return this.minWidth;
36579 getMinHeight: function(){
36580 return this.minHeight;
36593 Roo.bootstrap.layout.North = function(config)
36595 config.region = 'north';
36596 config.cursor = 'n-resize';
36598 Roo.bootstrap.layout.Split.call(this, config);
36602 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36603 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36604 this.split.el.addClass("roo-layout-split-v");
36606 var size = config.initialSize || config.height;
36607 if(typeof size != "undefined"){
36608 this.el.setHeight(size);
36611 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36613 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36617 getBox : function(){
36618 if(this.collapsed){
36619 return this.collapsedEl.getBox();
36621 var box = this.el.getBox();
36623 box.height += this.split.el.getHeight();
36628 updateBox : function(box){
36629 if(this.split && !this.collapsed){
36630 box.height -= this.split.el.getHeight();
36631 this.split.el.setLeft(box.x);
36632 this.split.el.setTop(box.y+box.height);
36633 this.split.el.setWidth(box.width);
36635 if(this.collapsed){
36636 this.updateBody(box.width, null);
36638 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36646 Roo.bootstrap.layout.South = function(config){
36647 config.region = 'south';
36648 config.cursor = 's-resize';
36649 Roo.bootstrap.layout.Split.call(this, config);
36651 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36652 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36653 this.split.el.addClass("roo-layout-split-v");
36655 var size = config.initialSize || config.height;
36656 if(typeof size != "undefined"){
36657 this.el.setHeight(size);
36661 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36662 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36663 getBox : function(){
36664 if(this.collapsed){
36665 return this.collapsedEl.getBox();
36667 var box = this.el.getBox();
36669 var sh = this.split.el.getHeight();
36676 updateBox : function(box){
36677 if(this.split && !this.collapsed){
36678 var sh = this.split.el.getHeight();
36681 this.split.el.setLeft(box.x);
36682 this.split.el.setTop(box.y-sh);
36683 this.split.el.setWidth(box.width);
36685 if(this.collapsed){
36686 this.updateBody(box.width, null);
36688 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36692 Roo.bootstrap.layout.East = function(config){
36693 config.region = "east";
36694 config.cursor = "e-resize";
36695 Roo.bootstrap.layout.Split.call(this, config);
36697 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36698 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36699 this.split.el.addClass("roo-layout-split-h");
36701 var size = config.initialSize || config.width;
36702 if(typeof size != "undefined"){
36703 this.el.setWidth(size);
36706 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36707 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36708 getBox : function(){
36709 if(this.collapsed){
36710 return this.collapsedEl.getBox();
36712 var box = this.el.getBox();
36714 var sw = this.split.el.getWidth();
36721 updateBox : function(box){
36722 if(this.split && !this.collapsed){
36723 var sw = this.split.el.getWidth();
36725 this.split.el.setLeft(box.x);
36726 this.split.el.setTop(box.y);
36727 this.split.el.setHeight(box.height);
36730 if(this.collapsed){
36731 this.updateBody(null, box.height);
36733 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36737 Roo.bootstrap.layout.West = function(config){
36738 config.region = "west";
36739 config.cursor = "w-resize";
36741 Roo.bootstrap.layout.Split.call(this, config);
36743 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36744 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36745 this.split.el.addClass("roo-layout-split-h");
36749 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36750 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36752 onRender: function(ctr, pos)
36754 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36755 var size = this.config.initialSize || this.config.width;
36756 if(typeof size != "undefined"){
36757 this.el.setWidth(size);
36761 getBox : function(){
36762 if(this.collapsed){
36763 return this.collapsedEl.getBox();
36765 var box = this.el.getBox();
36767 box.width += this.split.el.getWidth();
36772 updateBox : function(box){
36773 if(this.split && !this.collapsed){
36774 var sw = this.split.el.getWidth();
36776 this.split.el.setLeft(box.x+box.width);
36777 this.split.el.setTop(box.y);
36778 this.split.el.setHeight(box.height);
36780 if(this.collapsed){
36781 this.updateBody(null, box.height);
36783 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36786 Roo.namespace("Roo.bootstrap.panel");/*
36788 * Ext JS Library 1.1.1
36789 * Copyright(c) 2006-2007, Ext JS, LLC.
36791 * Originally Released Under LGPL - original licence link has changed is not relivant.
36794 * <script type="text/javascript">
36797 * @class Roo.ContentPanel
36798 * @extends Roo.util.Observable
36799 * A basic ContentPanel element.
36800 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36801 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36802 * @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
36803 * @cfg {Boolean} closable True if the panel can be closed/removed
36804 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36805 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36806 * @cfg {Toolbar} toolbar A toolbar for this panel
36807 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36808 * @cfg {String} title The title for this panel
36809 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36810 * @cfg {String} url Calls {@link #setUrl} with this value
36811 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36812 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36813 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36814 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36815 * @cfg {Boolean} badges render the badges
36818 * Create a new ContentPanel.
36819 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36820 * @param {String/Object} config A string to set only the title or a config object
36821 * @param {String} content (optional) Set the HTML content for this panel
36822 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36824 Roo.bootstrap.panel.Content = function( config){
36826 this.tpl = config.tpl || false;
36828 var el = config.el;
36829 var content = config.content;
36831 if(config.autoCreate){ // xtype is available if this is called from factory
36834 this.el = Roo.get(el);
36835 if(!this.el && config && config.autoCreate){
36836 if(typeof config.autoCreate == "object"){
36837 if(!config.autoCreate.id){
36838 config.autoCreate.id = config.id||el;
36840 this.el = Roo.DomHelper.append(document.body,
36841 config.autoCreate, true);
36843 var elcfg = { tag: "div",
36844 cls: "roo-layout-inactive-content",
36848 elcfg.html = config.html;
36852 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36855 this.closable = false;
36856 this.loaded = false;
36857 this.active = false;
36860 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36862 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36864 this.wrapEl = this.el; //this.el.wrap();
36866 if (config.toolbar.items) {
36867 ti = config.toolbar.items ;
36868 delete config.toolbar.items ;
36872 this.toolbar.render(this.wrapEl, 'before');
36873 for(var i =0;i < ti.length;i++) {
36874 // Roo.log(['add child', items[i]]);
36875 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36877 this.toolbar.items = nitems;
36878 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36879 delete config.toolbar;
36883 // xtype created footer. - not sure if will work as we normally have to render first..
36884 if (this.footer && !this.footer.el && this.footer.xtype) {
36885 if (!this.wrapEl) {
36886 this.wrapEl = this.el.wrap();
36889 this.footer.container = this.wrapEl.createChild();
36891 this.footer = Roo.factory(this.footer, Roo);
36896 if(typeof config == "string"){
36897 this.title = config;
36899 Roo.apply(this, config);
36903 this.resizeEl = Roo.get(this.resizeEl, true);
36905 this.resizeEl = this.el;
36907 // handle view.xtype
36915 * Fires when this panel is activated.
36916 * @param {Roo.ContentPanel} this
36920 * @event deactivate
36921 * Fires when this panel is activated.
36922 * @param {Roo.ContentPanel} this
36924 "deactivate" : true,
36928 * Fires when this panel is resized if fitToFrame is true.
36929 * @param {Roo.ContentPanel} this
36930 * @param {Number} width The width after any component adjustments
36931 * @param {Number} height The height after any component adjustments
36937 * Fires when this tab is created
36938 * @param {Roo.ContentPanel} this
36949 if(this.autoScroll){
36950 this.resizeEl.setStyle("overflow", "auto");
36952 // fix randome scrolling
36953 //this.el.on('scroll', function() {
36954 // Roo.log('fix random scolling');
36955 // this.scrollTo('top',0);
36958 content = content || this.content;
36960 this.setContent(content);
36962 if(config && config.url){
36963 this.setUrl(this.url, this.params, this.loadOnce);
36968 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36970 if (this.view && typeof(this.view.xtype) != 'undefined') {
36971 this.view.el = this.el.appendChild(document.createElement("div"));
36972 this.view = Roo.factory(this.view);
36973 this.view.render && this.view.render(false, '');
36977 this.fireEvent('render', this);
36980 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36984 setRegion : function(region){
36985 this.region = region;
36986 this.setActiveClass(region && !this.background);
36990 setActiveClass: function(state)
36993 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36994 this.el.setStyle('position','relative');
36996 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36997 this.el.setStyle('position', 'absolute');
37002 * Returns the toolbar for this Panel if one was configured.
37003 * @return {Roo.Toolbar}
37005 getToolbar : function(){
37006 return this.toolbar;
37009 setActiveState : function(active)
37011 this.active = active;
37012 this.setActiveClass(active);
37014 if(this.fireEvent("deactivate", this) === false){
37019 this.fireEvent("activate", this);
37023 * Updates this panel's element
37024 * @param {String} content The new content
37025 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37027 setContent : function(content, loadScripts){
37028 this.el.update(content, loadScripts);
37031 ignoreResize : function(w, h){
37032 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37035 this.lastSize = {width: w, height: h};
37040 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37041 * @return {Roo.UpdateManager} The UpdateManager
37043 getUpdateManager : function(){
37044 return this.el.getUpdateManager();
37047 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37048 * @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:
37051 url: "your-url.php",
37052 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37053 callback: yourFunction,
37054 scope: yourObject, //(optional scope)
37057 text: "Loading...",
37062 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37063 * 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.
37064 * @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}
37065 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37066 * @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.
37067 * @return {Roo.ContentPanel} this
37070 var um = this.el.getUpdateManager();
37071 um.update.apply(um, arguments);
37077 * 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.
37078 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37079 * @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)
37080 * @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)
37081 * @return {Roo.UpdateManager} The UpdateManager
37083 setUrl : function(url, params, loadOnce){
37084 if(this.refreshDelegate){
37085 this.removeListener("activate", this.refreshDelegate);
37087 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37088 this.on("activate", this.refreshDelegate);
37089 return this.el.getUpdateManager();
37092 _handleRefresh : function(url, params, loadOnce){
37093 if(!loadOnce || !this.loaded){
37094 var updater = this.el.getUpdateManager();
37095 updater.update(url, params, this._setLoaded.createDelegate(this));
37099 _setLoaded : function(){
37100 this.loaded = true;
37104 * Returns this panel's id
37107 getId : function(){
37112 * Returns this panel's element - used by regiosn to add.
37113 * @return {Roo.Element}
37115 getEl : function(){
37116 return this.wrapEl || this.el;
37121 adjustForComponents : function(width, height)
37123 //Roo.log('adjustForComponents ');
37124 if(this.resizeEl != this.el){
37125 width -= this.el.getFrameWidth('lr');
37126 height -= this.el.getFrameWidth('tb');
37129 var te = this.toolbar.getEl();
37130 te.setWidth(width);
37131 height -= te.getHeight();
37134 var te = this.footer.getEl();
37135 te.setWidth(width);
37136 height -= te.getHeight();
37140 if(this.adjustments){
37141 width += this.adjustments[0];
37142 height += this.adjustments[1];
37144 return {"width": width, "height": height};
37147 setSize : function(width, height){
37148 if(this.fitToFrame && !this.ignoreResize(width, height)){
37149 if(this.fitContainer && this.resizeEl != this.el){
37150 this.el.setSize(width, height);
37152 var size = this.adjustForComponents(width, height);
37153 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37154 this.fireEvent('resize', this, size.width, size.height);
37159 * Returns this panel's title
37162 getTitle : function(){
37164 if (typeof(this.title) != 'object') {
37169 for (var k in this.title) {
37170 if (!this.title.hasOwnProperty(k)) {
37174 if (k.indexOf('-') >= 0) {
37175 var s = k.split('-');
37176 for (var i = 0; i<s.length; i++) {
37177 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37180 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37187 * Set this panel's title
37188 * @param {String} title
37190 setTitle : function(title){
37191 this.title = title;
37193 this.region.updatePanelTitle(this, title);
37198 * Returns true is this panel was configured to be closable
37199 * @return {Boolean}
37201 isClosable : function(){
37202 return this.closable;
37205 beforeSlide : function(){
37207 this.resizeEl.clip();
37210 afterSlide : function(){
37212 this.resizeEl.unclip();
37216 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37217 * Will fail silently if the {@link #setUrl} method has not been called.
37218 * This does not activate the panel, just updates its content.
37220 refresh : function(){
37221 if(this.refreshDelegate){
37222 this.loaded = false;
37223 this.refreshDelegate();
37228 * Destroys this panel
37230 destroy : function(){
37231 this.el.removeAllListeners();
37232 var tempEl = document.createElement("span");
37233 tempEl.appendChild(this.el.dom);
37234 tempEl.innerHTML = "";
37240 * form - if the content panel contains a form - this is a reference to it.
37241 * @type {Roo.form.Form}
37245 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37246 * This contains a reference to it.
37252 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37262 * @param {Object} cfg Xtype definition of item to add.
37266 getChildContainer: function () {
37267 return this.getEl();
37272 var ret = new Roo.factory(cfg);
37277 if (cfg.xtype.match(/^Form$/)) {
37280 //if (this.footer) {
37281 // el = this.footer.container.insertSibling(false, 'before');
37283 el = this.el.createChild();
37286 this.form = new Roo.form.Form(cfg);
37289 if ( this.form.allItems.length) {
37290 this.form.render(el.dom);
37294 // should only have one of theses..
37295 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37296 // views.. should not be just added - used named prop 'view''
37298 cfg.el = this.el.appendChild(document.createElement("div"));
37301 var ret = new Roo.factory(cfg);
37303 ret.render && ret.render(false, ''); // render blank..
37313 * @class Roo.bootstrap.panel.Grid
37314 * @extends Roo.bootstrap.panel.Content
37316 * Create a new GridPanel.
37317 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37318 * @param {Object} config A the config object
37324 Roo.bootstrap.panel.Grid = function(config)
37328 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37329 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37331 config.el = this.wrapper;
37332 //this.el = this.wrapper;
37334 if (config.container) {
37335 // ctor'ed from a Border/panel.grid
37338 this.wrapper.setStyle("overflow", "hidden");
37339 this.wrapper.addClass('roo-grid-container');
37344 if(config.toolbar){
37345 var tool_el = this.wrapper.createChild();
37346 this.toolbar = Roo.factory(config.toolbar);
37348 if (config.toolbar.items) {
37349 ti = config.toolbar.items ;
37350 delete config.toolbar.items ;
37354 this.toolbar.render(tool_el);
37355 for(var i =0;i < ti.length;i++) {
37356 // Roo.log(['add child', items[i]]);
37357 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37359 this.toolbar.items = nitems;
37361 delete config.toolbar;
37364 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37365 config.grid.scrollBody = true;;
37366 config.grid.monitorWindowResize = false; // turn off autosizing
37367 config.grid.autoHeight = false;
37368 config.grid.autoWidth = false;
37370 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37372 if (config.background) {
37373 // render grid on panel activation (if panel background)
37374 this.on('activate', function(gp) {
37375 if (!gp.grid.rendered) {
37376 gp.grid.render(this.wrapper);
37377 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37382 this.grid.render(this.wrapper);
37383 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37386 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37387 // ??? needed ??? config.el = this.wrapper;
37392 // xtype created footer. - not sure if will work as we normally have to render first..
37393 if (this.footer && !this.footer.el && this.footer.xtype) {
37395 var ctr = this.grid.getView().getFooterPanel(true);
37396 this.footer.dataSource = this.grid.dataSource;
37397 this.footer = Roo.factory(this.footer, Roo);
37398 this.footer.render(ctr);
37408 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37409 getId : function(){
37410 return this.grid.id;
37414 * Returns the grid for this panel
37415 * @return {Roo.bootstrap.Table}
37417 getGrid : function(){
37421 setSize : function(width, height){
37422 if(!this.ignoreResize(width, height)){
37423 var grid = this.grid;
37424 var size = this.adjustForComponents(width, height);
37425 var gridel = grid.getGridEl();
37426 gridel.setSize(size.width, size.height);
37428 var thd = grid.getGridEl().select('thead',true).first();
37429 var tbd = grid.getGridEl().select('tbody', true).first();
37431 tbd.setSize(width, height - thd.getHeight());
37440 beforeSlide : function(){
37441 this.grid.getView().scroller.clip();
37444 afterSlide : function(){
37445 this.grid.getView().scroller.unclip();
37448 destroy : function(){
37449 this.grid.destroy();
37451 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37456 * @class Roo.bootstrap.panel.Nest
37457 * @extends Roo.bootstrap.panel.Content
37459 * Create a new Panel, that can contain a layout.Border.
37462 * @param {Roo.BorderLayout} layout The layout for this panel
37463 * @param {String/Object} config A string to set only the title or a config object
37465 Roo.bootstrap.panel.Nest = function(config)
37467 // construct with only one argument..
37468 /* FIXME - implement nicer consturctors
37469 if (layout.layout) {
37471 layout = config.layout;
37472 delete config.layout;
37474 if (layout.xtype && !layout.getEl) {
37475 // then layout needs constructing..
37476 layout = Roo.factory(layout, Roo);
37480 config.el = config.layout.getEl();
37482 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37484 config.layout.monitorWindowResize = false; // turn off autosizing
37485 this.layout = config.layout;
37486 this.layout.getEl().addClass("roo-layout-nested-layout");
37493 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37495 setSize : function(width, height){
37496 if(!this.ignoreResize(width, height)){
37497 var size = this.adjustForComponents(width, height);
37498 var el = this.layout.getEl();
37499 if (size.height < 1) {
37500 el.setWidth(size.width);
37502 el.setSize(size.width, size.height);
37504 var touch = el.dom.offsetWidth;
37505 this.layout.layout();
37506 // ie requires a double layout on the first pass
37507 if(Roo.isIE && !this.initialized){
37508 this.initialized = true;
37509 this.layout.layout();
37514 // activate all subpanels if not currently active..
37516 setActiveState : function(active){
37517 this.active = active;
37518 this.setActiveClass(active);
37521 this.fireEvent("deactivate", this);
37525 this.fireEvent("activate", this);
37526 // not sure if this should happen before or after..
37527 if (!this.layout) {
37528 return; // should not happen..
37531 for (var r in this.layout.regions) {
37532 reg = this.layout.getRegion(r);
37533 if (reg.getActivePanel()) {
37534 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37535 reg.setActivePanel(reg.getActivePanel());
37538 if (!reg.panels.length) {
37541 reg.showPanel(reg.getPanel(0));
37550 * Returns the nested BorderLayout for this panel
37551 * @return {Roo.BorderLayout}
37553 getLayout : function(){
37554 return this.layout;
37558 * Adds a xtype elements to the layout of the nested panel
37562 xtype : 'ContentPanel',
37569 xtype : 'NestedLayoutPanel',
37575 items : [ ... list of content panels or nested layout panels.. ]
37579 * @param {Object} cfg Xtype definition of item to add.
37581 addxtype : function(cfg) {
37582 return this.layout.addxtype(cfg);
37587 * Ext JS Library 1.1.1
37588 * Copyright(c) 2006-2007, Ext JS, LLC.
37590 * Originally Released Under LGPL - original licence link has changed is not relivant.
37593 * <script type="text/javascript">
37596 * @class Roo.TabPanel
37597 * @extends Roo.util.Observable
37598 * A lightweight tab container.
37602 // basic tabs 1, built from existing content
37603 var tabs = new Roo.TabPanel("tabs1");
37604 tabs.addTab("script", "View Script");
37605 tabs.addTab("markup", "View Markup");
37606 tabs.activate("script");
37608 // more advanced tabs, built from javascript
37609 var jtabs = new Roo.TabPanel("jtabs");
37610 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37612 // set up the UpdateManager
37613 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37614 var updater = tab2.getUpdateManager();
37615 updater.setDefaultUrl("ajax1.htm");
37616 tab2.on('activate', updater.refresh, updater, true);
37618 // Use setUrl for Ajax loading
37619 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37620 tab3.setUrl("ajax2.htm", null, true);
37623 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37626 jtabs.activate("jtabs-1");
37629 * Create a new TabPanel.
37630 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37631 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37633 Roo.bootstrap.panel.Tabs = function(config){
37635 * The container element for this TabPanel.
37636 * @type Roo.Element
37638 this.el = Roo.get(config.el);
37641 if(typeof config == "boolean"){
37642 this.tabPosition = config ? "bottom" : "top";
37644 Roo.apply(this, config);
37648 if(this.tabPosition == "bottom"){
37649 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37650 this.el.addClass("roo-tabs-bottom");
37652 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37653 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37654 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37656 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37658 if(this.tabPosition != "bottom"){
37659 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37660 * @type Roo.Element
37662 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37663 this.el.addClass("roo-tabs-top");
37667 this.bodyEl.setStyle("position", "relative");
37669 this.active = null;
37670 this.activateDelegate = this.activate.createDelegate(this);
37675 * Fires when the active tab changes
37676 * @param {Roo.TabPanel} this
37677 * @param {Roo.TabPanelItem} activePanel The new active tab
37681 * @event beforetabchange
37682 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37683 * @param {Roo.TabPanel} this
37684 * @param {Object} e Set cancel to true on this object to cancel the tab change
37685 * @param {Roo.TabPanelItem} tab The tab being changed to
37687 "beforetabchange" : true
37690 Roo.EventManager.onWindowResize(this.onResize, this);
37691 this.cpad = this.el.getPadding("lr");
37692 this.hiddenCount = 0;
37695 // toolbar on the tabbar support...
37696 if (this.toolbar) {
37697 alert("no toolbar support yet");
37698 this.toolbar = false;
37700 var tcfg = this.toolbar;
37701 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37702 this.toolbar = new Roo.Toolbar(tcfg);
37703 if (Roo.isSafari) {
37704 var tbl = tcfg.container.child('table', true);
37705 tbl.setAttribute('width', '100%');
37713 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37716 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37718 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37720 tabPosition : "top",
37722 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37724 currentTabWidth : 0,
37726 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37730 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37734 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37736 preferredTabWidth : 175,
37738 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37740 resizeTabs : false,
37742 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37744 monitorResize : true,
37746 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37751 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37752 * @param {String} id The id of the div to use <b>or create</b>
37753 * @param {String} text The text for the tab
37754 * @param {String} content (optional) Content to put in the TabPanelItem body
37755 * @param {Boolean} closable (optional) True to create a close icon on the tab
37756 * @return {Roo.TabPanelItem} The created TabPanelItem
37758 addTab : function(id, text, content, closable, tpl)
37760 var item = new Roo.bootstrap.panel.TabItem({
37764 closable : closable,
37767 this.addTabItem(item);
37769 item.setContent(content);
37775 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37776 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37777 * @return {Roo.TabPanelItem}
37779 getTab : function(id){
37780 return this.items[id];
37784 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37785 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37787 hideTab : function(id){
37788 var t = this.items[id];
37791 this.hiddenCount++;
37792 this.autoSizeTabs();
37797 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37798 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37800 unhideTab : function(id){
37801 var t = this.items[id];
37803 t.setHidden(false);
37804 this.hiddenCount--;
37805 this.autoSizeTabs();
37810 * Adds an existing {@link Roo.TabPanelItem}.
37811 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37813 addTabItem : function(item){
37814 this.items[item.id] = item;
37815 this.items.push(item);
37816 // if(this.resizeTabs){
37817 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37818 // this.autoSizeTabs();
37820 // item.autoSize();
37825 * Removes a {@link Roo.TabPanelItem}.
37826 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37828 removeTab : function(id){
37829 var items = this.items;
37830 var tab = items[id];
37831 if(!tab) { return; }
37832 var index = items.indexOf(tab);
37833 if(this.active == tab && items.length > 1){
37834 var newTab = this.getNextAvailable(index);
37839 this.stripEl.dom.removeChild(tab.pnode.dom);
37840 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37841 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37843 items.splice(index, 1);
37844 delete this.items[tab.id];
37845 tab.fireEvent("close", tab);
37846 tab.purgeListeners();
37847 this.autoSizeTabs();
37850 getNextAvailable : function(start){
37851 var items = this.items;
37853 // look for a next tab that will slide over to
37854 // replace the one being removed
37855 while(index < items.length){
37856 var item = items[++index];
37857 if(item && !item.isHidden()){
37861 // if one isn't found select the previous tab (on the left)
37864 var item = items[--index];
37865 if(item && !item.isHidden()){
37873 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37874 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37876 disableTab : function(id){
37877 var tab = this.items[id];
37878 if(tab && this.active != tab){
37884 * Enables a {@link Roo.TabPanelItem} that is disabled.
37885 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37887 enableTab : function(id){
37888 var tab = this.items[id];
37893 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37894 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37895 * @return {Roo.TabPanelItem} The TabPanelItem.
37897 activate : function(id){
37898 var tab = this.items[id];
37902 if(tab == this.active || tab.disabled){
37906 this.fireEvent("beforetabchange", this, e, tab);
37907 if(e.cancel !== true && !tab.disabled){
37909 this.active.hide();
37911 this.active = this.items[id];
37912 this.active.show();
37913 this.fireEvent("tabchange", this, this.active);
37919 * Gets the active {@link Roo.TabPanelItem}.
37920 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37922 getActiveTab : function(){
37923 return this.active;
37927 * Updates the tab body element to fit the height of the container element
37928 * for overflow scrolling
37929 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37931 syncHeight : function(targetHeight){
37932 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37933 var bm = this.bodyEl.getMargins();
37934 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37935 this.bodyEl.setHeight(newHeight);
37939 onResize : function(){
37940 if(this.monitorResize){
37941 this.autoSizeTabs();
37946 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37948 beginUpdate : function(){
37949 this.updating = true;
37953 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37955 endUpdate : function(){
37956 this.updating = false;
37957 this.autoSizeTabs();
37961 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37963 autoSizeTabs : function(){
37964 var count = this.items.length;
37965 var vcount = count - this.hiddenCount;
37966 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37969 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37970 var availWidth = Math.floor(w / vcount);
37971 var b = this.stripBody;
37972 if(b.getWidth() > w){
37973 var tabs = this.items;
37974 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37975 if(availWidth < this.minTabWidth){
37976 /*if(!this.sleft){ // incomplete scrolling code
37977 this.createScrollButtons();
37980 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37983 if(this.currentTabWidth < this.preferredTabWidth){
37984 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37990 * Returns the number of tabs in this TabPanel.
37993 getCount : function(){
37994 return this.items.length;
37998 * Resizes all the tabs to the passed width
37999 * @param {Number} The new width
38001 setTabWidth : function(width){
38002 this.currentTabWidth = width;
38003 for(var i = 0, len = this.items.length; i < len; i++) {
38004 if(!this.items[i].isHidden()) {
38005 this.items[i].setWidth(width);
38011 * Destroys this TabPanel
38012 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38014 destroy : function(removeEl){
38015 Roo.EventManager.removeResizeListener(this.onResize, this);
38016 for(var i = 0, len = this.items.length; i < len; i++){
38017 this.items[i].purgeListeners();
38019 if(removeEl === true){
38020 this.el.update("");
38025 createStrip : function(container)
38027 var strip = document.createElement("nav");
38028 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38029 container.appendChild(strip);
38033 createStripList : function(strip)
38035 // div wrapper for retard IE
38036 // returns the "tr" element.
38037 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38038 //'<div class="x-tabs-strip-wrap">'+
38039 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38040 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38041 return strip.firstChild; //.firstChild.firstChild.firstChild;
38043 createBody : function(container)
38045 var body = document.createElement("div");
38046 Roo.id(body, "tab-body");
38047 //Roo.fly(body).addClass("x-tabs-body");
38048 Roo.fly(body).addClass("tab-content");
38049 container.appendChild(body);
38052 createItemBody :function(bodyEl, id){
38053 var body = Roo.getDom(id);
38055 body = document.createElement("div");
38058 //Roo.fly(body).addClass("x-tabs-item-body");
38059 Roo.fly(body).addClass("tab-pane");
38060 bodyEl.insertBefore(body, bodyEl.firstChild);
38064 createStripElements : function(stripEl, text, closable, tpl)
38066 var td = document.createElement("li"); // was td..
38069 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38072 stripEl.appendChild(td);
38074 td.className = "x-tabs-closable";
38075 if(!this.closeTpl){
38076 this.closeTpl = new Roo.Template(
38077 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38078 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38079 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38082 var el = this.closeTpl.overwrite(td, {"text": text});
38083 var close = el.getElementsByTagName("div")[0];
38084 var inner = el.getElementsByTagName("em")[0];
38085 return {"el": el, "close": close, "inner": inner};
38088 // not sure what this is..
38089 // if(!this.tabTpl){
38090 //this.tabTpl = new Roo.Template(
38091 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38092 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38094 // this.tabTpl = new Roo.Template(
38095 // '<a href="#">' +
38096 // '<span unselectable="on"' +
38097 // (this.disableTooltips ? '' : ' title="{text}"') +
38098 // ' >{text}</span></a>'
38104 var template = tpl || this.tabTpl || false;
38108 template = new Roo.Template(
38110 '<span unselectable="on"' +
38111 (this.disableTooltips ? '' : ' title="{text}"') +
38112 ' >{text}</span></a>'
38116 switch (typeof(template)) {
38120 template = new Roo.Template(template);
38126 var el = template.overwrite(td, {"text": text});
38128 var inner = el.getElementsByTagName("span")[0];
38130 return {"el": el, "inner": inner};
38138 * @class Roo.TabPanelItem
38139 * @extends Roo.util.Observable
38140 * Represents an individual item (tab plus body) in a TabPanel.
38141 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38142 * @param {String} id The id of this TabPanelItem
38143 * @param {String} text The text for the tab of this TabPanelItem
38144 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38146 Roo.bootstrap.panel.TabItem = function(config){
38148 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38149 * @type Roo.TabPanel
38151 this.tabPanel = config.panel;
38153 * The id for this TabPanelItem
38156 this.id = config.id;
38158 this.disabled = false;
38160 this.text = config.text;
38162 this.loaded = false;
38163 this.closable = config.closable;
38166 * The body element for this TabPanelItem.
38167 * @type Roo.Element
38169 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38170 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38171 this.bodyEl.setStyle("display", "block");
38172 this.bodyEl.setStyle("zoom", "1");
38173 //this.hideAction();
38175 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38177 this.el = Roo.get(els.el);
38178 this.inner = Roo.get(els.inner, true);
38179 this.textEl = Roo.get(this.el.dom.firstChild, true);
38180 this.pnode = Roo.get(els.el.parentNode, true);
38181 // this.el.on("mousedown", this.onTabMouseDown, this);
38182 this.el.on("click", this.onTabClick, this);
38184 if(config.closable){
38185 var c = Roo.get(els.close, true);
38186 c.dom.title = this.closeText;
38187 c.addClassOnOver("close-over");
38188 c.on("click", this.closeClick, this);
38194 * Fires when this tab becomes the active tab.
38195 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38196 * @param {Roo.TabPanelItem} this
38200 * @event beforeclose
38201 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38202 * @param {Roo.TabPanelItem} this
38203 * @param {Object} e Set cancel to true on this object to cancel the close.
38205 "beforeclose": true,
38208 * Fires when this tab is closed.
38209 * @param {Roo.TabPanelItem} this
38213 * @event deactivate
38214 * Fires when this tab is no longer the active tab.
38215 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38216 * @param {Roo.TabPanelItem} this
38218 "deactivate" : true
38220 this.hidden = false;
38222 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38225 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38227 purgeListeners : function(){
38228 Roo.util.Observable.prototype.purgeListeners.call(this);
38229 this.el.removeAllListeners();
38232 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38235 this.pnode.addClass("active");
38238 this.tabPanel.stripWrap.repaint();
38240 this.fireEvent("activate", this.tabPanel, this);
38244 * Returns true if this tab is the active tab.
38245 * @return {Boolean}
38247 isActive : function(){
38248 return this.tabPanel.getActiveTab() == this;
38252 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38255 this.pnode.removeClass("active");
38257 this.fireEvent("deactivate", this.tabPanel, this);
38260 hideAction : function(){
38261 this.bodyEl.hide();
38262 this.bodyEl.setStyle("position", "absolute");
38263 this.bodyEl.setLeft("-20000px");
38264 this.bodyEl.setTop("-20000px");
38267 showAction : function(){
38268 this.bodyEl.setStyle("position", "relative");
38269 this.bodyEl.setTop("");
38270 this.bodyEl.setLeft("");
38271 this.bodyEl.show();
38275 * Set the tooltip for the tab.
38276 * @param {String} tooltip The tab's tooltip
38278 setTooltip : function(text){
38279 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38280 this.textEl.dom.qtip = text;
38281 this.textEl.dom.removeAttribute('title');
38283 this.textEl.dom.title = text;
38287 onTabClick : function(e){
38288 e.preventDefault();
38289 this.tabPanel.activate(this.id);
38292 onTabMouseDown : function(e){
38293 e.preventDefault();
38294 this.tabPanel.activate(this.id);
38297 getWidth : function(){
38298 return this.inner.getWidth();
38301 setWidth : function(width){
38302 var iwidth = width - this.pnode.getPadding("lr");
38303 this.inner.setWidth(iwidth);
38304 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38305 this.pnode.setWidth(width);
38309 * Show or hide the tab
38310 * @param {Boolean} hidden True to hide or false to show.
38312 setHidden : function(hidden){
38313 this.hidden = hidden;
38314 this.pnode.setStyle("display", hidden ? "none" : "");
38318 * Returns true if this tab is "hidden"
38319 * @return {Boolean}
38321 isHidden : function(){
38322 return this.hidden;
38326 * Returns the text for this tab
38329 getText : function(){
38333 autoSize : function(){
38334 //this.el.beginMeasure();
38335 this.textEl.setWidth(1);
38337 * #2804 [new] Tabs in Roojs
38338 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38340 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38341 //this.el.endMeasure();
38345 * Sets the text for the tab (Note: this also sets the tooltip text)
38346 * @param {String} text The tab's text and tooltip
38348 setText : function(text){
38350 this.textEl.update(text);
38351 this.setTooltip(text);
38352 //if(!this.tabPanel.resizeTabs){
38353 // this.autoSize();
38357 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38359 activate : function(){
38360 this.tabPanel.activate(this.id);
38364 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38366 disable : function(){
38367 if(this.tabPanel.active != this){
38368 this.disabled = true;
38369 this.pnode.addClass("disabled");
38374 * Enables this TabPanelItem if it was previously disabled.
38376 enable : function(){
38377 this.disabled = false;
38378 this.pnode.removeClass("disabled");
38382 * Sets the content for this TabPanelItem.
38383 * @param {String} content The content
38384 * @param {Boolean} loadScripts true to look for and load scripts
38386 setContent : function(content, loadScripts){
38387 this.bodyEl.update(content, loadScripts);
38391 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38392 * @return {Roo.UpdateManager} The UpdateManager
38394 getUpdateManager : function(){
38395 return this.bodyEl.getUpdateManager();
38399 * Set a URL to be used to load the content for this TabPanelItem.
38400 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38401 * @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)
38402 * @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)
38403 * @return {Roo.UpdateManager} The UpdateManager
38405 setUrl : function(url, params, loadOnce){
38406 if(this.refreshDelegate){
38407 this.un('activate', this.refreshDelegate);
38409 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38410 this.on("activate", this.refreshDelegate);
38411 return this.bodyEl.getUpdateManager();
38415 _handleRefresh : function(url, params, loadOnce){
38416 if(!loadOnce || !this.loaded){
38417 var updater = this.bodyEl.getUpdateManager();
38418 updater.update(url, params, this._setLoaded.createDelegate(this));
38423 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38424 * Will fail silently if the setUrl method has not been called.
38425 * This does not activate the panel, just updates its content.
38427 refresh : function(){
38428 if(this.refreshDelegate){
38429 this.loaded = false;
38430 this.refreshDelegate();
38435 _setLoaded : function(){
38436 this.loaded = true;
38440 closeClick : function(e){
38443 this.fireEvent("beforeclose", this, o);
38444 if(o.cancel !== true){
38445 this.tabPanel.removeTab(this.id);
38449 * The text displayed in the tooltip for the close icon.
38452 closeText : "Close this tab"
38455 * This script refer to:
38456 * Title: International Telephone Input
38457 * Author: Jack O'Connor
38458 * Code version: v12.1.12
38459 * Availability: https://github.com/jackocnr/intl-tel-input.git
38462 Roo.bootstrap.PhoneInputData = function() {
38465 "Afghanistan (افغانستان)",
38470 "Albania (Shqipëri)",
38475 "Algeria (الجزائر)",
38500 "Antigua and Barbuda",
38510 "Armenia (Հայաստան)",
38526 "Austria (Österreich)",
38531 "Azerbaijan (Azərbaycan)",
38541 "Bahrain (البحرين)",
38546 "Bangladesh (বাংলাদেশ)",
38556 "Belarus (Беларусь)",
38561 "Belgium (België)",
38591 "Bosnia and Herzegovina (Босна и Херцеговина)",
38606 "British Indian Ocean Territory",
38611 "British Virgin Islands",
38621 "Bulgaria (България)",
38631 "Burundi (Uburundi)",
38636 "Cambodia (កម្ពុជា)",
38641 "Cameroon (Cameroun)",
38650 ["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"]
38653 "Cape Verde (Kabu Verdi)",
38658 "Caribbean Netherlands",
38669 "Central African Republic (République centrafricaine)",
38689 "Christmas Island",
38695 "Cocos (Keeling) Islands",
38706 "Comoros (جزر القمر)",
38711 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38716 "Congo (Republic) (Congo-Brazzaville)",
38736 "Croatia (Hrvatska)",
38757 "Czech Republic (Česká republika)",
38762 "Denmark (Danmark)",
38777 "Dominican Republic (República Dominicana)",
38781 ["809", "829", "849"]
38799 "Equatorial Guinea (Guinea Ecuatorial)",
38819 "Falkland Islands (Islas Malvinas)",
38824 "Faroe Islands (Føroyar)",
38845 "French Guiana (Guyane française)",
38850 "French Polynesia (Polynésie française)",
38865 "Georgia (საქართველო)",
38870 "Germany (Deutschland)",
38890 "Greenland (Kalaallit Nunaat)",
38927 "Guinea-Bissau (Guiné Bissau)",
38952 "Hungary (Magyarország)",
38957 "Iceland (Ísland)",
38977 "Iraq (العراق)",
38993 "Israel (ישראל)",
39020 "Jordan (الأردن)",
39025 "Kazakhstan (Казахстан)",
39046 "Kuwait (الكويت)",
39051 "Kyrgyzstan (Кыргызстан)",
39061 "Latvia (Latvija)",
39066 "Lebanon (لبنان)",
39081 "Libya (ليبيا)",
39091 "Lithuania (Lietuva)",
39106 "Macedonia (FYROM) (Македонија)",
39111 "Madagascar (Madagasikara)",
39141 "Marshall Islands",
39151 "Mauritania (موريتانيا)",
39156 "Mauritius (Moris)",
39177 "Moldova (Republica Moldova)",
39187 "Mongolia (Монгол)",
39192 "Montenegro (Crna Gora)",
39202 "Morocco (المغرب)",
39208 "Mozambique (Moçambique)",
39213 "Myanmar (Burma) (မြန်မာ)",
39218 "Namibia (Namibië)",
39233 "Netherlands (Nederland)",
39238 "New Caledonia (Nouvelle-Calédonie)",
39273 "North Korea (조선 민주주의 인민 공화국)",
39278 "Northern Mariana Islands",
39294 "Pakistan (پاکستان)",
39304 "Palestine (فلسطين)",
39314 "Papua New Guinea",
39356 "Réunion (La Réunion)",
39362 "Romania (România)",
39378 "Saint Barthélemy",
39389 "Saint Kitts and Nevis",
39399 "Saint Martin (Saint-Martin (partie française))",
39405 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39410 "Saint Vincent and the Grenadines",
39425 "São Tomé and Príncipe (São Tomé e Príncipe)",
39430 "Saudi Arabia (المملكة العربية السعودية)",
39435 "Senegal (Sénégal)",
39465 "Slovakia (Slovensko)",
39470 "Slovenia (Slovenija)",
39480 "Somalia (Soomaaliya)",
39490 "South Korea (대한민국)",
39495 "South Sudan (جنوب السودان)",
39505 "Sri Lanka (ශ්රී ලංකාව)",
39510 "Sudan (السودان)",
39520 "Svalbard and Jan Mayen",
39531 "Sweden (Sverige)",
39536 "Switzerland (Schweiz)",
39541 "Syria (سوريا)",
39586 "Trinidad and Tobago",
39591 "Tunisia (تونس)",
39596 "Turkey (Türkiye)",
39606 "Turks and Caicos Islands",
39616 "U.S. Virgin Islands",
39626 "Ukraine (Україна)",
39631 "United Arab Emirates (الإمارات العربية المتحدة)",
39653 "Uzbekistan (Oʻzbekiston)",
39663 "Vatican City (Città del Vaticano)",
39674 "Vietnam (Việt Nam)",
39679 "Wallis and Futuna (Wallis-et-Futuna)",
39684 "Western Sahara (الصحراء الغربية)",
39690 "Yemen (اليمن)",
39714 * This script refer to:
39715 * Title: International Telephone Input
39716 * Author: Jack O'Connor
39717 * Code version: v12.1.12
39718 * Availability: https://github.com/jackocnr/intl-tel-input.git
39722 * @class Roo.bootstrap.PhoneInput
39723 * @extends Roo.bootstrap.TriggerField
39724 * An input with International dial-code selection
39726 * @cfg {String} defaultDialCode default '+852'
39727 * @cfg {Array} preferedCountries default []
39730 * Create a new PhoneInput.
39731 * @param {Object} config Configuration options
39734 Roo.bootstrap.PhoneInput = function(config) {
39735 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39738 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39740 listWidth: undefined,
39742 selectedClass: 'active',
39744 invalidClass : "has-warning",
39746 validClass: 'has-success',
39748 allowed: '0123456789',
39751 * @cfg {String} defaultDialCode The default dial code when initializing the input
39753 defaultDialCode: '+852',
39756 * @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
39758 preferedCountries: false,
39760 getAutoCreate : function()
39762 var data = Roo.bootstrap.PhoneInputData();
39763 var align = this.labelAlign || this.parentLabelAlign();
39766 this.allCountries = [];
39767 this.dialCodeMapping = [];
39769 for (var i = 0; i < data.length; i++) {
39771 this.allCountries[i] = {
39775 priority: c[3] || 0,
39776 areaCodes: c[4] || null
39778 this.dialCodeMapping[c[2]] = {
39781 priority: c[3] || 0,
39782 areaCodes: c[4] || null
39794 cls : 'form-control tel-input',
39795 autocomplete: 'new-password'
39798 var hiddenInput = {
39801 cls: 'hidden-tel-input'
39805 hiddenInput.name = this.name;
39808 if (this.disabled) {
39809 input.disabled = true;
39812 var flag_container = {
39829 cls: this.hasFeedback ? 'has-feedback' : '',
39835 cls: 'dial-code-holder',
39842 cls: 'roo-select2-container input-group',
39849 if (this.fieldLabel.length) {
39852 tooltip: 'This field is required'
39858 cls: 'control-label',
39864 html: this.fieldLabel
39867 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39873 if(this.indicatorpos == 'right') {
39874 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39881 if(align == 'left') {
39889 if(this.labelWidth > 12){
39890 label.style = "width: " + this.labelWidth + 'px';
39892 if(this.labelWidth < 13 && this.labelmd == 0){
39893 this.labelmd = this.labelWidth;
39895 if(this.labellg > 0){
39896 label.cls += ' col-lg-' + this.labellg;
39897 input.cls += ' col-lg-' + (12 - this.labellg);
39899 if(this.labelmd > 0){
39900 label.cls += ' col-md-' + this.labelmd;
39901 container.cls += ' col-md-' + (12 - this.labelmd);
39903 if(this.labelsm > 0){
39904 label.cls += ' col-sm-' + this.labelsm;
39905 container.cls += ' col-sm-' + (12 - this.labelsm);
39907 if(this.labelxs > 0){
39908 label.cls += ' col-xs-' + this.labelxs;
39909 container.cls += ' col-xs-' + (12 - this.labelxs);
39919 var settings = this;
39921 ['xs','sm','md','lg'].map(function(size){
39922 if (settings[size]) {
39923 cfg.cls += ' col-' + size + '-' + settings[size];
39927 this.store = new Roo.data.Store({
39928 proxy : new Roo.data.MemoryProxy({}),
39929 reader : new Roo.data.JsonReader({
39940 'name' : 'dialCode',
39944 'name' : 'priority',
39948 'name' : 'areaCodes',
39955 if(!this.preferedCountries) {
39956 this.preferedCountries = [
39963 var p = this.preferedCountries.reverse();
39966 for (var i = 0; i < p.length; i++) {
39967 for (var j = 0; j < this.allCountries.length; j++) {
39968 if(this.allCountries[j].iso2 == p[i]) {
39969 var t = this.allCountries[j];
39970 this.allCountries.splice(j,1);
39971 this.allCountries.unshift(t);
39977 this.store.proxy.data = {
39979 data: this.allCountries
39985 initEvents : function()
39988 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39990 this.indicator = this.indicatorEl();
39991 this.flag = this.flagEl();
39992 this.dialCodeHolder = this.dialCodeHolderEl();
39994 this.trigger = this.el.select('div.flag-box',true).first();
39995 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40000 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40001 _this.list.setWidth(lw);
40004 this.list.on('mouseover', this.onViewOver, this);
40005 this.list.on('mousemove', this.onViewMove, this);
40006 this.inputEl().on("keyup", this.onKeyUp, this);
40008 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40010 this.view = new Roo.View(this.list, this.tpl, {
40011 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40014 this.view.on('click', this.onViewClick, this);
40015 this.setValue(this.defaultDialCode);
40018 onTriggerClick : function(e)
40020 Roo.log('trigger click');
40025 if(this.isExpanded()){
40027 this.hasFocus = false;
40029 this.store.load({});
40030 this.hasFocus = true;
40035 isExpanded : function()
40037 return this.list.isVisible();
40040 collapse : function()
40042 if(!this.isExpanded()){
40046 Roo.get(document).un('mousedown', this.collapseIf, this);
40047 Roo.get(document).un('mousewheel', this.collapseIf, this);
40048 this.fireEvent('collapse', this);
40052 expand : function()
40056 if(this.isExpanded() || !this.hasFocus){
40060 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40061 this.list.setWidth(lw);
40064 this.restrictHeight();
40066 Roo.get(document).on('mousedown', this.collapseIf, this);
40067 Roo.get(document).on('mousewheel', this.collapseIf, this);
40069 this.fireEvent('expand', this);
40072 restrictHeight : function()
40074 this.list.alignTo(this.inputEl(), this.listAlign);
40075 this.list.alignTo(this.inputEl(), this.listAlign);
40078 onViewOver : function(e, t)
40080 if(this.inKeyMode){
40083 var item = this.view.findItemFromChild(t);
40086 var index = this.view.indexOf(item);
40087 this.select(index, false);
40092 onViewClick : function(view, doFocus, el, e)
40094 var index = this.view.getSelectedIndexes()[0];
40096 var r = this.store.getAt(index);
40099 this.onSelect(r, index);
40101 if(doFocus !== false && !this.blockFocus){
40102 this.inputEl().focus();
40106 onViewMove : function(e, t)
40108 this.inKeyMode = false;
40111 select : function(index, scrollIntoView)
40113 this.selectedIndex = index;
40114 this.view.select(index);
40115 if(scrollIntoView !== false){
40116 var el = this.view.getNode(index);
40118 this.list.scrollChildIntoView(el, false);
40123 createList : function()
40125 this.list = Roo.get(document.body).createChild({
40127 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40128 style: 'display:none'
40131 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40134 collapseIf : function(e)
40136 var in_combo = e.within(this.el);
40137 var in_list = e.within(this.list);
40138 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40140 if (in_combo || in_list || is_list) {
40146 onSelect : function(record, index)
40148 if(this.fireEvent('beforeselect', this, record, index) !== false){
40150 this.setFlagClass(record.data.iso2);
40151 this.setDialCode(record.data.dialCode);
40152 this.hasFocus = false;
40154 this.fireEvent('select', this, record, index);
40158 flagEl : function()
40160 var flag = this.el.select('div.flag',true).first();
40167 dialCodeHolderEl : function()
40169 var d = this.el.select('input.dial-code-holder',true).first();
40176 setDialCode : function(v)
40178 this.dialCodeHolder.dom.value = '+'+v;
40181 setFlagClass : function(n)
40183 this.flag.dom.className = 'flag '+n;
40186 getValue : function()
40188 var v = this.inputEl().getValue();
40189 if(this.dialCodeHolder) {
40190 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40195 setValue : function(v)
40197 var d = this.getDialCode(v);
40199 //invalid dial code
40200 if(v.length == 0 || !d || d.length == 0) {
40202 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40203 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40209 this.setFlagClass(this.dialCodeMapping[d].iso2);
40210 this.setDialCode(d);
40211 this.inputEl().dom.value = v.replace('+'+d,'');
40212 this.hiddenEl().dom.value = this.getValue();
40217 getDialCode : function(v)
40221 if (v.length == 0) {
40222 return this.dialCodeHolder.dom.value;
40226 if (v.charAt(0) != "+") {
40229 var numericChars = "";
40230 for (var i = 1; i < v.length; i++) {
40231 var c = v.charAt(i);
40234 if (this.dialCodeMapping[numericChars]) {
40235 dialCode = v.substr(1, i);
40237 if (numericChars.length == 4) {
40247 this.setValue(this.defaultDialCode);
40251 hiddenEl : function()
40253 return this.el.select('input.hidden-tel-input',true).first();
40256 onKeyUp : function(e){
40258 var k = e.getKey();
40259 var c = e.getCharCode();
40262 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40263 this.allowed.indexOf(String.fromCharCode(c)) === -1
40268 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40271 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40275 this.setValue(this.getValue());
40280 * @class Roo.bootstrap.MoneyField
40281 * @extends Roo.bootstrap.ComboBox
40282 * Bootstrap MoneyField class
40285 * Create a new MoneyField.
40286 * @param {Object} config Configuration options
40289 Roo.bootstrap.MoneyField = function(config) {
40291 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40295 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40298 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40300 allowDecimals : true,
40302 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40304 decimalSeparator : ".",
40306 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40308 decimalPrecision : 0,
40310 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40312 allowNegative : true,
40314 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40318 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40320 minValue : Number.NEGATIVE_INFINITY,
40322 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40324 maxValue : Number.MAX_VALUE,
40326 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40328 minText : "The minimum value for this field is {0}",
40330 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40332 maxText : "The maximum value for this field is {0}",
40334 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40335 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40337 nanText : "{0} is not a valid number",
40339 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40343 * @cfg {String} defaults currency of the MoneyField
40344 * value should be in lkey
40346 defaultCurrency : false,
40348 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40350 thousandsDelimiter : false,
40360 getAutoCreate : function()
40362 var align = this.labelAlign || this.parentLabelAlign();
40374 cls : 'form-control roo-money-amount-input',
40375 autocomplete: 'new-password'
40378 var hiddenInput = {
40382 cls: 'hidden-number-input'
40386 hiddenInput.name = this.name;
40389 if (this.disabled) {
40390 input.disabled = true;
40393 var clg = 12 - this.inputlg;
40394 var cmd = 12 - this.inputmd;
40395 var csm = 12 - this.inputsm;
40396 var cxs = 12 - this.inputxs;
40400 cls : 'row roo-money-field',
40404 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40408 cls: 'roo-select2-container input-group',
40412 cls : 'form-control roo-money-currency-input',
40413 autocomplete: 'new-password',
40415 name : this.currencyName
40419 cls : 'input-group-addon',
40433 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40437 cls: this.hasFeedback ? 'has-feedback' : '',
40448 if (this.fieldLabel.length) {
40451 tooltip: 'This field is required'
40457 cls: 'control-label',
40463 html: this.fieldLabel
40466 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40472 if(this.indicatorpos == 'right') {
40473 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40480 if(align == 'left') {
40488 if(this.labelWidth > 12){
40489 label.style = "width: " + this.labelWidth + 'px';
40491 if(this.labelWidth < 13 && this.labelmd == 0){
40492 this.labelmd = this.labelWidth;
40494 if(this.labellg > 0){
40495 label.cls += ' col-lg-' + this.labellg;
40496 input.cls += ' col-lg-' + (12 - this.labellg);
40498 if(this.labelmd > 0){
40499 label.cls += ' col-md-' + this.labelmd;
40500 container.cls += ' col-md-' + (12 - this.labelmd);
40502 if(this.labelsm > 0){
40503 label.cls += ' col-sm-' + this.labelsm;
40504 container.cls += ' col-sm-' + (12 - this.labelsm);
40506 if(this.labelxs > 0){
40507 label.cls += ' col-xs-' + this.labelxs;
40508 container.cls += ' col-xs-' + (12 - this.labelxs);
40519 var settings = this;
40521 ['xs','sm','md','lg'].map(function(size){
40522 if (settings[size]) {
40523 cfg.cls += ' col-' + size + '-' + settings[size];
40530 initEvents : function()
40532 this.indicator = this.indicatorEl();
40534 this.initCurrencyEvent();
40536 this.initNumberEvent();
40539 initCurrencyEvent : function()
40542 throw "can not find store for combo";
40545 this.store = Roo.factory(this.store, Roo.data);
40546 this.store.parent = this;
40550 this.triggerEl = this.el.select('.input-group-addon', true).first();
40552 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40557 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40558 _this.list.setWidth(lw);
40561 this.list.on('mouseover', this.onViewOver, this);
40562 this.list.on('mousemove', this.onViewMove, this);
40563 this.list.on('scroll', this.onViewScroll, this);
40566 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40569 this.view = new Roo.View(this.list, this.tpl, {
40570 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40573 this.view.on('click', this.onViewClick, this);
40575 this.store.on('beforeload', this.onBeforeLoad, this);
40576 this.store.on('load', this.onLoad, this);
40577 this.store.on('loadexception', this.onLoadException, this);
40579 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40580 "up" : function(e){
40581 this.inKeyMode = true;
40585 "down" : function(e){
40586 if(!this.isExpanded()){
40587 this.onTriggerClick();
40589 this.inKeyMode = true;
40594 "enter" : function(e){
40597 if(this.fireEvent("specialkey", this, e)){
40598 this.onViewClick(false);
40604 "esc" : function(e){
40608 "tab" : function(e){
40611 if(this.fireEvent("specialkey", this, e)){
40612 this.onViewClick(false);
40620 doRelay : function(foo, bar, hname){
40621 if(hname == 'down' || this.scope.isExpanded()){
40622 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40630 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40634 initNumberEvent : function(e)
40636 this.inputEl().on("keydown" , this.fireKey, this);
40637 this.inputEl().on("focus", this.onFocus, this);
40638 this.inputEl().on("blur", this.onBlur, this);
40640 this.inputEl().relayEvent('keyup', this);
40642 if(this.indicator){
40643 this.indicator.addClass('invisible');
40646 this.originalValue = this.getValue();
40648 if(this.validationEvent == 'keyup'){
40649 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40650 this.inputEl().on('keyup', this.filterValidation, this);
40652 else if(this.validationEvent !== false){
40653 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40656 if(this.selectOnFocus){
40657 this.on("focus", this.preFocus, this);
40660 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40661 this.inputEl().on("keypress", this.filterKeys, this);
40663 this.inputEl().relayEvent('keypress', this);
40666 var allowed = "0123456789";
40668 if(this.allowDecimals){
40669 allowed += this.decimalSeparator;
40672 if(this.allowNegative){
40676 if(this.thousandsDelimiter) {
40680 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40682 var keyPress = function(e){
40684 var k = e.getKey();
40686 var c = e.getCharCode();
40689 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40690 allowed.indexOf(String.fromCharCode(c)) === -1
40696 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40700 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40705 this.inputEl().on("keypress", keyPress, this);
40709 onTriggerClick : function(e)
40716 this.loadNext = false;
40718 if(this.isExpanded()){
40723 this.hasFocus = true;
40725 if(this.triggerAction == 'all') {
40726 this.doQuery(this.allQuery, true);
40730 this.doQuery(this.getRawValue());
40733 getCurrency : function()
40735 var v = this.currencyEl().getValue();
40740 restrictHeight : function()
40742 this.list.alignTo(this.currencyEl(), this.listAlign);
40743 this.list.alignTo(this.currencyEl(), this.listAlign);
40746 onViewClick : function(view, doFocus, el, e)
40748 var index = this.view.getSelectedIndexes()[0];
40750 var r = this.store.getAt(index);
40753 this.onSelect(r, index);
40757 onSelect : function(record, index){
40759 if(this.fireEvent('beforeselect', this, record, index) !== false){
40761 this.setFromCurrencyData(index > -1 ? record.data : false);
40765 this.fireEvent('select', this, record, index);
40769 setFromCurrencyData : function(o)
40773 this.lastCurrency = o;
40775 if (this.currencyField) {
40776 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40778 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40781 this.lastSelectionText = currency;
40783 //setting default currency
40784 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40785 this.setCurrency(this.defaultCurrency);
40789 this.setCurrency(currency);
40792 setFromData : function(o)
40796 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40798 this.setFromCurrencyData(c);
40803 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40805 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40808 this.setValue(value);
40812 setCurrency : function(v)
40814 this.currencyValue = v;
40817 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40822 setValue : function(v)
40824 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40830 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40832 this.inputEl().dom.value = (v == '') ? '' :
40833 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40835 if(!this.allowZero && v === '0') {
40836 this.hiddenEl().dom.value = '';
40837 this.inputEl().dom.value = '';
40844 getRawValue : function()
40846 var v = this.inputEl().getValue();
40851 getValue : function()
40853 return this.fixPrecision(this.parseValue(this.getRawValue()));
40856 parseValue : function(value)
40858 if(this.thousandsDelimiter) {
40860 r = new RegExp(",", "g");
40861 value = value.replace(r, "");
40864 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40865 return isNaN(value) ? '' : value;
40869 fixPrecision : function(value)
40871 if(this.thousandsDelimiter) {
40873 r = new RegExp(",", "g");
40874 value = value.replace(r, "");
40877 var nan = isNaN(value);
40879 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40880 return nan ? '' : value;
40882 return parseFloat(value).toFixed(this.decimalPrecision);
40885 decimalPrecisionFcn : function(v)
40887 return Math.floor(v);
40890 validateValue : function(value)
40892 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40896 var num = this.parseValue(value);
40899 this.markInvalid(String.format(this.nanText, value));
40903 if(num < this.minValue){
40904 this.markInvalid(String.format(this.minText, this.minValue));
40908 if(num > this.maxValue){
40909 this.markInvalid(String.format(this.maxText, this.maxValue));
40916 validate : function()
40918 if(this.disabled || this.allowBlank){
40923 var currency = this.getCurrency();
40925 if(this.validateValue(this.getRawValue()) && currency.length){
40930 this.markInvalid();
40934 getName: function()
40939 beforeBlur : function()
40945 var v = this.parseValue(this.getRawValue());
40952 onBlur : function()
40956 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40957 //this.el.removeClass(this.focusClass);
40960 this.hasFocus = false;
40962 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40966 var v = this.getValue();
40968 if(String(v) !== String(this.startValue)){
40969 this.fireEvent('change', this, v, this.startValue);
40972 this.fireEvent("blur", this);
40975 inputEl : function()
40977 return this.el.select('.roo-money-amount-input', true).first();
40980 currencyEl : function()
40982 return this.el.select('.roo-money-currency-input', true).first();
40985 hiddenEl : function()
40987 return this.el.select('input.hidden-number-input',true).first();