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().dom.setAttribute('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2650 onRender : function(ct, position)
2652 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2655 var cfg = Roo.apply({}, this.getAutoCreate());
2658 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660 //if (!cfg.name.length) {
2664 cfg.cls += ' ' + this.cls;
2667 cfg.style = this.style;
2669 this.el = Roo.get(document.body).createChild(cfg, position);
2671 //var type = this.el.dom.type;
2674 if(this.tabIndex !== undefined){
2675 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2678 this.dialogEl = this.el.select('.modal-dialog',true).first();
2679 this.bodyEl = this.el.select('.modal-body',true).first();
2680 this.closeEl = this.el.select('.modal-header .close', true).first();
2681 this.headerEl = this.el.select('.modal-header',true).first();
2682 this.titleEl = this.el.select('.modal-title',true).first();
2683 this.footerEl = this.el.select('.modal-footer',true).first();
2685 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687 //this.el.addClass("x-dlg-modal");
2689 if (this.buttons.length) {
2690 Roo.each(this.buttons, function(bb) {
2691 var b = Roo.apply({}, bb);
2692 b.xns = b.xns || Roo.bootstrap;
2693 b.xtype = b.xtype || 'Button';
2694 if (typeof(b.listeners) == 'undefined') {
2695 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2698 var btn = Roo.factory(b);
2700 btn.render(this.el.select('.modal-footer div').first());
2704 // render the children.
2707 if(typeof(this.items) != 'undefined'){
2708 var items = this.items;
2711 for(var i =0;i < items.length;i++) {
2712 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2716 this.items = nitems;
2718 // where are these used - they used to be body/close/footer
2722 //this.el.addClass([this.fieldClass, this.cls]);
2726 getAutoCreate : function()
2730 html : this.html || ''
2735 cls : 'modal-title',
2739 if(this.specificTitle){
2745 if (this.allow_close) {
2757 if(this.size.length){
2758 size = 'modal-' + this.size;
2765 cls: "modal-dialog " + size,
2768 cls : "modal-content",
2771 cls : 'modal-header',
2776 cls : 'modal-footer',
2780 cls: 'btn-' + this.buttonPosition
2797 modal.cls += ' fade';
2803 getChildContainer : function() {
2808 getButtonContainer : function() {
2809 return this.el.select('.modal-footer div',true).first();
2812 initEvents : function()
2814 if (this.allow_close) {
2815 this.closeEl.on('click', this.hide, this);
2817 Roo.EventManager.onWindowResize(this.resize, this, true);
2824 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2826 if (this.fitwindow) {
2827 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2832 if(!this.fitwindow && this.max_width !== 0){
2834 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2835 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2841 setSize : function(w,h)
2851 if (!this.rendered) {
2855 //this.el.setStyle('display', 'block');
2856 this.el.removeClass('hideing');
2857 this.el.addClass('show');
2859 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2862 this.el.addClass('in');
2865 this.el.addClass('in');
2868 // not sure how we can show data in here..
2870 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2873 Roo.get(document.body).addClass("x-body-masked");
2875 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2876 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2877 this.maskEl.addClass('show');
2881 this.fireEvent('show', this);
2883 // set zindex here - otherwise it appears to be ignored...
2884 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2887 this.items.forEach( function(e) {
2888 e.layout ? e.layout() : false;
2896 if(this.fireEvent("beforehide", this) !== false){
2897 this.maskEl.removeClass('show');
2898 Roo.get(document.body).removeClass("x-body-masked");
2899 this.el.removeClass('in');
2900 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2902 if(this.animate){ // why
2903 this.el.addClass('hideing');
2905 if (!this.el.hasClass('hideing')) {
2906 return; // it's been shown again...
2908 this.el.removeClass('show');
2909 this.el.removeClass('hideing');
2913 this.el.removeClass('show');
2915 this.fireEvent('hide', this);
2918 isVisible : function()
2921 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2925 addButton : function(str, cb)
2929 var b = Roo.apply({}, { html : str } );
2930 b.xns = b.xns || Roo.bootstrap;
2931 b.xtype = b.xtype || 'Button';
2932 if (typeof(b.listeners) == 'undefined') {
2933 b.listeners = { click : cb.createDelegate(this) };
2936 var btn = Roo.factory(b);
2938 btn.render(this.el.select('.modal-footer div').first());
2944 setDefaultButton : function(btn)
2946 //this.el.select('.modal-footer').()
2950 resizeTo: function(w,h)
2954 this.dialogEl.setWidth(w);
2955 if (this.diff === false) {
2956 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2959 this.bodyEl.setHeight(h-this.diff);
2961 this.fireEvent('resize', this);
2964 setContentSize : function(w, h)
2968 onButtonClick: function(btn,e)
2971 this.fireEvent('btnclick', btn.name, e);
2974 * Set the title of the Dialog
2975 * @param {String} str new Title
2977 setTitle: function(str) {
2978 this.titleEl.dom.innerHTML = str;
2981 * Set the body of the Dialog
2982 * @param {String} str new Title
2984 setBody: function(str) {
2985 this.bodyEl.dom.innerHTML = str;
2988 * Set the body of the Dialog using the template
2989 * @param {Obj} data - apply this data to the template and replace the body contents.
2991 applyBody: function(obj)
2994 Roo.log("Error - using apply Body without a template");
2997 this.tmpl.overwrite(this.bodyEl, obj);
3003 Roo.apply(Roo.bootstrap.Modal, {
3005 * Button config that displays a single OK button
3014 * Button config that displays Yes and No buttons
3030 * Button config that displays OK and Cancel buttons
3045 * Button config that displays Yes, No and Cancel buttons
3069 * messagebox - can be used as a replace
3073 * @class Roo.MessageBox
3074 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3078 Roo.Msg.alert('Status', 'Changes saved successfully.');
3080 // Prompt for user data:
3081 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3083 // process text value...
3087 // Show a dialog using config options:
3089 title:'Save Changes?',
3090 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3091 buttons: Roo.Msg.YESNOCANCEL,
3098 Roo.bootstrap.MessageBox = function(){
3099 var dlg, opt, mask, waitTimer;
3100 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3101 var buttons, activeTextEl, bwidth;
3105 var handleButton = function(button){
3107 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3111 var handleHide = function(){
3113 dlg.el.removeClass(opt.cls);
3116 // Roo.TaskMgr.stop(waitTimer);
3117 // waitTimer = null;
3122 var updateButtons = function(b){
3125 buttons["ok"].hide();
3126 buttons["cancel"].hide();
3127 buttons["yes"].hide();
3128 buttons["no"].hide();
3129 //dlg.footer.dom.style.display = 'none';
3132 dlg.footerEl.dom.style.display = '';
3133 for(var k in buttons){
3134 if(typeof buttons[k] != "function"){
3137 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3138 width += buttons[k].el.getWidth()+15;
3148 var handleEsc = function(d, k, e){
3149 if(opt && opt.closable !== false){
3159 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3160 * @return {Roo.BasicDialog} The BasicDialog element
3162 getDialog : function(){
3164 dlg = new Roo.bootstrap.Modal( {
3167 //constraintoviewport:false,
3169 //collapsible : false,
3174 //buttonAlign:"center",
3175 closeClick : function(){
3176 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3179 handleButton("cancel");
3184 dlg.on("hide", handleHide);
3186 //dlg.addKeyListener(27, handleEsc);
3188 this.buttons = buttons;
3189 var bt = this.buttonText;
3190 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3191 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3192 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3193 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3195 bodyEl = dlg.bodyEl.createChild({
3197 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3198 '<textarea class="roo-mb-textarea"></textarea>' +
3199 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3201 msgEl = bodyEl.dom.firstChild;
3202 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3203 textboxEl.enableDisplayMode();
3204 textboxEl.addKeyListener([10,13], function(){
3205 if(dlg.isVisible() && opt && opt.buttons){
3208 }else if(opt.buttons.yes){
3209 handleButton("yes");
3213 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3214 textareaEl.enableDisplayMode();
3215 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3216 progressEl.enableDisplayMode();
3218 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3219 var pf = progressEl.dom.firstChild;
3221 pp = Roo.get(pf.firstChild);
3222 pp.setHeight(pf.offsetHeight);
3230 * Updates the message box body text
3231 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3232 * the XHTML-compliant non-breaking space character '&#160;')
3233 * @return {Roo.MessageBox} This message box
3235 updateText : function(text)
3237 if(!dlg.isVisible() && !opt.width){
3238 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3239 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3241 msgEl.innerHTML = text || ' ';
3243 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3244 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3246 Math.min(opt.width || cw , this.maxWidth),
3247 Math.max(opt.minWidth || this.minWidth, bwidth)
3250 activeTextEl.setWidth(w);
3252 if(dlg.isVisible()){
3253 dlg.fixedcenter = false;
3255 // to big, make it scroll. = But as usual stupid IE does not support
3258 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3259 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3260 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3262 bodyEl.dom.style.height = '';
3263 bodyEl.dom.style.overflowY = '';
3266 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3268 bodyEl.dom.style.overflowX = '';
3271 dlg.setContentSize(w, bodyEl.getHeight());
3272 if(dlg.isVisible()){
3273 dlg.fixedcenter = true;
3279 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3280 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3281 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3282 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3283 * @return {Roo.MessageBox} This message box
3285 updateProgress : function(value, text){
3287 this.updateText(text);
3290 if (pp) { // weird bug on my firefox - for some reason this is not defined
3291 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3292 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3298 * Returns true if the message box is currently displayed
3299 * @return {Boolean} True if the message box is visible, else false
3301 isVisible : function(){
3302 return dlg && dlg.isVisible();
3306 * Hides the message box if it is displayed
3309 if(this.isVisible()){
3315 * Displays a new message box, or reinitializes an existing message box, based on the config options
3316 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3317 * The following config object properties are supported:
3319 Property Type Description
3320 ---------- --------------- ------------------------------------------------------------------------------------
3321 animEl String/Element An id or Element from which the message box should animate as it opens and
3322 closes (defaults to undefined)
3323 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3324 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3325 closable Boolean False to hide the top-right close button (defaults to true). Note that
3326 progress and wait dialogs will ignore this property and always hide the
3327 close button as they can only be closed programmatically.
3328 cls String A custom CSS class to apply to the message box element
3329 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3330 displayed (defaults to 75)
3331 fn Function A callback function to execute after closing the dialog. The arguments to the
3332 function will be btn (the name of the button that was clicked, if applicable,
3333 e.g. "ok"), and text (the value of the active text field, if applicable).
3334 Progress and wait dialogs will ignore this option since they do not respond to
3335 user actions and can only be closed programmatically, so any required function
3336 should be called by the same code after it closes the dialog.
3337 icon String A CSS class that provides a background image to be used as an icon for
3338 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3339 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3340 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3341 modal Boolean False to allow user interaction with the page while the message box is
3342 displayed (defaults to true)
3343 msg String A string that will replace the existing message box body text (defaults
3344 to the XHTML-compliant non-breaking space character ' ')
3345 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3346 progress Boolean True to display a progress bar (defaults to false)
3347 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3348 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3349 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3350 title String The title text
3351 value String The string value to set into the active textbox element if displayed
3352 wait Boolean True to display a progress bar (defaults to false)
3353 width Number The width of the dialog in pixels
3360 msg: 'Please enter your address:',
3362 buttons: Roo.MessageBox.OKCANCEL,
3365 animEl: 'addAddressBtn'
3368 * @param {Object} config Configuration options
3369 * @return {Roo.MessageBox} This message box
3371 show : function(options)
3374 // this causes nightmares if you show one dialog after another
3375 // especially on callbacks..
3377 if(this.isVisible()){
3380 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3381 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3382 Roo.log("New Dialog Message:" + options.msg )
3383 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3384 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3387 var d = this.getDialog();
3389 d.setTitle(opt.title || " ");
3390 d.closeEl.setDisplayed(opt.closable !== false);
3391 activeTextEl = textboxEl;
3392 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3397 textareaEl.setHeight(typeof opt.multiline == "number" ?
3398 opt.multiline : this.defaultTextHeight);
3399 activeTextEl = textareaEl;
3408 progressEl.setDisplayed(opt.progress === true);
3409 this.updateProgress(0);
3410 activeTextEl.dom.value = opt.value || "";
3412 dlg.setDefaultButton(activeTextEl);
3414 var bs = opt.buttons;
3418 }else if(bs && bs.yes){
3419 db = buttons["yes"];
3421 dlg.setDefaultButton(db);
3423 bwidth = updateButtons(opt.buttons);
3424 this.updateText(opt.msg);
3426 d.el.addClass(opt.cls);
3428 d.proxyDrag = opt.proxyDrag === true;
3429 d.modal = opt.modal !== false;
3430 d.mask = opt.modal !== false ? mask : false;
3432 // force it to the end of the z-index stack so it gets a cursor in FF
3433 document.body.appendChild(dlg.el.dom);
3434 d.animateTarget = null;
3435 d.show(options.animEl);
3441 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3442 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3443 * and closing the message box when the process is complete.
3444 * @param {String} title The title bar text
3445 * @param {String} msg The message box body text
3446 * @return {Roo.MessageBox} This message box
3448 progress : function(title, msg){
3455 minWidth: this.minProgressWidth,
3462 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3463 * If a callback function is passed it will be called after the user clicks the button, and the
3464 * id of the button that was clicked will be passed as the only parameter to the callback
3465 * (could also be the top-right close button).
3466 * @param {String} title The title bar text
3467 * @param {String} msg The message box body text
3468 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3469 * @param {Object} scope (optional) The scope of the callback function
3470 * @return {Roo.MessageBox} This message box
3472 alert : function(title, msg, fn, scope)
3487 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3488 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3489 * You are responsible for closing the message box when the process is complete.
3490 * @param {String} msg The message box body text
3491 * @param {String} title (optional) The title bar text
3492 * @return {Roo.MessageBox} This message box
3494 wait : function(msg, title){
3505 waitTimer = Roo.TaskMgr.start({
3507 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3515 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3516 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3517 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3518 * @param {String} title The title bar text
3519 * @param {String} msg The message box body text
3520 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3521 * @param {Object} scope (optional) The scope of the callback function
3522 * @return {Roo.MessageBox} This message box
3524 confirm : function(title, msg, fn, scope){
3528 buttons: this.YESNO,
3537 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3538 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3539 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3540 * (could also be the top-right close button) and the text that was entered will be passed as the two
3541 * parameters to the callback.
3542 * @param {String} title The title bar text
3543 * @param {String} msg The message box body text
3544 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3545 * @param {Object} scope (optional) The scope of the callback function
3546 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3547 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3548 * @return {Roo.MessageBox} This message box
3550 prompt : function(title, msg, fn, scope, multiline){
3554 buttons: this.OKCANCEL,
3559 multiline: multiline,
3566 * Button config that displays a single OK button
3571 * Button config that displays Yes and No buttons
3574 YESNO : {yes:true, no:true},
3576 * Button config that displays OK and Cancel buttons
3579 OKCANCEL : {ok:true, cancel:true},
3581 * Button config that displays Yes, No and Cancel buttons
3584 YESNOCANCEL : {yes:true, no:true, cancel:true},
3587 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3590 defaultTextHeight : 75,
3592 * The maximum width in pixels of the message box (defaults to 600)
3597 * The minimum width in pixels of the message box (defaults to 100)
3602 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3603 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3606 minProgressWidth : 250,
3608 * An object containing the default button text strings that can be overriden for localized language support.
3609 * Supported properties are: ok, cancel, yes and no.
3610 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3623 * Shorthand for {@link Roo.MessageBox}
3625 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3626 Roo.Msg = Roo.Msg || Roo.MessageBox;
3635 * @class Roo.bootstrap.Navbar
3636 * @extends Roo.bootstrap.Component
3637 * Bootstrap Navbar class
3640 * Create a new Navbar
3641 * @param {Object} config The config object
3645 Roo.bootstrap.Navbar = function(config){
3646 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3650 * @event beforetoggle
3651 * Fire before toggle the menu
3652 * @param {Roo.EventObject} e
3654 "beforetoggle" : true
3658 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3667 getAutoCreate : function(){
3670 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3674 initEvents :function ()
3676 //Roo.log(this.el.select('.navbar-toggle',true));
3677 this.el.select('.navbar-toggle',true).on('click', function() {
3678 if(this.fireEvent('beforetoggle', this) !== false){
3679 this.el.select('.navbar-collapse',true).toggleClass('in');
3689 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3691 var size = this.el.getSize();
3692 this.maskEl.setSize(size.width, size.height);
3693 this.maskEl.enableDisplayMode("block");
3702 getChildContainer : function()
3704 if (this.el.select('.collapse').getCount()) {
3705 return this.el.select('.collapse',true).first();
3738 * @class Roo.bootstrap.NavSimplebar
3739 * @extends Roo.bootstrap.Navbar
3740 * Bootstrap Sidebar class
3742 * @cfg {Boolean} inverse is inverted color
3744 * @cfg {String} type (nav | pills | tabs)
3745 * @cfg {Boolean} arrangement stacked | justified
3746 * @cfg {String} align (left | right) alignment
3748 * @cfg {Boolean} main (true|false) main nav bar? default false
3749 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3751 * @cfg {String} tag (header|footer|nav|div) default is nav
3757 * Create a new Sidebar
3758 * @param {Object} config The config object
3762 Roo.bootstrap.NavSimplebar = function(config){
3763 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3766 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3782 getAutoCreate : function(){
3786 tag : this.tag || 'div',
3799 this.type = this.type || 'nav';
3800 if (['tabs','pills'].indexOf(this.type)!==-1) {
3801 cfg.cn[0].cls += ' nav-' + this.type
3805 if (this.type!=='nav') {
3806 Roo.log('nav type must be nav/tabs/pills')
3808 cfg.cn[0].cls += ' navbar-nav'
3814 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3815 cfg.cn[0].cls += ' nav-' + this.arrangement;
3819 if (this.align === 'right') {
3820 cfg.cn[0].cls += ' navbar-right';
3824 cfg.cls += ' navbar-inverse';
3851 * @class Roo.bootstrap.NavHeaderbar
3852 * @extends Roo.bootstrap.NavSimplebar
3853 * Bootstrap Sidebar class
3855 * @cfg {String} brand what is brand
3856 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3857 * @cfg {String} brand_href href of the brand
3858 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3859 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3860 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3861 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3864 * Create a new Sidebar
3865 * @param {Object} config The config object
3869 Roo.bootstrap.NavHeaderbar = function(config){
3870 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3874 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3881 desktopCenter : false,
3884 getAutoCreate : function(){
3887 tag: this.nav || 'nav',
3894 if (this.desktopCenter) {
3895 cn.push({cls : 'container', cn : []});
3902 cls: 'navbar-header',
3907 cls: 'navbar-toggle',
3908 'data-toggle': 'collapse',
3913 html: 'Toggle navigation'
3935 cls: 'collapse navbar-collapse',
3939 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3941 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3942 cfg.cls += ' navbar-' + this.position;
3944 // tag can override this..
3946 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3949 if (this.brand !== '') {
3952 href: this.brand_href ? this.brand_href : '#',
3953 cls: 'navbar-brand',
3961 cfg.cls += ' main-nav';
3969 getHeaderChildContainer : function()
3971 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3972 return this.el.select('.navbar-header',true).first();
3975 return this.getChildContainer();
3979 initEvents : function()
3981 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3983 if (this.autohide) {
3988 Roo.get(document).on('scroll',function(e) {
3989 var ns = Roo.get(document).getScroll().top;
3990 var os = prevScroll;
3994 ft.removeClass('slideDown');
3995 ft.addClass('slideUp');
3998 ft.removeClass('slideUp');
3999 ft.addClass('slideDown');
4020 * @class Roo.bootstrap.NavSidebar
4021 * @extends Roo.bootstrap.Navbar
4022 * Bootstrap Sidebar class
4025 * Create a new Sidebar
4026 * @param {Object} config The config object
4030 Roo.bootstrap.NavSidebar = function(config){
4031 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4034 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4036 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4038 getAutoCreate : function(){
4043 cls: 'sidebar sidebar-nav'
4065 * @class Roo.bootstrap.NavGroup
4066 * @extends Roo.bootstrap.Component
4067 * Bootstrap NavGroup class
4068 * @cfg {String} align (left|right)
4069 * @cfg {Boolean} inverse
4070 * @cfg {String} type (nav|pills|tab) default nav
4071 * @cfg {String} navId - reference Id for navbar.
4075 * Create a new nav group
4076 * @param {Object} config The config object
4079 Roo.bootstrap.NavGroup = function(config){
4080 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4083 Roo.bootstrap.NavGroup.register(this);
4087 * Fires when the active item changes
4088 * @param {Roo.bootstrap.NavGroup} this
4089 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4090 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4097 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4108 getAutoCreate : function()
4110 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4117 if (['tabs','pills'].indexOf(this.type)!==-1) {
4118 cfg.cls += ' nav-' + this.type
4120 if (this.type!=='nav') {
4121 Roo.log('nav type must be nav/tabs/pills')
4123 cfg.cls += ' navbar-nav'
4126 if (this.parent() && this.parent().sidebar) {
4129 cls: 'dashboard-menu sidebar-menu'
4135 if (this.form === true) {
4141 if (this.align === 'right') {
4142 cfg.cls += ' navbar-right';
4144 cfg.cls += ' navbar-left';
4148 if (this.align === 'right') {
4149 cfg.cls += ' navbar-right';
4153 cfg.cls += ' navbar-inverse';
4161 * sets the active Navigation item
4162 * @param {Roo.bootstrap.NavItem} the new current navitem
4164 setActiveItem : function(item)
4167 Roo.each(this.navItems, function(v){
4172 v.setActive(false, true);
4179 item.setActive(true, true);
4180 this.fireEvent('changed', this, item, prev);
4185 * gets the active Navigation item
4186 * @return {Roo.bootstrap.NavItem} the current navitem
4188 getActive : function()
4192 Roo.each(this.navItems, function(v){
4203 indexOfNav : function()
4207 Roo.each(this.navItems, function(v,i){
4218 * adds a Navigation item
4219 * @param {Roo.bootstrap.NavItem} the navitem to add
4221 addItem : function(cfg)
4223 var cn = new Roo.bootstrap.NavItem(cfg);
4225 cn.parentId = this.id;
4226 cn.onRender(this.el, null);
4230 * register a Navigation item
4231 * @param {Roo.bootstrap.NavItem} the navitem to add
4233 register : function(item)
4235 this.navItems.push( item);
4236 item.navId = this.navId;
4241 * clear all the Navigation item
4244 clearAll : function()
4247 this.el.dom.innerHTML = '';
4250 getNavItem: function(tabId)
4253 Roo.each(this.navItems, function(e) {
4254 if (e.tabId == tabId) {
4264 setActiveNext : function()
4266 var i = this.indexOfNav(this.getActive());
4267 if (i > this.navItems.length) {
4270 this.setActiveItem(this.navItems[i+1]);
4272 setActivePrev : function()
4274 var i = this.indexOfNav(this.getActive());
4278 this.setActiveItem(this.navItems[i-1]);
4280 clearWasActive : function(except) {
4281 Roo.each(this.navItems, function(e) {
4282 if (e.tabId != except.tabId && e.was_active) {
4283 e.was_active = false;
4290 getWasActive : function ()
4293 Roo.each(this.navItems, function(e) {
4308 Roo.apply(Roo.bootstrap.NavGroup, {
4312 * register a Navigation Group
4313 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4315 register : function(navgrp)
4317 this.groups[navgrp.navId] = navgrp;
4321 * fetch a Navigation Group based on the navigation ID
4322 * @param {string} the navgroup to add
4323 * @returns {Roo.bootstrap.NavGroup} the navgroup
4325 get: function(navId) {
4326 if (typeof(this.groups[navId]) == 'undefined') {
4328 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4330 return this.groups[navId] ;
4345 * @class Roo.bootstrap.NavItem
4346 * @extends Roo.bootstrap.Component
4347 * Bootstrap Navbar.NavItem class
4348 * @cfg {String} href link to
4349 * @cfg {String} html content of button
4350 * @cfg {String} badge text inside badge
4351 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4352 * @cfg {String} glyphicon name of glyphicon
4353 * @cfg {String} icon name of font awesome icon
4354 * @cfg {Boolean} active Is item active
4355 * @cfg {Boolean} disabled Is item disabled
4357 * @cfg {Boolean} preventDefault (true | false) default false
4358 * @cfg {String} tabId the tab that this item activates.
4359 * @cfg {String} tagtype (a|span) render as a href or span?
4360 * @cfg {Boolean} animateRef (true|false) link to element default false
4363 * Create a new Navbar Item
4364 * @param {Object} config The config object
4366 Roo.bootstrap.NavItem = function(config){
4367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4372 * The raw click event for the entire grid.
4373 * @param {Roo.EventObject} e
4378 * Fires when the active item active state changes
4379 * @param {Roo.bootstrap.NavItem} this
4380 * @param {boolean} state the new state
4386 * Fires when scroll to element
4387 * @param {Roo.bootstrap.NavItem} this
4388 * @param {Object} options
4389 * @param {Roo.EventObject} e
4397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4405 preventDefault : false,
4412 getAutoCreate : function(){
4421 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4423 if (this.disabled) {
4424 cfg.cls += ' disabled';
4427 if (this.href || this.html || this.glyphicon || this.icon) {
4431 href : this.href || "#",
4432 html: this.html || ''
4437 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4440 if(this.glyphicon) {
4441 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4446 cfg.cn[0].html += " <span class='caret'></span>";
4450 if (this.badge !== '') {
4452 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4460 initEvents: function()
4462 if (typeof (this.menu) != 'undefined') {
4463 this.menu.parentType = this.xtype;
4464 this.menu.triggerEl = this.el;
4465 this.menu = this.addxtype(Roo.apply({}, this.menu));
4468 this.el.select('a',true).on('click', this.onClick, this);
4470 if(this.tagtype == 'span'){
4471 this.el.select('span',true).on('click', this.onClick, this);
4474 // at this point parent should be available..
4475 this.parent().register(this);
4478 onClick : function(e)
4480 if (e.getTarget('.dropdown-menu-item')) {
4481 // did you click on a menu itemm.... - then don't trigger onclick..
4486 this.preventDefault ||
4489 Roo.log("NavItem - prevent Default?");
4493 if (this.disabled) {
4497 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4498 if (tg && tg.transition) {
4499 Roo.log("waiting for the transitionend");
4505 //Roo.log("fire event clicked");
4506 if(this.fireEvent('click', this, e) === false){
4510 if(this.tagtype == 'span'){
4514 //Roo.log(this.href);
4515 var ael = this.el.select('a',true).first();
4518 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4519 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4520 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4521 return; // ignore... - it's a 'hash' to another page.
4523 Roo.log("NavItem - prevent Default?");
4525 this.scrollToElement(e);
4529 var p = this.parent();
4531 if (['tabs','pills'].indexOf(p.type)!==-1) {
4532 if (typeof(p.setActiveItem) !== 'undefined') {
4533 p.setActiveItem(this);
4537 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4538 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4539 // remove the collapsed menu expand...
4540 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4544 isActive: function () {
4547 setActive : function(state, fire, is_was_active)
4549 if (this.active && !state && this.navId) {
4550 this.was_active = true;
4551 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4553 nv.clearWasActive(this);
4557 this.active = state;
4560 this.el.removeClass('active');
4561 } else if (!this.el.hasClass('active')) {
4562 this.el.addClass('active');
4565 this.fireEvent('changed', this, state);
4568 // show a panel if it's registered and related..
4570 if (!this.navId || !this.tabId || !state || is_was_active) {
4574 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4578 var pan = tg.getPanelByName(this.tabId);
4582 // if we can not flip to new panel - go back to old nav highlight..
4583 if (false == tg.showPanel(pan)) {
4584 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4586 var onav = nv.getWasActive();
4588 onav.setActive(true, false, true);
4597 // this should not be here...
4598 setDisabled : function(state)
4600 this.disabled = state;
4602 this.el.removeClass('disabled');
4603 } else if (!this.el.hasClass('disabled')) {
4604 this.el.addClass('disabled');
4610 * Fetch the element to display the tooltip on.
4611 * @return {Roo.Element} defaults to this.el
4613 tooltipEl : function()
4615 return this.el.select('' + this.tagtype + '', true).first();
4618 scrollToElement : function(e)
4620 var c = document.body;
4623 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4625 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4626 c = document.documentElement;
4629 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4635 var o = target.calcOffsetsTo(c);
4642 this.fireEvent('scrollto', this, options, e);
4644 Roo.get(c).scrollTo('top', options.value, true);
4657 * <span> icon </span>
4658 * <span> text </span>
4659 * <span>badge </span>
4663 * @class Roo.bootstrap.NavSidebarItem
4664 * @extends Roo.bootstrap.NavItem
4665 * Bootstrap Navbar.NavSidebarItem class
4666 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4667 * {Boolean} open is the menu open
4668 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4669 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4670 * {String} buttonSize (sm|md|lg)the extra classes for the button
4671 * {Boolean} showArrow show arrow next to the text (default true)
4673 * Create a new Navbar Button
4674 * @param {Object} config The config object
4676 Roo.bootstrap.NavSidebarItem = function(config){
4677 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4682 * The raw click event for the entire grid.
4683 * @param {Roo.EventObject} e
4688 * Fires when the active item active state changes
4689 * @param {Roo.bootstrap.NavSidebarItem} this
4690 * @param {boolean} state the new state
4698 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4700 badgeWeight : 'default',
4706 buttonWeight : 'default',
4712 getAutoCreate : function(){
4717 href : this.href || '#',
4723 if(this.buttonView){
4726 href : this.href || '#',
4727 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4740 cfg.cls += ' active';
4743 if (this.disabled) {
4744 cfg.cls += ' disabled';
4747 cfg.cls += ' open x-open';
4750 if (this.glyphicon || this.icon) {
4751 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4752 a.cn.push({ tag : 'i', cls : c }) ;
4755 if(!this.buttonView){
4758 html : this.html || ''
4765 if (this.badge !== '') {
4766 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4772 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4775 a.cls += ' dropdown-toggle treeview' ;
4781 initEvents : function()
4783 if (typeof (this.menu) != 'undefined') {
4784 this.menu.parentType = this.xtype;
4785 this.menu.triggerEl = this.el;
4786 this.menu = this.addxtype(Roo.apply({}, this.menu));
4789 this.el.on('click', this.onClick, this);
4791 if(this.badge !== ''){
4792 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4797 onClick : function(e)
4804 if(this.preventDefault){
4808 this.fireEvent('click', this);
4811 disable : function()
4813 this.setDisabled(true);
4818 this.setDisabled(false);
4821 setDisabled : function(state)
4823 if(this.disabled == state){
4827 this.disabled = state;
4830 this.el.addClass('disabled');
4834 this.el.removeClass('disabled');
4839 setActive : function(state)
4841 if(this.active == state){
4845 this.active = state;
4848 this.el.addClass('active');
4852 this.el.removeClass('active');
4857 isActive: function ()
4862 setBadge : function(str)
4868 this.badgeEl.dom.innerHTML = str;
4885 * @class Roo.bootstrap.Row
4886 * @extends Roo.bootstrap.Component
4887 * Bootstrap Row class (contains columns...)
4891 * @param {Object} config The config object
4894 Roo.bootstrap.Row = function(config){
4895 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4898 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4900 getAutoCreate : function(){
4919 * @class Roo.bootstrap.Element
4920 * @extends Roo.bootstrap.Component
4921 * Bootstrap Element class
4922 * @cfg {String} html contents of the element
4923 * @cfg {String} tag tag of the element
4924 * @cfg {String} cls class of the element
4925 * @cfg {Boolean} preventDefault (true|false) default false
4926 * @cfg {Boolean} clickable (true|false) default false
4929 * Create a new Element
4930 * @param {Object} config The config object
4933 Roo.bootstrap.Element = function(config){
4934 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4940 * When a element is chick
4941 * @param {Roo.bootstrap.Element} this
4942 * @param {Roo.EventObject} e
4948 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4953 preventDefault: false,
4956 getAutoCreate : function(){
4960 // cls: this.cls, double assign in parent class Component.js :: onRender
4967 initEvents: function()
4969 Roo.bootstrap.Element.superclass.initEvents.call(this);
4972 this.el.on('click', this.onClick, this);
4977 onClick : function(e)
4979 if(this.preventDefault){
4983 this.fireEvent('click', this, e);
4986 getValue : function()
4988 return this.el.dom.innerHTML;
4991 setValue : function(value)
4993 this.el.dom.innerHTML = value;
5008 * @class Roo.bootstrap.Pagination
5009 * @extends Roo.bootstrap.Component
5010 * Bootstrap Pagination class
5011 * @cfg {String} size xs | sm | md | lg
5012 * @cfg {Boolean} inverse false | true
5015 * Create a new Pagination
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Pagination = function(config){
5020 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5023 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5029 getAutoCreate : function(){
5035 cfg.cls += ' inverse';
5041 cfg.cls += " " + this.cls;
5059 * @class Roo.bootstrap.PaginationItem
5060 * @extends Roo.bootstrap.Component
5061 * Bootstrap PaginationItem class
5062 * @cfg {String} html text
5063 * @cfg {String} href the link
5064 * @cfg {Boolean} preventDefault (true | false) default true
5065 * @cfg {Boolean} active (true | false) default false
5066 * @cfg {Boolean} disabled default false
5070 * Create a new PaginationItem
5071 * @param {Object} config The config object
5075 Roo.bootstrap.PaginationItem = function(config){
5076 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5081 * The raw click event for the entire grid.
5082 * @param {Roo.EventObject} e
5088 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5092 preventDefault: true,
5097 getAutoCreate : function(){
5103 href : this.href ? this.href : '#',
5104 html : this.html ? this.html : ''
5114 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5118 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5124 initEvents: function() {
5126 this.el.on('click', this.onClick, this);
5129 onClick : function(e)
5131 Roo.log('PaginationItem on click ');
5132 if(this.preventDefault){
5140 this.fireEvent('click', this, e);
5156 * @class Roo.bootstrap.Slider
5157 * @extends Roo.bootstrap.Component
5158 * Bootstrap Slider class
5161 * Create a new Slider
5162 * @param {Object} config The config object
5165 Roo.bootstrap.Slider = function(config){
5166 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5169 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5171 getAutoCreate : function(){
5175 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5179 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5191 * Ext JS Library 1.1.1
5192 * Copyright(c) 2006-2007, Ext JS, LLC.
5194 * Originally Released Under LGPL - original licence link has changed is not relivant.
5197 * <script type="text/javascript">
5202 * @class Roo.grid.ColumnModel
5203 * @extends Roo.util.Observable
5204 * This is the default implementation of a ColumnModel used by the Grid. It defines
5205 * the columns in the grid.
5208 var colModel = new Roo.grid.ColumnModel([
5209 {header: "Ticker", width: 60, sortable: true, locked: true},
5210 {header: "Company Name", width: 150, sortable: true},
5211 {header: "Market Cap.", width: 100, sortable: true},
5212 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5213 {header: "Employees", width: 100, sortable: true, resizable: false}
5218 * The config options listed for this class are options which may appear in each
5219 * individual column definition.
5220 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5222 * @param {Object} config An Array of column config objects. See this class's
5223 * config objects for details.
5225 Roo.grid.ColumnModel = function(config){
5227 * The config passed into the constructor
5229 this.config = config;
5232 // if no id, create one
5233 // if the column does not have a dataIndex mapping,
5234 // map it to the order it is in the config
5235 for(var i = 0, len = config.length; i < len; i++){
5237 if(typeof c.dataIndex == "undefined"){
5240 if(typeof c.renderer == "string"){
5241 c.renderer = Roo.util.Format[c.renderer];
5243 if(typeof c.id == "undefined"){
5246 if(c.editor && c.editor.xtype){
5247 c.editor = Roo.factory(c.editor, Roo.grid);
5249 if(c.editor && c.editor.isFormField){
5250 c.editor = new Roo.grid.GridEditor(c.editor);
5252 this.lookup[c.id] = c;
5256 * The width of columns which have no width specified (defaults to 100)
5259 this.defaultWidth = 100;
5262 * Default sortable of columns which have no sortable specified (defaults to false)
5265 this.defaultSortable = false;
5269 * @event widthchange
5270 * Fires when the width of a column changes.
5271 * @param {ColumnModel} this
5272 * @param {Number} columnIndex The column index
5273 * @param {Number} newWidth The new width
5275 "widthchange": true,
5277 * @event headerchange
5278 * Fires when the text of a header changes.
5279 * @param {ColumnModel} this
5280 * @param {Number} columnIndex The column index
5281 * @param {Number} newText The new header text
5283 "headerchange": true,
5285 * @event hiddenchange
5286 * Fires when a column is hidden or "unhidden".
5287 * @param {ColumnModel} this
5288 * @param {Number} columnIndex The column index
5289 * @param {Boolean} hidden true if hidden, false otherwise
5291 "hiddenchange": true,
5293 * @event columnmoved
5294 * Fires when a column is moved.
5295 * @param {ColumnModel} this
5296 * @param {Number} oldIndex
5297 * @param {Number} newIndex
5299 "columnmoved" : true,
5301 * @event columlockchange
5302 * Fires when a column's locked state is changed
5303 * @param {ColumnModel} this
5304 * @param {Number} colIndex
5305 * @param {Boolean} locked true if locked
5307 "columnlockchange" : true
5309 Roo.grid.ColumnModel.superclass.constructor.call(this);
5311 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5313 * @cfg {String} header The header text to display in the Grid view.
5316 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5317 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5318 * specified, the column's index is used as an index into the Record's data Array.
5321 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5322 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5325 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5326 * Defaults to the value of the {@link #defaultSortable} property.
5327 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5330 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5333 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5336 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5339 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5342 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5343 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5344 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5345 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5348 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5351 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5354 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5357 * @cfg {String} cursor (Optional)
5360 * @cfg {String} tooltip (Optional)
5363 * @cfg {Number} xs (Optional)
5366 * @cfg {Number} sm (Optional)
5369 * @cfg {Number} md (Optional)
5372 * @cfg {Number} lg (Optional)
5375 * Returns the id of the column at the specified index.
5376 * @param {Number} index The column index
5377 * @return {String} the id
5379 getColumnId : function(index){
5380 return this.config[index].id;
5384 * Returns the column for a specified id.
5385 * @param {String} id The column id
5386 * @return {Object} the column
5388 getColumnById : function(id){
5389 return this.lookup[id];
5394 * Returns the column for a specified dataIndex.
5395 * @param {String} dataIndex The column dataIndex
5396 * @return {Object|Boolean} the column or false if not found
5398 getColumnByDataIndex: function(dataIndex){
5399 var index = this.findColumnIndex(dataIndex);
5400 return index > -1 ? this.config[index] : false;
5404 * Returns the index for a specified column id.
5405 * @param {String} id The column id
5406 * @return {Number} the index, or -1 if not found
5408 getIndexById : function(id){
5409 for(var i = 0, len = this.config.length; i < len; i++){
5410 if(this.config[i].id == id){
5418 * Returns the index for a specified column dataIndex.
5419 * @param {String} dataIndex The column dataIndex
5420 * @return {Number} the index, or -1 if not found
5423 findColumnIndex : function(dataIndex){
5424 for(var i = 0, len = this.config.length; i < len; i++){
5425 if(this.config[i].dataIndex == dataIndex){
5433 moveColumn : function(oldIndex, newIndex){
5434 var c = this.config[oldIndex];
5435 this.config.splice(oldIndex, 1);
5436 this.config.splice(newIndex, 0, c);
5437 this.dataMap = null;
5438 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5441 isLocked : function(colIndex){
5442 return this.config[colIndex].locked === true;
5445 setLocked : function(colIndex, value, suppressEvent){
5446 if(this.isLocked(colIndex) == value){
5449 this.config[colIndex].locked = value;
5451 this.fireEvent("columnlockchange", this, colIndex, value);
5455 getTotalLockedWidth : function(){
5457 for(var i = 0; i < this.config.length; i++){
5458 if(this.isLocked(i) && !this.isHidden(i)){
5459 this.totalWidth += this.getColumnWidth(i);
5465 getLockedCount : function(){
5466 for(var i = 0, len = this.config.length; i < len; i++){
5467 if(!this.isLocked(i)){
5472 return this.config.length;
5476 * Returns the number of columns.
5479 getColumnCount : function(visibleOnly){
5480 if(visibleOnly === true){
5482 for(var i = 0, len = this.config.length; i < len; i++){
5483 if(!this.isHidden(i)){
5489 return this.config.length;
5493 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5494 * @param {Function} fn
5495 * @param {Object} scope (optional)
5496 * @return {Array} result
5498 getColumnsBy : function(fn, scope){
5500 for(var i = 0, len = this.config.length; i < len; i++){
5501 var c = this.config[i];
5502 if(fn.call(scope||this, c, i) === true){
5510 * Returns true if the specified column is sortable.
5511 * @param {Number} col The column index
5514 isSortable : function(col){
5515 if(typeof this.config[col].sortable == "undefined"){
5516 return this.defaultSortable;
5518 return this.config[col].sortable;
5522 * Returns the rendering (formatting) function defined for the column.
5523 * @param {Number} col The column index.
5524 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5526 getRenderer : function(col){
5527 if(!this.config[col].renderer){
5528 return Roo.grid.ColumnModel.defaultRenderer;
5530 return this.config[col].renderer;
5534 * Sets the rendering (formatting) function for a column.
5535 * @param {Number} col The column index
5536 * @param {Function} fn The function to use to process the cell's raw data
5537 * to return HTML markup for the grid view. The render function is called with
5538 * the following parameters:<ul>
5539 * <li>Data value.</li>
5540 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5541 * <li>css A CSS style string to apply to the table cell.</li>
5542 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5543 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5544 * <li>Row index</li>
5545 * <li>Column index</li>
5546 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5548 setRenderer : function(col, fn){
5549 this.config[col].renderer = fn;
5553 * Returns the width for the specified column.
5554 * @param {Number} col The column index
5557 getColumnWidth : function(col){
5558 return this.config[col].width * 1 || this.defaultWidth;
5562 * Sets the width for a column.
5563 * @param {Number} col The column index
5564 * @param {Number} width The new width
5566 setColumnWidth : function(col, width, suppressEvent){
5567 this.config[col].width = width;
5568 this.totalWidth = null;
5570 this.fireEvent("widthchange", this, col, width);
5575 * Returns the total width of all columns.
5576 * @param {Boolean} includeHidden True to include hidden column widths
5579 getTotalWidth : function(includeHidden){
5580 if(!this.totalWidth){
5581 this.totalWidth = 0;
5582 for(var i = 0, len = this.config.length; i < len; i++){
5583 if(includeHidden || !this.isHidden(i)){
5584 this.totalWidth += this.getColumnWidth(i);
5588 return this.totalWidth;
5592 * Returns the header for the specified column.
5593 * @param {Number} col The column index
5596 getColumnHeader : function(col){
5597 return this.config[col].header;
5601 * Sets the header for a column.
5602 * @param {Number} col The column index
5603 * @param {String} header The new header
5605 setColumnHeader : function(col, header){
5606 this.config[col].header = header;
5607 this.fireEvent("headerchange", this, col, header);
5611 * Returns the tooltip for the specified column.
5612 * @param {Number} col The column index
5615 getColumnTooltip : function(col){
5616 return this.config[col].tooltip;
5619 * Sets the tooltip for a column.
5620 * @param {Number} col The column index
5621 * @param {String} tooltip The new tooltip
5623 setColumnTooltip : function(col, tooltip){
5624 this.config[col].tooltip = tooltip;
5628 * Returns the dataIndex for the specified column.
5629 * @param {Number} col The column index
5632 getDataIndex : function(col){
5633 return this.config[col].dataIndex;
5637 * Sets the dataIndex for a column.
5638 * @param {Number} col The column index
5639 * @param {Number} dataIndex The new dataIndex
5641 setDataIndex : function(col, dataIndex){
5642 this.config[col].dataIndex = dataIndex;
5648 * Returns true if the cell is editable.
5649 * @param {Number} colIndex The column index
5650 * @param {Number} rowIndex The row index - this is nto actually used..?
5653 isCellEditable : function(colIndex, rowIndex){
5654 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5658 * Returns the editor defined for the cell/column.
5659 * return false or null to disable editing.
5660 * @param {Number} colIndex The column index
5661 * @param {Number} rowIndex The row index
5664 getCellEditor : function(colIndex, rowIndex){
5665 return this.config[colIndex].editor;
5669 * Sets if a column is editable.
5670 * @param {Number} col The column index
5671 * @param {Boolean} editable True if the column is editable
5673 setEditable : function(col, editable){
5674 this.config[col].editable = editable;
5679 * Returns true if the column is hidden.
5680 * @param {Number} colIndex The column index
5683 isHidden : function(colIndex){
5684 return this.config[colIndex].hidden;
5689 * Returns true if the column width cannot be changed
5691 isFixed : function(colIndex){
5692 return this.config[colIndex].fixed;
5696 * Returns true if the column can be resized
5699 isResizable : function(colIndex){
5700 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5703 * Sets if a column is hidden.
5704 * @param {Number} colIndex The column index
5705 * @param {Boolean} hidden True if the column is hidden
5707 setHidden : function(colIndex, hidden){
5708 this.config[colIndex].hidden = hidden;
5709 this.totalWidth = null;
5710 this.fireEvent("hiddenchange", this, colIndex, hidden);
5714 * Sets the editor for a column.
5715 * @param {Number} col The column index
5716 * @param {Object} editor The editor object
5718 setEditor : function(col, editor){
5719 this.config[col].editor = editor;
5723 Roo.grid.ColumnModel.defaultRenderer = function(value)
5725 if(typeof value == "object") {
5728 if(typeof value == "string" && value.length < 1){
5732 return String.format("{0}", value);
5735 // Alias for backwards compatibility
5736 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5739 * Ext JS Library 1.1.1
5740 * Copyright(c) 2006-2007, Ext JS, LLC.
5742 * Originally Released Under LGPL - original licence link has changed is not relivant.
5745 * <script type="text/javascript">
5749 * @class Roo.LoadMask
5750 * A simple utility class for generically masking elements while loading data. If the element being masked has
5751 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5752 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5753 * element's UpdateManager load indicator and will be destroyed after the initial load.
5755 * Create a new LoadMask
5756 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5757 * @param {Object} config The config object
5759 Roo.LoadMask = function(el, config){
5760 this.el = Roo.get(el);
5761 Roo.apply(this, config);
5763 this.store.on('beforeload', this.onBeforeLoad, this);
5764 this.store.on('load', this.onLoad, this);
5765 this.store.on('loadexception', this.onLoadException, this);
5766 this.removeMask = false;
5768 var um = this.el.getUpdateManager();
5769 um.showLoadIndicator = false; // disable the default indicator
5770 um.on('beforeupdate', this.onBeforeLoad, this);
5771 um.on('update', this.onLoad, this);
5772 um.on('failure', this.onLoad, this);
5773 this.removeMask = true;
5777 Roo.LoadMask.prototype = {
5779 * @cfg {Boolean} removeMask
5780 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5781 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5785 * The text to display in a centered loading message box (defaults to 'Loading...')
5789 * @cfg {String} msgCls
5790 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5792 msgCls : 'x-mask-loading',
5795 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5801 * Disables the mask to prevent it from being displayed
5803 disable : function(){
5804 this.disabled = true;
5808 * Enables the mask so that it can be displayed
5810 enable : function(){
5811 this.disabled = false;
5814 onLoadException : function()
5818 if (typeof(arguments[3]) != 'undefined') {
5819 Roo.MessageBox.alert("Error loading",arguments[3]);
5823 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5824 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5831 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5836 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5840 onBeforeLoad : function(){
5842 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5847 destroy : function(){
5849 this.store.un('beforeload', this.onBeforeLoad, this);
5850 this.store.un('load', this.onLoad, this);
5851 this.store.un('loadexception', this.onLoadException, this);
5853 var um = this.el.getUpdateManager();
5854 um.un('beforeupdate', this.onBeforeLoad, this);
5855 um.un('update', this.onLoad, this);
5856 um.un('failure', this.onLoad, this);
5867 * @class Roo.bootstrap.Table
5868 * @extends Roo.bootstrap.Component
5869 * Bootstrap Table class
5870 * @cfg {String} cls table class
5871 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5872 * @cfg {String} bgcolor Specifies the background color for a table
5873 * @cfg {Number} border Specifies whether the table cells should have borders or not
5874 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5875 * @cfg {Number} cellspacing Specifies the space between cells
5876 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5877 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5878 * @cfg {String} sortable Specifies that the table should be sortable
5879 * @cfg {String} summary Specifies a summary of the content of a table
5880 * @cfg {Number} width Specifies the width of a table
5881 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5883 * @cfg {boolean} striped Should the rows be alternative striped
5884 * @cfg {boolean} bordered Add borders to the table
5885 * @cfg {boolean} hover Add hover highlighting
5886 * @cfg {boolean} condensed Format condensed
5887 * @cfg {boolean} responsive Format condensed
5888 * @cfg {Boolean} loadMask (true|false) default false
5889 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5890 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5891 * @cfg {Boolean} rowSelection (true|false) default false
5892 * @cfg {Boolean} cellSelection (true|false) default false
5893 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5894 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5895 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5896 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5900 * Create a new Table
5901 * @param {Object} config The config object
5904 Roo.bootstrap.Table = function(config){
5905 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5910 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5911 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5912 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5913 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5915 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5917 this.sm.grid = this;
5918 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5919 this.sm = this.selModel;
5920 this.sm.xmodule = this.xmodule || false;
5923 if (this.cm && typeof(this.cm.config) == 'undefined') {
5924 this.colModel = new Roo.grid.ColumnModel(this.cm);
5925 this.cm = this.colModel;
5926 this.cm.xmodule = this.xmodule || false;
5929 this.store= Roo.factory(this.store, Roo.data);
5930 this.ds = this.store;
5931 this.ds.xmodule = this.xmodule || false;
5934 if (this.footer && this.store) {
5935 this.footer.dataSource = this.ds;
5936 this.footer = Roo.factory(this.footer);
5943 * Fires when a cell is clicked
5944 * @param {Roo.bootstrap.Table} this
5945 * @param {Roo.Element} el
5946 * @param {Number} rowIndex
5947 * @param {Number} columnIndex
5948 * @param {Roo.EventObject} e
5952 * @event celldblclick
5953 * Fires when a cell is double clicked
5954 * @param {Roo.bootstrap.Table} this
5955 * @param {Roo.Element} el
5956 * @param {Number} rowIndex
5957 * @param {Number} columnIndex
5958 * @param {Roo.EventObject} e
5960 "celldblclick" : true,
5963 * Fires when a row is clicked
5964 * @param {Roo.bootstrap.Table} this
5965 * @param {Roo.Element} el
5966 * @param {Number} rowIndex
5967 * @param {Roo.EventObject} e
5971 * @event rowdblclick
5972 * Fires when a row is double clicked
5973 * @param {Roo.bootstrap.Table} this
5974 * @param {Roo.Element} el
5975 * @param {Number} rowIndex
5976 * @param {Roo.EventObject} e
5978 "rowdblclick" : true,
5981 * Fires when a mouseover occur
5982 * @param {Roo.bootstrap.Table} this
5983 * @param {Roo.Element} el
5984 * @param {Number} rowIndex
5985 * @param {Number} columnIndex
5986 * @param {Roo.EventObject} e
5991 * Fires when a mouseout occur
5992 * @param {Roo.bootstrap.Table} this
5993 * @param {Roo.Element} el
5994 * @param {Number} rowIndex
5995 * @param {Number} columnIndex
5996 * @param {Roo.EventObject} e
6001 * Fires when a row is rendered, so you can change add a style to it.
6002 * @param {Roo.bootstrap.Table} this
6003 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6007 * @event rowsrendered
6008 * Fires when all the rows have been rendered
6009 * @param {Roo.bootstrap.Table} this
6011 'rowsrendered' : true,
6013 * @event contextmenu
6014 * The raw contextmenu event for the entire grid.
6015 * @param {Roo.EventObject} e
6017 "contextmenu" : true,
6019 * @event rowcontextmenu
6020 * Fires when a row is right clicked
6021 * @param {Roo.bootstrap.Table} this
6022 * @param {Number} rowIndex
6023 * @param {Roo.EventObject} e
6025 "rowcontextmenu" : true,
6027 * @event cellcontextmenu
6028 * Fires when a cell is right clicked
6029 * @param {Roo.bootstrap.Table} this
6030 * @param {Number} rowIndex
6031 * @param {Number} cellIndex
6032 * @param {Roo.EventObject} e
6034 "cellcontextmenu" : true,
6036 * @event headercontextmenu
6037 * Fires when a header is right clicked
6038 * @param {Roo.bootstrap.Table} this
6039 * @param {Number} columnIndex
6040 * @param {Roo.EventObject} e
6042 "headercontextmenu" : true
6046 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6072 rowSelection : false,
6073 cellSelection : false,
6076 // Roo.Element - the tbody
6078 // Roo.Element - thead element
6081 container: false, // used by gridpanel...
6087 auto_hide_footer : false,
6089 getAutoCreate : function()
6091 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6098 if (this.scrollBody) {
6099 cfg.cls += ' table-body-fixed';
6102 cfg.cls += ' table-striped';
6106 cfg.cls += ' table-hover';
6108 if (this.bordered) {
6109 cfg.cls += ' table-bordered';
6111 if (this.condensed) {
6112 cfg.cls += ' table-condensed';
6114 if (this.responsive) {
6115 cfg.cls += ' table-responsive';
6119 cfg.cls+= ' ' +this.cls;
6122 // this lot should be simplifed...
6135 ].forEach(function(k) {
6143 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6146 if(this.store || this.cm){
6147 if(this.headerShow){
6148 cfg.cn.push(this.renderHeader());
6151 cfg.cn.push(this.renderBody());
6153 if(this.footerShow){
6154 cfg.cn.push(this.renderFooter());
6156 // where does this come from?
6157 //cfg.cls+= ' TableGrid';
6160 return { cn : [ cfg ] };
6163 initEvents : function()
6165 if(!this.store || !this.cm){
6168 if (this.selModel) {
6169 this.selModel.initEvents();
6173 //Roo.log('initEvents with ds!!!!');
6175 this.mainBody = this.el.select('tbody', true).first();
6176 this.mainHead = this.el.select('thead', true).first();
6177 this.mainFoot = this.el.select('tfoot', true).first();
6183 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6184 e.on('click', _this.sort, _this);
6187 this.mainBody.on("click", this.onClick, this);
6188 this.mainBody.on("dblclick", this.onDblClick, this);
6190 // why is this done????? = it breaks dialogs??
6191 //this.parent().el.setStyle('position', 'relative');
6195 this.footer.parentId = this.id;
6196 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6199 this.el.select('tfoot tr td').first().addClass('hide');
6204 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6207 this.store.on('load', this.onLoad, this);
6208 this.store.on('beforeload', this.onBeforeLoad, this);
6209 this.store.on('update', this.onUpdate, this);
6210 this.store.on('add', this.onAdd, this);
6211 this.store.on("clear", this.clear, this);
6213 this.el.on("contextmenu", this.onContextMenu, this);
6215 this.mainBody.on('scroll', this.onBodyScroll, this);
6217 this.cm.on("headerchange", this.onHeaderChange, this);
6219 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6223 onContextMenu : function(e, t)
6225 this.processEvent("contextmenu", e);
6228 processEvent : function(name, e)
6230 if (name != 'touchstart' ) {
6231 this.fireEvent(name, e);
6234 var t = e.getTarget();
6236 var cell = Roo.get(t);
6242 if(cell.findParent('tfoot', false, true)){
6246 if(cell.findParent('thead', false, true)){
6248 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6249 cell = Roo.get(t).findParent('th', false, true);
6251 Roo.log("failed to find th in thead?");
6252 Roo.log(e.getTarget());
6257 var cellIndex = cell.dom.cellIndex;
6259 var ename = name == 'touchstart' ? 'click' : name;
6260 this.fireEvent("header" + ename, this, cellIndex, e);
6265 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6266 cell = Roo.get(t).findParent('td', false, true);
6268 Roo.log("failed to find th in tbody?");
6269 Roo.log(e.getTarget());
6274 var row = cell.findParent('tr', false, true);
6275 var cellIndex = cell.dom.cellIndex;
6276 var rowIndex = row.dom.rowIndex - 1;
6280 this.fireEvent("row" + name, this, rowIndex, e);
6284 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6290 onMouseover : function(e, el)
6292 var cell = Roo.get(el);
6298 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6299 cell = cell.findParent('td', false, true);
6302 var row = cell.findParent('tr', false, true);
6303 var cellIndex = cell.dom.cellIndex;
6304 var rowIndex = row.dom.rowIndex - 1; // start from 0
6306 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6310 onMouseout : function(e, el)
6312 var cell = Roo.get(el);
6318 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6319 cell = cell.findParent('td', false, true);
6322 var row = cell.findParent('tr', false, true);
6323 var cellIndex = cell.dom.cellIndex;
6324 var rowIndex = row.dom.rowIndex - 1; // start from 0
6326 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6330 onClick : function(e, el)
6332 var cell = Roo.get(el);
6334 if(!cell || (!this.cellSelection && !this.rowSelection)){
6338 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6339 cell = cell.findParent('td', false, true);
6342 if(!cell || typeof(cell) == 'undefined'){
6346 var row = cell.findParent('tr', false, true);
6348 if(!row || typeof(row) == 'undefined'){
6352 var cellIndex = cell.dom.cellIndex;
6353 var rowIndex = this.getRowIndex(row);
6355 // why??? - should these not be based on SelectionModel?
6356 if(this.cellSelection){
6357 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6360 if(this.rowSelection){
6361 this.fireEvent('rowclick', this, row, rowIndex, e);
6367 onDblClick : function(e,el)
6369 var cell = Roo.get(el);
6371 if(!cell || (!this.cellSelection && !this.rowSelection)){
6375 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6376 cell = cell.findParent('td', false, true);
6379 if(!cell || typeof(cell) == 'undefined'){
6383 var row = cell.findParent('tr', false, true);
6385 if(!row || typeof(row) == 'undefined'){
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = this.getRowIndex(row);
6392 if(this.cellSelection){
6393 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6396 if(this.rowSelection){
6397 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6401 sort : function(e,el)
6403 var col = Roo.get(el);
6405 if(!col.hasClass('sortable')){
6409 var sort = col.attr('sort');
6412 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6416 this.store.sortInfo = {field : sort, direction : dir};
6419 Roo.log("calling footer first");
6420 this.footer.onClick('first');
6423 this.store.load({ params : { start : 0 } });
6427 renderHeader : function()
6435 this.totalWidth = 0;
6437 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6439 var config = cm.config[i];
6443 cls : 'x-hcol-' + i,
6445 html: cm.getColumnHeader(i)
6450 if(typeof(config.sortable) != 'undefined' && config.sortable){
6452 c.html = '<i class="glyphicon"></i>' + c.html;
6455 if(typeof(config.lgHeader) != 'undefined'){
6456 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6459 if(typeof(config.mdHeader) != 'undefined'){
6460 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6463 if(typeof(config.smHeader) != 'undefined'){
6464 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6467 if(typeof(config.xsHeader) != 'undefined'){
6468 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6475 if(typeof(config.tooltip) != 'undefined'){
6476 c.tooltip = config.tooltip;
6479 if(typeof(config.colspan) != 'undefined'){
6480 c.colspan = config.colspan;
6483 if(typeof(config.hidden) != 'undefined' && config.hidden){
6484 c.style += ' display:none;';
6487 if(typeof(config.dataIndex) != 'undefined'){
6488 c.sort = config.dataIndex;
6493 if(typeof(config.align) != 'undefined' && config.align.length){
6494 c.style += ' text-align:' + config.align + ';';
6497 if(typeof(config.width) != 'undefined'){
6498 c.style += ' width:' + config.width + 'px;';
6499 this.totalWidth += config.width;
6501 this.totalWidth += 100; // assume minimum of 100 per column?
6504 if(typeof(config.cls) != 'undefined'){
6505 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6508 ['xs','sm','md','lg'].map(function(size){
6510 if(typeof(config[size]) == 'undefined'){
6514 if (!config[size]) { // 0 = hidden
6515 c.cls += ' hidden-' + size;
6519 c.cls += ' col-' + size + '-' + config[size];
6529 renderBody : function()
6539 colspan : this.cm.getColumnCount()
6549 renderFooter : function()
6559 colspan : this.cm.getColumnCount()
6573 // Roo.log('ds onload');
6578 var ds = this.store;
6580 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6581 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6582 if (_this.store.sortInfo) {
6584 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6585 e.select('i', true).addClass(['glyphicon-arrow-up']);
6588 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6589 e.select('i', true).addClass(['glyphicon-arrow-down']);
6594 var tbody = this.mainBody;
6596 if(ds.getCount() > 0){
6597 ds.data.each(function(d,rowIndex){
6598 var row = this.renderRow(cm, ds, rowIndex);
6600 tbody.createChild(row);
6604 if(row.cellObjects.length){
6605 Roo.each(row.cellObjects, function(r){
6606 _this.renderCellObject(r);
6613 var tfoot = this.el.select('tfoot', true).first();
6615 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6617 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6619 var total = this.ds.getTotalCount();
6621 if(this.footer.pageSize < total){
6622 this.mainFoot.show();
6626 Roo.each(this.el.select('tbody td', true).elements, function(e){
6627 e.on('mouseover', _this.onMouseover, _this);
6630 Roo.each(this.el.select('tbody td', true).elements, function(e){
6631 e.on('mouseout', _this.onMouseout, _this);
6633 this.fireEvent('rowsrendered', this);
6639 onUpdate : function(ds,record)
6641 this.refreshRow(record);
6645 onRemove : function(ds, record, index, isUpdate){
6646 if(isUpdate !== true){
6647 this.fireEvent("beforerowremoved", this, index, record);
6649 var bt = this.mainBody.dom;
6651 var rows = this.el.select('tbody > tr', true).elements;
6653 if(typeof(rows[index]) != 'undefined'){
6654 bt.removeChild(rows[index].dom);
6657 // if(bt.rows[index]){
6658 // bt.removeChild(bt.rows[index]);
6661 if(isUpdate !== true){
6662 //this.stripeRows(index);
6663 //this.syncRowHeights(index, index);
6665 this.fireEvent("rowremoved", this, index, record);
6669 onAdd : function(ds, records, rowIndex)
6671 //Roo.log('on Add called');
6672 // - note this does not handle multiple adding very well..
6673 var bt = this.mainBody.dom;
6674 for (var i =0 ; i < records.length;i++) {
6675 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6676 //Roo.log(records[i]);
6677 //Roo.log(this.store.getAt(rowIndex+i));
6678 this.insertRow(this.store, rowIndex + i, false);
6685 refreshRow : function(record){
6686 var ds = this.store, index;
6687 if(typeof record == 'number'){
6689 record = ds.getAt(index);
6691 index = ds.indexOf(record);
6693 this.insertRow(ds, index, true);
6695 this.onRemove(ds, record, index+1, true);
6697 //this.syncRowHeights(index, index);
6699 this.fireEvent("rowupdated", this, index, record);
6702 insertRow : function(dm, rowIndex, isUpdate){
6705 this.fireEvent("beforerowsinserted", this, rowIndex);
6707 //var s = this.getScrollState();
6708 var row = this.renderRow(this.cm, this.store, rowIndex);
6709 // insert before rowIndex..
6710 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6714 if(row.cellObjects.length){
6715 Roo.each(row.cellObjects, function(r){
6716 _this.renderCellObject(r);
6721 this.fireEvent("rowsinserted", this, rowIndex);
6722 //this.syncRowHeights(firstRow, lastRow);
6723 //this.stripeRows(firstRow);
6730 getRowDom : function(rowIndex)
6732 var rows = this.el.select('tbody > tr', true).elements;
6734 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6737 // returns the object tree for a tr..
6740 renderRow : function(cm, ds, rowIndex)
6742 var d = ds.getAt(rowIndex);
6746 cls : 'x-row-' + rowIndex,
6750 var cellObjects = [];
6752 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6753 var config = cm.config[i];
6755 var renderer = cm.getRenderer(i);
6759 if(typeof(renderer) !== 'undefined'){
6760 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6762 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6763 // and are rendered into the cells after the row is rendered - using the id for the element.
6765 if(typeof(value) === 'object'){
6775 rowIndex : rowIndex,
6780 this.fireEvent('rowclass', this, rowcfg);
6784 cls : rowcfg.rowClass + ' x-col-' + i,
6786 html: (typeof(value) === 'object') ? '' : value
6793 if(typeof(config.colspan) != 'undefined'){
6794 td.colspan = config.colspan;
6797 if(typeof(config.hidden) != 'undefined' && config.hidden){
6798 td.style += ' display:none;';
6801 if(typeof(config.align) != 'undefined' && config.align.length){
6802 td.style += ' text-align:' + config.align + ';';
6804 if(typeof(config.valign) != 'undefined' && config.valign.length){
6805 td.style += ' vertical-align:' + config.valign + ';';
6808 if(typeof(config.width) != 'undefined'){
6809 td.style += ' width:' + config.width + 'px;';
6812 if(typeof(config.cursor) != 'undefined'){
6813 td.style += ' cursor:' + config.cursor + ';';
6816 if(typeof(config.cls) != 'undefined'){
6817 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6820 ['xs','sm','md','lg'].map(function(size){
6822 if(typeof(config[size]) == 'undefined'){
6826 if (!config[size]) { // 0 = hidden
6827 td.cls += ' hidden-' + size;
6831 td.cls += ' col-' + size + '-' + config[size];
6839 row.cellObjects = cellObjects;
6847 onBeforeLoad : function()
6856 this.el.select('tbody', true).first().dom.innerHTML = '';
6859 * Show or hide a row.
6860 * @param {Number} rowIndex to show or hide
6861 * @param {Boolean} state hide
6863 setRowVisibility : function(rowIndex, state)
6865 var bt = this.mainBody.dom;
6867 var rows = this.el.select('tbody > tr', true).elements;
6869 if(typeof(rows[rowIndex]) == 'undefined'){
6872 rows[rowIndex].dom.style.display = state ? '' : 'none';
6876 getSelectionModel : function(){
6878 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6880 return this.selModel;
6883 * Render the Roo.bootstrap object from renderder
6885 renderCellObject : function(r)
6889 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6891 var t = r.cfg.render(r.container);
6894 Roo.each(r.cfg.cn, function(c){
6896 container: t.getChildContainer(),
6899 _this.renderCellObject(child);
6904 getRowIndex : function(row)
6908 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6919 * Returns the grid's underlying element = used by panel.Grid
6920 * @return {Element} The element
6922 getGridEl : function(){
6926 * Forces a resize - used by panel.Grid
6927 * @return {Element} The element
6929 autoSize : function()
6931 //var ctr = Roo.get(this.container.dom.parentElement);
6932 var ctr = Roo.get(this.el.dom);
6934 var thd = this.getGridEl().select('thead',true).first();
6935 var tbd = this.getGridEl().select('tbody', true).first();
6936 var tfd = this.getGridEl().select('tfoot', true).first();
6938 var cw = ctr.getWidth();
6942 tbd.setSize(ctr.getWidth(),
6943 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6945 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6948 cw = Math.max(cw, this.totalWidth);
6949 this.getGridEl().select('tr',true).setWidth(cw);
6950 // resize 'expandable coloumn?
6952 return; // we doe not have a view in this design..
6955 onBodyScroll: function()
6957 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6959 this.mainHead.setStyle({
6960 'position' : 'relative',
6961 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6967 var scrollHeight = this.mainBody.dom.scrollHeight;
6969 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6971 var height = this.mainBody.getHeight();
6973 if(scrollHeight - height == scrollTop) {
6975 var total = this.ds.getTotalCount();
6977 if(this.footer.cursor + this.footer.pageSize < total){
6979 this.footer.ds.load({
6981 start : this.footer.cursor + this.footer.pageSize,
6982 limit : this.footer.pageSize
6992 onHeaderChange : function()
6994 var header = this.renderHeader();
6995 var table = this.el.select('table', true).first();
6997 this.mainHead.remove();
6998 this.mainHead = table.createChild(header, this.mainBody, false);
7001 onHiddenChange : function(colModel, colIndex, hidden)
7003 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7004 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7006 this.CSS.updateRule(thSelector, "display", "");
7007 this.CSS.updateRule(tdSelector, "display", "");
7010 this.CSS.updateRule(thSelector, "display", "none");
7011 this.CSS.updateRule(tdSelector, "display", "none");
7014 this.onHeaderChange();
7031 * @class Roo.bootstrap.TableCell
7032 * @extends Roo.bootstrap.Component
7033 * Bootstrap TableCell class
7034 * @cfg {String} html cell contain text
7035 * @cfg {String} cls cell class
7036 * @cfg {String} tag cell tag (td|th) default td
7037 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7038 * @cfg {String} align Aligns the content in a cell
7039 * @cfg {String} axis Categorizes cells
7040 * @cfg {String} bgcolor Specifies the background color of a cell
7041 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7042 * @cfg {Number} colspan Specifies the number of columns a cell should span
7043 * @cfg {String} headers Specifies one or more header cells a cell is related to
7044 * @cfg {Number} height Sets the height of a cell
7045 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7046 * @cfg {Number} rowspan Sets the number of rows a cell should span
7047 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7048 * @cfg {String} valign Vertical aligns the content in a cell
7049 * @cfg {Number} width Specifies the width of a cell
7052 * Create a new TableCell
7053 * @param {Object} config The config object
7056 Roo.bootstrap.TableCell = function(config){
7057 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7060 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7080 getAutoCreate : function(){
7081 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7101 cfg.align=this.align
7107 cfg.bgcolor=this.bgcolor
7110 cfg.charoff=this.charoff
7113 cfg.colspan=this.colspan
7116 cfg.headers=this.headers
7119 cfg.height=this.height
7122 cfg.nowrap=this.nowrap
7125 cfg.rowspan=this.rowspan
7128 cfg.scope=this.scope
7131 cfg.valign=this.valign
7134 cfg.width=this.width
7153 * @class Roo.bootstrap.TableRow
7154 * @extends Roo.bootstrap.Component
7155 * Bootstrap TableRow class
7156 * @cfg {String} cls row class
7157 * @cfg {String} align Aligns the content in a table row
7158 * @cfg {String} bgcolor Specifies a background color for a table row
7159 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7160 * @cfg {String} valign Vertical aligns the content in a table row
7163 * Create a new TableRow
7164 * @param {Object} config The config object
7167 Roo.bootstrap.TableRow = function(config){
7168 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7171 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7179 getAutoCreate : function(){
7180 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7190 cfg.align = this.align;
7193 cfg.bgcolor = this.bgcolor;
7196 cfg.charoff = this.charoff;
7199 cfg.valign = this.valign;
7217 * @class Roo.bootstrap.TableBody
7218 * @extends Roo.bootstrap.Component
7219 * Bootstrap TableBody class
7220 * @cfg {String} cls element class
7221 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7222 * @cfg {String} align Aligns the content inside the element
7223 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7224 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7227 * Create a new TableBody
7228 * @param {Object} config The config object
7231 Roo.bootstrap.TableBody = function(config){
7232 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7235 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7243 getAutoCreate : function(){
7244 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7258 cfg.align = this.align;
7261 cfg.charoff = this.charoff;
7264 cfg.valign = this.valign;
7271 // initEvents : function()
7278 // this.store = Roo.factory(this.store, Roo.data);
7279 // this.store.on('load', this.onLoad, this);
7281 // this.store.load();
7285 // onLoad: function ()
7287 // this.fireEvent('load', this);
7297 * Ext JS Library 1.1.1
7298 * Copyright(c) 2006-2007, Ext JS, LLC.
7300 * Originally Released Under LGPL - original licence link has changed is not relivant.
7303 * <script type="text/javascript">
7306 // as we use this in bootstrap.
7307 Roo.namespace('Roo.form');
7309 * @class Roo.form.Action
7310 * Internal Class used to handle form actions
7312 * @param {Roo.form.BasicForm} el The form element or its id
7313 * @param {Object} config Configuration options
7318 // define the action interface
7319 Roo.form.Action = function(form, options){
7321 this.options = options || {};
7324 * Client Validation Failed
7327 Roo.form.Action.CLIENT_INVALID = 'client';
7329 * Server Validation Failed
7332 Roo.form.Action.SERVER_INVALID = 'server';
7334 * Connect to Server Failed
7337 Roo.form.Action.CONNECT_FAILURE = 'connect';
7339 * Reading Data from Server Failed
7342 Roo.form.Action.LOAD_FAILURE = 'load';
7344 Roo.form.Action.prototype = {
7346 failureType : undefined,
7347 response : undefined,
7351 run : function(options){
7356 success : function(response){
7361 handleResponse : function(response){
7365 // default connection failure
7366 failure : function(response){
7368 this.response = response;
7369 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7370 this.form.afterAction(this, false);
7373 processResponse : function(response){
7374 this.response = response;
7375 if(!response.responseText){
7378 this.result = this.handleResponse(response);
7382 // utility functions used internally
7383 getUrl : function(appendParams){
7384 var url = this.options.url || this.form.url || this.form.el.dom.action;
7386 var p = this.getParams();
7388 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7394 getMethod : function(){
7395 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7398 getParams : function(){
7399 var bp = this.form.baseParams;
7400 var p = this.options.params;
7402 if(typeof p == "object"){
7403 p = Roo.urlEncode(Roo.applyIf(p, bp));
7404 }else if(typeof p == 'string' && bp){
7405 p += '&' + Roo.urlEncode(bp);
7408 p = Roo.urlEncode(bp);
7413 createCallback : function(){
7415 success: this.success,
7416 failure: this.failure,
7418 timeout: (this.form.timeout*1000),
7419 upload: this.form.fileUpload ? this.success : undefined
7424 Roo.form.Action.Submit = function(form, options){
7425 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7428 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7431 haveProgress : false,
7432 uploadComplete : false,
7434 // uploadProgress indicator.
7435 uploadProgress : function()
7437 if (!this.form.progressUrl) {
7441 if (!this.haveProgress) {
7442 Roo.MessageBox.progress("Uploading", "Uploading");
7444 if (this.uploadComplete) {
7445 Roo.MessageBox.hide();
7449 this.haveProgress = true;
7451 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7453 var c = new Roo.data.Connection();
7455 url : this.form.progressUrl,
7460 success : function(req){
7461 //console.log(data);
7465 rdata = Roo.decode(req.responseText)
7467 Roo.log("Invalid data from server..");
7471 if (!rdata || !rdata.success) {
7473 Roo.MessageBox.alert(Roo.encode(rdata));
7476 var data = rdata.data;
7478 if (this.uploadComplete) {
7479 Roo.MessageBox.hide();
7484 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7485 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7488 this.uploadProgress.defer(2000,this);
7491 failure: function(data) {
7492 Roo.log('progress url failed ');
7503 // run get Values on the form, so it syncs any secondary forms.
7504 this.form.getValues();
7506 var o = this.options;
7507 var method = this.getMethod();
7508 var isPost = method == 'POST';
7509 if(o.clientValidation === false || this.form.isValid()){
7511 if (this.form.progressUrl) {
7512 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7513 (new Date() * 1) + '' + Math.random());
7518 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7519 form:this.form.el.dom,
7520 url:this.getUrl(!isPost),
7522 params:isPost ? this.getParams() : null,
7523 isUpload: this.form.fileUpload
7526 this.uploadProgress();
7528 }else if (o.clientValidation !== false){ // client validation failed
7529 this.failureType = Roo.form.Action.CLIENT_INVALID;
7530 this.form.afterAction(this, false);
7534 success : function(response)
7536 this.uploadComplete= true;
7537 if (this.haveProgress) {
7538 Roo.MessageBox.hide();
7542 var result = this.processResponse(response);
7543 if(result === true || result.success){
7544 this.form.afterAction(this, true);
7548 this.form.markInvalid(result.errors);
7549 this.failureType = Roo.form.Action.SERVER_INVALID;
7551 this.form.afterAction(this, false);
7553 failure : function(response)
7555 this.uploadComplete= true;
7556 if (this.haveProgress) {
7557 Roo.MessageBox.hide();
7560 this.response = response;
7561 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7562 this.form.afterAction(this, false);
7565 handleResponse : function(response){
7566 if(this.form.errorReader){
7567 var rs = this.form.errorReader.read(response);
7570 for(var i = 0, len = rs.records.length; i < len; i++) {
7571 var r = rs.records[i];
7575 if(errors.length < 1){
7579 success : rs.success,
7585 ret = Roo.decode(response.responseText);
7589 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7599 Roo.form.Action.Load = function(form, options){
7600 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7601 this.reader = this.form.reader;
7604 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7609 Roo.Ajax.request(Roo.apply(
7610 this.createCallback(), {
7611 method:this.getMethod(),
7612 url:this.getUrl(false),
7613 params:this.getParams()
7617 success : function(response){
7619 var result = this.processResponse(response);
7620 if(result === true || !result.success || !result.data){
7621 this.failureType = Roo.form.Action.LOAD_FAILURE;
7622 this.form.afterAction(this, false);
7625 this.form.clearInvalid();
7626 this.form.setValues(result.data);
7627 this.form.afterAction(this, true);
7630 handleResponse : function(response){
7631 if(this.form.reader){
7632 var rs = this.form.reader.read(response);
7633 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7635 success : rs.success,
7639 return Roo.decode(response.responseText);
7643 Roo.form.Action.ACTION_TYPES = {
7644 'load' : Roo.form.Action.Load,
7645 'submit' : Roo.form.Action.Submit
7654 * @class Roo.bootstrap.Form
7655 * @extends Roo.bootstrap.Component
7656 * Bootstrap Form class
7657 * @cfg {String} method GET | POST (default POST)
7658 * @cfg {String} labelAlign top | left (default top)
7659 * @cfg {String} align left | right - for navbars
7660 * @cfg {Boolean} loadMask load mask when submit (default true)
7665 * @param {Object} config The config object
7669 Roo.bootstrap.Form = function(config){
7671 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7673 Roo.bootstrap.Form.popover.apply();
7677 * @event clientvalidation
7678 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7679 * @param {Form} this
7680 * @param {Boolean} valid true if the form has passed client-side validation
7682 clientvalidation: true,
7684 * @event beforeaction
7685 * Fires before any action is performed. Return false to cancel the action.
7686 * @param {Form} this
7687 * @param {Action} action The action to be performed
7691 * @event actionfailed
7692 * Fires when an action fails.
7693 * @param {Form} this
7694 * @param {Action} action The action that failed
7696 actionfailed : true,
7698 * @event actioncomplete
7699 * Fires when an action is completed.
7700 * @param {Form} this
7701 * @param {Action} action The action that completed
7703 actioncomplete : true
7707 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7710 * @cfg {String} method
7711 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7716 * The URL to use for form actions if one isn't supplied in the action options.
7719 * @cfg {Boolean} fileUpload
7720 * Set to true if this form is a file upload.
7724 * @cfg {Object} baseParams
7725 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7729 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7733 * @cfg {Sting} align (left|right) for navbar forms
7738 activeAction : null,
7741 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7742 * element by passing it or its id or mask the form itself by passing in true.
7745 waitMsgTarget : false,
7750 * @cfg {Boolean} errorMask (true|false) default false
7755 * @cfg {Number} maskOffset Default 100
7760 * @cfg {Boolean} maskBody
7764 getAutoCreate : function(){
7768 method : this.method || 'POST',
7769 id : this.id || Roo.id(),
7772 if (this.parent().xtype.match(/^Nav/)) {
7773 cfg.cls = 'navbar-form navbar-' + this.align;
7777 if (this.labelAlign == 'left' ) {
7778 cfg.cls += ' form-horizontal';
7784 initEvents : function()
7786 this.el.on('submit', this.onSubmit, this);
7787 // this was added as random key presses on the form where triggering form submit.
7788 this.el.on('keypress', function(e) {
7789 if (e.getCharCode() != 13) {
7792 // we might need to allow it for textareas.. and some other items.
7793 // check e.getTarget().
7795 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7799 Roo.log("keypress blocked");
7807 onSubmit : function(e){
7812 * Returns true if client-side validation on the form is successful.
7815 isValid : function(){
7816 var items = this.getItems();
7820 items.each(function(f){
7828 if(!target && f.el.isVisible(true)){
7834 if(this.errorMask && !valid){
7835 Roo.bootstrap.Form.popover.mask(this, target);
7842 * Returns true if any fields in this form have changed since their original load.
7845 isDirty : function(){
7847 var items = this.getItems();
7848 items.each(function(f){
7858 * Performs a predefined action (submit or load) or custom actions you define on this form.
7859 * @param {String} actionName The name of the action type
7860 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7861 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7862 * accept other config options):
7864 Property Type Description
7865 ---------------- --------------- ----------------------------------------------------------------------------------
7866 url String The url for the action (defaults to the form's url)
7867 method String The form method to use (defaults to the form's method, or POST if not defined)
7868 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7869 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7870 validate the form on the client (defaults to false)
7872 * @return {BasicForm} this
7874 doAction : function(action, options){
7875 if(typeof action == 'string'){
7876 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7878 if(this.fireEvent('beforeaction', this, action) !== false){
7879 this.beforeAction(action);
7880 action.run.defer(100, action);
7886 beforeAction : function(action){
7887 var o = action.options;
7892 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7894 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7897 // not really supported yet.. ??
7899 //if(this.waitMsgTarget === true){
7900 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7901 //}else if(this.waitMsgTarget){
7902 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7903 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7905 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7911 afterAction : function(action, success){
7912 this.activeAction = null;
7913 var o = action.options;
7918 Roo.get(document.body).unmask();
7924 //if(this.waitMsgTarget === true){
7925 // this.el.unmask();
7926 //}else if(this.waitMsgTarget){
7927 // this.waitMsgTarget.unmask();
7929 // Roo.MessageBox.updateProgress(1);
7930 // Roo.MessageBox.hide();
7937 Roo.callback(o.success, o.scope, [this, action]);
7938 this.fireEvent('actioncomplete', this, action);
7942 // failure condition..
7943 // we have a scenario where updates need confirming.
7944 // eg. if a locking scenario exists..
7945 // we look for { errors : { needs_confirm : true }} in the response.
7947 (typeof(action.result) != 'undefined') &&
7948 (typeof(action.result.errors) != 'undefined') &&
7949 (typeof(action.result.errors.needs_confirm) != 'undefined')
7952 Roo.log("not supported yet");
7955 Roo.MessageBox.confirm(
7956 "Change requires confirmation",
7957 action.result.errorMsg,
7962 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7972 Roo.callback(o.failure, o.scope, [this, action]);
7973 // show an error message if no failed handler is set..
7974 if (!this.hasListener('actionfailed')) {
7975 Roo.log("need to add dialog support");
7977 Roo.MessageBox.alert("Error",
7978 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7979 action.result.errorMsg :
7980 "Saving Failed, please check your entries or try again"
7985 this.fireEvent('actionfailed', this, action);
7990 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7991 * @param {String} id The value to search for
7994 findField : function(id){
7995 var items = this.getItems();
7996 var field = items.get(id);
7998 items.each(function(f){
7999 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8006 return field || null;
8009 * Mark fields in this form invalid in bulk.
8010 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8011 * @return {BasicForm} this
8013 markInvalid : function(errors){
8014 if(errors instanceof Array){
8015 for(var i = 0, len = errors.length; i < len; i++){
8016 var fieldError = errors[i];
8017 var f = this.findField(fieldError.id);
8019 f.markInvalid(fieldError.msg);
8025 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8026 field.markInvalid(errors[id]);
8030 //Roo.each(this.childForms || [], function (f) {
8031 // f.markInvalid(errors);
8038 * Set values for fields in this form in bulk.
8039 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8040 * @return {BasicForm} this
8042 setValues : function(values){
8043 if(values instanceof Array){ // array of objects
8044 for(var i = 0, len = values.length; i < len; i++){
8046 var f = this.findField(v.id);
8048 f.setValue(v.value);
8049 if(this.trackResetOnLoad){
8050 f.originalValue = f.getValue();
8054 }else{ // object hash
8057 if(typeof values[id] != 'function' && (field = this.findField(id))){
8059 if (field.setFromData &&
8061 field.displayField &&
8062 // combos' with local stores can
8063 // be queried via setValue()
8064 // to set their value..
8065 (field.store && !field.store.isLocal)
8069 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8070 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8071 field.setFromData(sd);
8073 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8075 field.setFromData(values);
8078 field.setValue(values[id]);
8082 if(this.trackResetOnLoad){
8083 field.originalValue = field.getValue();
8089 //Roo.each(this.childForms || [], function (f) {
8090 // f.setValues(values);
8097 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8098 * they are returned as an array.
8099 * @param {Boolean} asString
8102 getValues : function(asString){
8103 //if (this.childForms) {
8104 // copy values from the child forms
8105 // Roo.each(this.childForms, function (f) {
8106 // this.setValues(f.getValues());
8112 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8113 if(asString === true){
8116 return Roo.urlDecode(fs);
8120 * Returns the fields in this form as an object with key/value pairs.
8121 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8124 getFieldValues : function(with_hidden)
8126 var items = this.getItems();
8128 items.each(function(f){
8134 var v = f.getValue();
8136 if (f.inputType =='radio') {
8137 if (typeof(ret[f.getName()]) == 'undefined') {
8138 ret[f.getName()] = ''; // empty..
8141 if (!f.el.dom.checked) {
8149 if(f.xtype == 'MoneyField'){
8150 ret[f.currencyName] = f.getCurrency();
8153 // not sure if this supported any more..
8154 if ((typeof(v) == 'object') && f.getRawValue) {
8155 v = f.getRawValue() ; // dates..
8157 // combo boxes where name != hiddenName...
8158 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8159 ret[f.name] = f.getRawValue();
8161 ret[f.getName()] = v;
8168 * Clears all invalid messages in this form.
8169 * @return {BasicForm} this
8171 clearInvalid : function(){
8172 var items = this.getItems();
8174 items.each(function(f){
8183 * @return {BasicForm} this
8186 var items = this.getItems();
8187 items.each(function(f){
8191 Roo.each(this.childForms || [], function (f) {
8199 getItems : function()
8201 var r=new Roo.util.MixedCollection(false, function(o){
8202 return o.id || (o.id = Roo.id());
8204 var iter = function(el) {
8211 Roo.each(el.items,function(e) {
8220 hideFields : function(items)
8222 Roo.each(items, function(i){
8224 var f = this.findField(i);
8230 if(f.xtype == 'DateField'){
8231 f.setVisible(false);
8240 showFields : function(items)
8242 Roo.each(items, function(i){
8244 var f = this.findField(i);
8250 if(f.xtype == 'DateField'){
8262 Roo.apply(Roo.bootstrap.Form, {
8289 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8290 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8291 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8292 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8295 this.maskEl.top.enableDisplayMode("block");
8296 this.maskEl.left.enableDisplayMode("block");
8297 this.maskEl.bottom.enableDisplayMode("block");
8298 this.maskEl.right.enableDisplayMode("block");
8300 this.toolTip = new Roo.bootstrap.Tooltip({
8301 cls : 'roo-form-error-popover',
8303 'left' : ['r-l', [-2,0], 'right'],
8304 'right' : ['l-r', [2,0], 'left'],
8305 'bottom' : ['tl-bl', [0,2], 'top'],
8306 'top' : [ 'bl-tl', [0,-2], 'bottom']
8310 this.toolTip.render(Roo.get(document.body));
8312 this.toolTip.el.enableDisplayMode("block");
8314 Roo.get(document.body).on('click', function(){
8318 Roo.get(document.body).on('touchstart', function(){
8322 this.isApplied = true
8325 mask : function(form, target)
8329 this.target = target;
8331 if(!this.form.errorMask || !target.el){
8335 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8337 Roo.log(scrollable);
8339 var ot = this.target.el.calcOffsetsTo(scrollable);
8341 var scrollTo = ot[1] - this.form.maskOffset;
8343 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8345 scrollable.scrollTo('top', scrollTo);
8347 var box = this.target.el.getBox();
8349 var zIndex = Roo.bootstrap.Modal.zIndex++;
8352 this.maskEl.top.setStyle('position', 'absolute');
8353 this.maskEl.top.setStyle('z-index', zIndex);
8354 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8355 this.maskEl.top.setLeft(0);
8356 this.maskEl.top.setTop(0);
8357 this.maskEl.top.show();
8359 this.maskEl.left.setStyle('position', 'absolute');
8360 this.maskEl.left.setStyle('z-index', zIndex);
8361 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8362 this.maskEl.left.setLeft(0);
8363 this.maskEl.left.setTop(box.y - this.padding);
8364 this.maskEl.left.show();
8366 this.maskEl.bottom.setStyle('position', 'absolute');
8367 this.maskEl.bottom.setStyle('z-index', zIndex);
8368 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8369 this.maskEl.bottom.setLeft(0);
8370 this.maskEl.bottom.setTop(box.bottom + this.padding);
8371 this.maskEl.bottom.show();
8373 this.maskEl.right.setStyle('position', 'absolute');
8374 this.maskEl.right.setStyle('z-index', zIndex);
8375 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8376 this.maskEl.right.setLeft(box.right + this.padding);
8377 this.maskEl.right.setTop(box.y - this.padding);
8378 this.maskEl.right.show();
8380 this.toolTip.bindEl = this.target.el;
8382 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8384 var tip = this.target.blankText;
8386 if(this.target.getValue() !== '' ) {
8388 if (this.target.invalidText.length) {
8389 tip = this.target.invalidText;
8390 } else if (this.target.regexText.length){
8391 tip = this.target.regexText;
8395 this.toolTip.show(tip);
8397 this.intervalID = window.setInterval(function() {
8398 Roo.bootstrap.Form.popover.unmask();
8401 window.onwheel = function(){ return false;};
8403 (function(){ this.isMasked = true; }).defer(500, this);
8409 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8413 this.maskEl.top.setStyle('position', 'absolute');
8414 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8415 this.maskEl.top.hide();
8417 this.maskEl.left.setStyle('position', 'absolute');
8418 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8419 this.maskEl.left.hide();
8421 this.maskEl.bottom.setStyle('position', 'absolute');
8422 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8423 this.maskEl.bottom.hide();
8425 this.maskEl.right.setStyle('position', 'absolute');
8426 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8427 this.maskEl.right.hide();
8429 this.toolTip.hide();
8431 this.toolTip.el.hide();
8433 window.onwheel = function(){ return true;};
8435 if(this.intervalID){
8436 window.clearInterval(this.intervalID);
8437 this.intervalID = false;
8440 this.isMasked = false;
8450 * Ext JS Library 1.1.1
8451 * Copyright(c) 2006-2007, Ext JS, LLC.
8453 * Originally Released Under LGPL - original licence link has changed is not relivant.
8456 * <script type="text/javascript">
8459 * @class Roo.form.VTypes
8460 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8463 Roo.form.VTypes = function(){
8464 // closure these in so they are only created once.
8465 var alpha = /^[a-zA-Z_]+$/;
8466 var alphanum = /^[a-zA-Z0-9_]+$/;
8467 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8468 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8470 // All these messages and functions are configurable
8473 * The function used to validate email addresses
8474 * @param {String} value The email address
8476 'email' : function(v){
8477 return email.test(v);
8480 * The error text to display when the email validation function returns false
8483 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8485 * The keystroke filter mask to be applied on email input
8488 'emailMask' : /[a-z0-9_\.\-@]/i,
8491 * The function used to validate URLs
8492 * @param {String} value The URL
8494 'url' : function(v){
8498 * The error text to display when the url validation function returns false
8501 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8504 * The function used to validate alpha values
8505 * @param {String} value The value
8507 'alpha' : function(v){
8508 return alpha.test(v);
8511 * The error text to display when the alpha validation function returns false
8514 'alphaText' : 'This field should only contain letters and _',
8516 * The keystroke filter mask to be applied on alpha input
8519 'alphaMask' : /[a-z_]/i,
8522 * The function used to validate alphanumeric values
8523 * @param {String} value The value
8525 'alphanum' : function(v){
8526 return alphanum.test(v);
8529 * The error text to display when the alphanumeric validation function returns false
8532 'alphanumText' : 'This field should only contain letters, numbers and _',
8534 * The keystroke filter mask to be applied on alphanumeric input
8537 'alphanumMask' : /[a-z0-9_]/i
8547 * @class Roo.bootstrap.Input
8548 * @extends Roo.bootstrap.Component
8549 * Bootstrap Input class
8550 * @cfg {Boolean} disabled is it disabled
8551 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8552 * @cfg {String} name name of the input
8553 * @cfg {string} fieldLabel - the label associated
8554 * @cfg {string} placeholder - placeholder to put in text.
8555 * @cfg {string} before - input group add on before
8556 * @cfg {string} after - input group add on after
8557 * @cfg {string} size - (lg|sm) or leave empty..
8558 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8559 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8560 * @cfg {Number} md colspan out of 12 for computer-sized screens
8561 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8562 * @cfg {string} value default value of the input
8563 * @cfg {Number} labelWidth set the width of label
8564 * @cfg {Number} labellg set the width of label (1-12)
8565 * @cfg {Number} labelmd set the width of label (1-12)
8566 * @cfg {Number} labelsm set the width of label (1-12)
8567 * @cfg {Number} labelxs set the width of label (1-12)
8568 * @cfg {String} labelAlign (top|left)
8569 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8570 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8571 * @cfg {String} indicatorpos (left|right) default left
8572 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8573 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8575 * @cfg {String} align (left|center|right) Default left
8576 * @cfg {Boolean} forceFeedback (true|false) Default false
8579 * Create a new Input
8580 * @param {Object} config The config object
8583 Roo.bootstrap.Input = function(config){
8585 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8590 * Fires when this field receives input focus.
8591 * @param {Roo.form.Field} this
8596 * Fires when this field loses input focus.
8597 * @param {Roo.form.Field} this
8602 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8603 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8604 * @param {Roo.form.Field} this
8605 * @param {Roo.EventObject} e The event object
8610 * Fires just before the field blurs if the field value has changed.
8611 * @param {Roo.form.Field} this
8612 * @param {Mixed} newValue The new value
8613 * @param {Mixed} oldValue The original value
8618 * Fires after the field has been marked as invalid.
8619 * @param {Roo.form.Field} this
8620 * @param {String} msg The validation message
8625 * Fires after the field has been validated with no errors.
8626 * @param {Roo.form.Field} this
8631 * Fires after the key up
8632 * @param {Roo.form.Field} this
8633 * @param {Roo.EventObject} e The event Object
8639 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8641 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8642 automatic validation (defaults to "keyup").
8644 validationEvent : "keyup",
8646 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8648 validateOnBlur : true,
8650 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8652 validationDelay : 250,
8654 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8656 focusClass : "x-form-focus", // not needed???
8660 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8662 invalidClass : "has-warning",
8665 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8667 validClass : "has-success",
8670 * @cfg {Boolean} hasFeedback (true|false) default true
8675 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8677 invalidFeedbackClass : "glyphicon-warning-sign",
8680 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8682 validFeedbackClass : "glyphicon-ok",
8685 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8687 selectOnFocus : false,
8690 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8694 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8699 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8701 disableKeyFilter : false,
8704 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8708 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8712 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8714 blankText : "Please complete this mandatory field",
8717 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8721 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8723 maxLength : Number.MAX_VALUE,
8725 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8727 minLengthText : "The minimum length for this field is {0}",
8729 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8731 maxLengthText : "The maximum length for this field is {0}",
8735 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8736 * If available, this function will be called only after the basic validators all return true, and will be passed the
8737 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8741 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8742 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8743 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8747 * @cfg {String} regexText -- Depricated - use Invalid Text
8752 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8758 autocomplete: false,
8777 formatedValue : false,
8778 forceFeedback : false,
8780 indicatorpos : 'left',
8790 parentLabelAlign : function()
8793 while (parent.parent()) {
8794 parent = parent.parent();
8795 if (typeof(parent.labelAlign) !='undefined') {
8796 return parent.labelAlign;
8803 getAutoCreate : function()
8805 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8811 if(this.inputType != 'hidden'){
8812 cfg.cls = 'form-group' //input-group
8818 type : this.inputType,
8820 cls : 'form-control',
8821 placeholder : this.placeholder || '',
8822 autocomplete : this.autocomplete || 'new-password'
8825 if(this.capture.length){
8826 input.capture = this.capture;
8829 if(this.accept.length){
8830 input.accept = this.accept + "/*";
8834 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8837 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8838 input.maxLength = this.maxLength;
8841 if (this.disabled) {
8842 input.disabled=true;
8845 if (this.readOnly) {
8846 input.readonly=true;
8850 input.name = this.name;
8854 input.cls += ' input-' + this.size;
8858 ['xs','sm','md','lg'].map(function(size){
8859 if (settings[size]) {
8860 cfg.cls += ' col-' + size + '-' + settings[size];
8864 var inputblock = input;
8868 cls: 'glyphicon form-control-feedback'
8871 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8874 cls : 'has-feedback',
8882 if (this.before || this.after) {
8885 cls : 'input-group',
8889 if (this.before && typeof(this.before) == 'string') {
8891 inputblock.cn.push({
8893 cls : 'roo-input-before input-group-addon',
8897 if (this.before && typeof(this.before) == 'object') {
8898 this.before = Roo.factory(this.before);
8900 inputblock.cn.push({
8902 cls : 'roo-input-before input-group-' +
8903 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8907 inputblock.cn.push(input);
8909 if (this.after && typeof(this.after) == 'string') {
8910 inputblock.cn.push({
8912 cls : 'roo-input-after input-group-addon',
8916 if (this.after && typeof(this.after) == 'object') {
8917 this.after = Roo.factory(this.after);
8919 inputblock.cn.push({
8921 cls : 'roo-input-after input-group-' +
8922 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8926 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8927 inputblock.cls += ' has-feedback';
8928 inputblock.cn.push(feedback);
8932 if (align ==='left' && this.fieldLabel.length) {
8934 cfg.cls += ' roo-form-group-label-left';
8939 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8940 tooltip : 'This field is required'
8945 cls : 'control-label',
8946 html : this.fieldLabel
8957 var labelCfg = cfg.cn[1];
8958 var contentCfg = cfg.cn[2];
8960 if(this.indicatorpos == 'right'){
8965 cls : 'control-label',
8969 html : this.fieldLabel
8973 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8974 tooltip : 'This field is required'
8987 labelCfg = cfg.cn[0];
8988 contentCfg = cfg.cn[1];
8992 if(this.labelWidth > 12){
8993 labelCfg.style = "width: " + this.labelWidth + 'px';
8996 if(this.labelWidth < 13 && this.labelmd == 0){
8997 this.labelmd = this.labelWidth;
9000 if(this.labellg > 0){
9001 labelCfg.cls += ' col-lg-' + this.labellg;
9002 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9005 if(this.labelmd > 0){
9006 labelCfg.cls += ' col-md-' + this.labelmd;
9007 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9010 if(this.labelsm > 0){
9011 labelCfg.cls += ' col-sm-' + this.labelsm;
9012 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9015 if(this.labelxs > 0){
9016 labelCfg.cls += ' col-xs-' + this.labelxs;
9017 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9021 } else if ( this.fieldLabel.length) {
9026 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9027 tooltip : 'This field is required'
9031 //cls : 'input-group-addon',
9032 html : this.fieldLabel
9040 if(this.indicatorpos == 'right'){
9045 //cls : 'input-group-addon',
9046 html : this.fieldLabel
9051 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9052 tooltip : 'This field is required'
9072 if (this.parentType === 'Navbar' && this.parent().bar) {
9073 cfg.cls += ' navbar-form';
9076 if (this.parentType === 'NavGroup') {
9077 cfg.cls += ' navbar-form';
9085 * return the real input element.
9087 inputEl: function ()
9089 return this.el.select('input.form-control',true).first();
9092 tooltipEl : function()
9094 return this.inputEl();
9097 indicatorEl : function()
9099 var indicator = this.el.select('i.roo-required-indicator',true).first();
9109 setDisabled : function(v)
9111 var i = this.inputEl().dom;
9113 i.removeAttribute('disabled');
9117 i.setAttribute('disabled','true');
9119 initEvents : function()
9122 this.inputEl().on("keydown" , this.fireKey, this);
9123 this.inputEl().on("focus", this.onFocus, this);
9124 this.inputEl().on("blur", this.onBlur, this);
9126 this.inputEl().relayEvent('keyup', this);
9128 this.indicator = this.indicatorEl();
9131 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9134 // reference to original value for reset
9135 this.originalValue = this.getValue();
9136 //Roo.form.TextField.superclass.initEvents.call(this);
9137 if(this.validationEvent == 'keyup'){
9138 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9139 this.inputEl().on('keyup', this.filterValidation, this);
9141 else if(this.validationEvent !== false){
9142 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9145 if(this.selectOnFocus){
9146 this.on("focus", this.preFocus, this);
9149 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9150 this.inputEl().on("keypress", this.filterKeys, this);
9152 this.inputEl().relayEvent('keypress', this);
9155 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9156 this.el.on("click", this.autoSize, this);
9159 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9160 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9163 if (typeof(this.before) == 'object') {
9164 this.before.render(this.el.select('.roo-input-before',true).first());
9166 if (typeof(this.after) == 'object') {
9167 this.after.render(this.el.select('.roo-input-after',true).first());
9170 this.inputEl().on('change', this.onChange, this);
9173 filterValidation : function(e){
9174 if(!e.isNavKeyPress()){
9175 this.validationTask.delay(this.validationDelay);
9179 * Validates the field value
9180 * @return {Boolean} True if the value is valid, else false
9182 validate : function(){
9183 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9184 if(this.disabled || this.validateValue(this.getRawValue())){
9195 * Validates a value according to the field's validation rules and marks the field as invalid
9196 * if the validation fails
9197 * @param {Mixed} value The value to validate
9198 * @return {Boolean} True if the value is valid, else false
9200 validateValue : function(value)
9202 if(this.getVisibilityEl().hasClass('hidden')){
9206 if(value.length < 1) { // if it's blank
9207 if(this.allowBlank){
9213 if(value.length < this.minLength){
9216 if(value.length > this.maxLength){
9220 var vt = Roo.form.VTypes;
9221 if(!vt[this.vtype](value, this)){
9225 if(typeof this.validator == "function"){
9226 var msg = this.validator(value);
9230 if (typeof(msg) == 'string') {
9231 this.invalidText = msg;
9235 if(this.regex && !this.regex.test(value)){
9243 fireKey : function(e){
9244 //Roo.log('field ' + e.getKey());
9245 if(e.isNavKeyPress()){
9246 this.fireEvent("specialkey", this, e);
9249 focus : function (selectText){
9251 this.inputEl().focus();
9252 if(selectText === true){
9253 this.inputEl().dom.select();
9259 onFocus : function(){
9260 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9261 // this.el.addClass(this.focusClass);
9264 this.hasFocus = true;
9265 this.startValue = this.getValue();
9266 this.fireEvent("focus", this);
9270 beforeBlur : Roo.emptyFn,
9274 onBlur : function(){
9276 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9277 //this.el.removeClass(this.focusClass);
9279 this.hasFocus = false;
9280 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9283 var v = this.getValue();
9284 if(String(v) !== String(this.startValue)){
9285 this.fireEvent('change', this, v, this.startValue);
9287 this.fireEvent("blur", this);
9290 onChange : function(e)
9292 var v = this.getValue();
9293 if(String(v) !== String(this.startValue)){
9294 this.fireEvent('change', this, v, this.startValue);
9300 * Resets the current field value to the originally loaded value and clears any validation messages
9303 this.setValue(this.originalValue);
9307 * Returns the name of the field
9308 * @return {Mixed} name The name field
9310 getName: function(){
9314 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9315 * @return {Mixed} value The field value
9317 getValue : function(){
9319 var v = this.inputEl().getValue();
9324 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9325 * @return {Mixed} value The field value
9327 getRawValue : function(){
9328 var v = this.inputEl().getValue();
9334 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9335 * @param {Mixed} value The value to set
9337 setRawValue : function(v){
9338 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9341 selectText : function(start, end){
9342 var v = this.getRawValue();
9344 start = start === undefined ? 0 : start;
9345 end = end === undefined ? v.length : end;
9346 var d = this.inputEl().dom;
9347 if(d.setSelectionRange){
9348 d.setSelectionRange(start, end);
9349 }else if(d.createTextRange){
9350 var range = d.createTextRange();
9351 range.moveStart("character", start);
9352 range.moveEnd("character", v.length-end);
9359 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9360 * @param {Mixed} value The value to set
9362 setValue : function(v){
9365 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9371 processValue : function(value){
9372 if(this.stripCharsRe){
9373 var newValue = value.replace(this.stripCharsRe, '');
9374 if(newValue !== value){
9375 this.setRawValue(newValue);
9382 preFocus : function(){
9384 if(this.selectOnFocus){
9385 this.inputEl().dom.select();
9388 filterKeys : function(e){
9390 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9393 var c = e.getCharCode(), cc = String.fromCharCode(c);
9394 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9397 if(!this.maskRe.test(cc)){
9402 * Clear any invalid styles/messages for this field
9404 clearInvalid : function(){
9406 if(!this.el || this.preventMark){ // not rendered
9411 this.el.removeClass(this.invalidClass);
9413 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9415 var feedback = this.el.select('.form-control-feedback', true).first();
9418 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9424 this.indicator.removeClass('visible');
9425 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9428 this.fireEvent('valid', this);
9432 * Mark this field as valid
9434 markValid : function()
9436 if(!this.el || this.preventMark){ // not rendered...
9440 this.el.removeClass([this.invalidClass, this.validClass]);
9442 var feedback = this.el.select('.form-control-feedback', true).first();
9445 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9449 this.indicator.removeClass('visible');
9450 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9457 if(this.allowBlank && !this.getRawValue().length){
9461 this.el.addClass(this.validClass);
9463 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9465 var feedback = this.el.select('.form-control-feedback', true).first();
9468 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9469 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9474 this.fireEvent('valid', this);
9478 * Mark this field as invalid
9479 * @param {String} msg The validation message
9481 markInvalid : function(msg)
9483 if(!this.el || this.preventMark){ // not rendered
9487 this.el.removeClass([this.invalidClass, this.validClass]);
9489 var feedback = this.el.select('.form-control-feedback', true).first();
9492 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9499 if(this.allowBlank && !this.getRawValue().length){
9504 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9505 this.indicator.addClass('visible');
9508 this.el.addClass(this.invalidClass);
9510 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9512 var feedback = this.el.select('.form-control-feedback', true).first();
9515 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9517 if(this.getValue().length || this.forceFeedback){
9518 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9525 this.fireEvent('invalid', this, msg);
9528 SafariOnKeyDown : function(event)
9530 // this is a workaround for a password hang bug on chrome/ webkit.
9531 if (this.inputEl().dom.type != 'password') {
9535 var isSelectAll = false;
9537 if(this.inputEl().dom.selectionEnd > 0){
9538 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9540 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9541 event.preventDefault();
9546 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9548 event.preventDefault();
9549 // this is very hacky as keydown always get's upper case.
9551 var cc = String.fromCharCode(event.getCharCode());
9552 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9556 adjustWidth : function(tag, w){
9557 tag = tag.toLowerCase();
9558 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9559 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9563 if(tag == 'textarea'){
9566 }else if(Roo.isOpera){
9570 if(tag == 'textarea'){
9578 setFieldLabel : function(v)
9585 var ar = this.el.select('label > span',true);
9587 if (ar.elements.length) {
9588 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9589 this.fieldLabel = v;
9593 var br = this.el.select('label',true);
9595 if(br.elements.length) {
9596 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9597 this.fieldLabel = v;
9601 Roo.log('Cannot Found any of label > span || label in input');
9605 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9606 this.fieldLabel = v;
9621 * @class Roo.bootstrap.TextArea
9622 * @extends Roo.bootstrap.Input
9623 * Bootstrap TextArea class
9624 * @cfg {Number} cols Specifies the visible width of a text area
9625 * @cfg {Number} rows Specifies the visible number of lines in a text area
9626 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9627 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9628 * @cfg {string} html text
9631 * Create a new TextArea
9632 * @param {Object} config The config object
9635 Roo.bootstrap.TextArea = function(config){
9636 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9640 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9650 getAutoCreate : function(){
9652 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9658 if(this.inputType != 'hidden'){
9659 cfg.cls = 'form-group' //input-group
9667 value : this.value || '',
9668 html: this.html || '',
9669 cls : 'form-control',
9670 placeholder : this.placeholder || ''
9674 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9675 input.maxLength = this.maxLength;
9679 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9683 input.cols = this.cols;
9686 if (this.readOnly) {
9687 input.readonly = true;
9691 input.name = this.name;
9695 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9699 ['xs','sm','md','lg'].map(function(size){
9700 if (settings[size]) {
9701 cfg.cls += ' col-' + size + '-' + settings[size];
9705 var inputblock = input;
9707 if(this.hasFeedback && !this.allowBlank){
9711 cls: 'glyphicon form-control-feedback'
9715 cls : 'has-feedback',
9724 if (this.before || this.after) {
9727 cls : 'input-group',
9731 inputblock.cn.push({
9733 cls : 'input-group-addon',
9738 inputblock.cn.push(input);
9740 if(this.hasFeedback && !this.allowBlank){
9741 inputblock.cls += ' has-feedback';
9742 inputblock.cn.push(feedback);
9746 inputblock.cn.push({
9748 cls : 'input-group-addon',
9755 if (align ==='left' && this.fieldLabel.length) {
9760 cls : 'control-label',
9761 html : this.fieldLabel
9772 if(this.labelWidth > 12){
9773 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9776 if(this.labelWidth < 13 && this.labelmd == 0){
9777 this.labelmd = this.labelWidth;
9780 if(this.labellg > 0){
9781 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9782 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9785 if(this.labelmd > 0){
9786 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9787 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9790 if(this.labelsm > 0){
9791 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9792 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9795 if(this.labelxs > 0){
9796 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9797 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9800 } else if ( this.fieldLabel.length) {
9805 //cls : 'input-group-addon',
9806 html : this.fieldLabel
9824 if (this.disabled) {
9825 input.disabled=true;
9832 * return the real textarea element.
9834 inputEl: function ()
9836 return this.el.select('textarea.form-control',true).first();
9840 * Clear any invalid styles/messages for this field
9842 clearInvalid : function()
9845 if(!this.el || this.preventMark){ // not rendered
9849 var label = this.el.select('label', true).first();
9850 var icon = this.el.select('i.fa-star', true).first();
9856 this.el.removeClass(this.invalidClass);
9858 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9860 var feedback = this.el.select('.form-control-feedback', true).first();
9863 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9868 this.fireEvent('valid', this);
9872 * Mark this field as valid
9874 markValid : function()
9876 if(!this.el || this.preventMark){ // not rendered
9880 this.el.removeClass([this.invalidClass, this.validClass]);
9882 var feedback = this.el.select('.form-control-feedback', true).first();
9885 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9888 if(this.disabled || this.allowBlank){
9892 var label = this.el.select('label', true).first();
9893 var icon = this.el.select('i.fa-star', true).first();
9899 this.el.addClass(this.validClass);
9901 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9903 var feedback = this.el.select('.form-control-feedback', true).first();
9906 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9907 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9912 this.fireEvent('valid', this);
9916 * Mark this field as invalid
9917 * @param {String} msg The validation message
9919 markInvalid : function(msg)
9921 if(!this.el || this.preventMark){ // not rendered
9925 this.el.removeClass([this.invalidClass, this.validClass]);
9927 var feedback = this.el.select('.form-control-feedback', true).first();
9930 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9933 if(this.disabled || this.allowBlank){
9937 var label = this.el.select('label', true).first();
9938 var icon = this.el.select('i.fa-star', true).first();
9940 if(!this.getValue().length && label && !icon){
9941 this.el.createChild({
9943 cls : 'text-danger fa fa-lg fa-star',
9944 tooltip : 'This field is required',
9945 style : 'margin-right:5px;'
9949 this.el.addClass(this.invalidClass);
9951 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9953 var feedback = this.el.select('.form-control-feedback', true).first();
9956 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9958 if(this.getValue().length || this.forceFeedback){
9959 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9966 this.fireEvent('invalid', this, msg);
9974 * trigger field - base class for combo..
9979 * @class Roo.bootstrap.TriggerField
9980 * @extends Roo.bootstrap.Input
9981 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9982 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9983 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9984 * for which you can provide a custom implementation. For example:
9986 var trigger = new Roo.bootstrap.TriggerField();
9987 trigger.onTriggerClick = myTriggerFn;
9988 trigger.applyTo('my-field');
9991 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9992 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9993 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9994 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9995 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9998 * Create a new TriggerField.
9999 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10000 * to the base TextField)
10002 Roo.bootstrap.TriggerField = function(config){
10003 this.mimicing = false;
10004 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10007 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10009 * @cfg {String} triggerClass A CSS class to apply to the trigger
10012 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10017 * @cfg {Boolean} removable (true|false) special filter default false
10021 /** @cfg {Boolean} grow @hide */
10022 /** @cfg {Number} growMin @hide */
10023 /** @cfg {Number} growMax @hide */
10029 autoSize: Roo.emptyFn,
10033 deferHeight : true,
10036 actionMode : 'wrap',
10041 getAutoCreate : function(){
10043 var align = this.labelAlign || this.parentLabelAlign();
10048 cls: 'form-group' //input-group
10055 type : this.inputType,
10056 cls : 'form-control',
10057 autocomplete: 'new-password',
10058 placeholder : this.placeholder || ''
10062 input.name = this.name;
10065 input.cls += ' input-' + this.size;
10068 if (this.disabled) {
10069 input.disabled=true;
10072 var inputblock = input;
10074 if(this.hasFeedback && !this.allowBlank){
10078 cls: 'glyphicon form-control-feedback'
10081 if(this.removable && !this.editable && !this.tickable){
10083 cls : 'has-feedback',
10089 cls : 'roo-combo-removable-btn close'
10096 cls : 'has-feedback',
10105 if(this.removable && !this.editable && !this.tickable){
10107 cls : 'roo-removable',
10113 cls : 'roo-combo-removable-btn close'
10120 if (this.before || this.after) {
10123 cls : 'input-group',
10127 inputblock.cn.push({
10129 cls : 'input-group-addon',
10134 inputblock.cn.push(input);
10136 if(this.hasFeedback && !this.allowBlank){
10137 inputblock.cls += ' has-feedback';
10138 inputblock.cn.push(feedback);
10142 inputblock.cn.push({
10144 cls : 'input-group-addon',
10157 cls: 'form-hidden-field'
10171 cls: 'form-hidden-field'
10175 cls: 'roo-select2-choices',
10179 cls: 'roo-select2-search-field',
10192 cls: 'roo-select2-container input-group',
10197 // cls: 'typeahead typeahead-long dropdown-menu',
10198 // style: 'display:none'
10203 if(!this.multiple && this.showToggleBtn){
10209 if (this.caret != false) {
10212 cls: 'fa fa-' + this.caret
10219 cls : 'input-group-addon btn dropdown-toggle',
10224 cls: 'combobox-clear',
10238 combobox.cls += ' roo-select2-container-multi';
10241 if (align ==='left' && this.fieldLabel.length) {
10243 cfg.cls += ' roo-form-group-label-left';
10248 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10249 tooltip : 'This field is required'
10254 cls : 'control-label',
10255 html : this.fieldLabel
10267 var labelCfg = cfg.cn[1];
10268 var contentCfg = cfg.cn[2];
10270 if(this.indicatorpos == 'right'){
10275 cls : 'control-label',
10279 html : this.fieldLabel
10283 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10284 tooltip : 'This field is required'
10297 labelCfg = cfg.cn[0];
10298 contentCfg = cfg.cn[1];
10301 if(this.labelWidth > 12){
10302 labelCfg.style = "width: " + this.labelWidth + 'px';
10305 if(this.labelWidth < 13 && this.labelmd == 0){
10306 this.labelmd = this.labelWidth;
10309 if(this.labellg > 0){
10310 labelCfg.cls += ' col-lg-' + this.labellg;
10311 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10314 if(this.labelmd > 0){
10315 labelCfg.cls += ' col-md-' + this.labelmd;
10316 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10319 if(this.labelsm > 0){
10320 labelCfg.cls += ' col-sm-' + this.labelsm;
10321 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10324 if(this.labelxs > 0){
10325 labelCfg.cls += ' col-xs-' + this.labelxs;
10326 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10329 } else if ( this.fieldLabel.length) {
10330 // Roo.log(" label");
10334 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10335 tooltip : 'This field is required'
10339 //cls : 'input-group-addon',
10340 html : this.fieldLabel
10348 if(this.indicatorpos == 'right'){
10356 html : this.fieldLabel
10360 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10361 tooltip : 'This field is required'
10374 // Roo.log(" no label && no align");
10381 ['xs','sm','md','lg'].map(function(size){
10382 if (settings[size]) {
10383 cfg.cls += ' col-' + size + '-' + settings[size];
10394 onResize : function(w, h){
10395 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10396 // if(typeof w == 'number'){
10397 // var x = w - this.trigger.getWidth();
10398 // this.inputEl().setWidth(this.adjustWidth('input', x));
10399 // this.trigger.setStyle('left', x+'px');
10404 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10407 getResizeEl : function(){
10408 return this.inputEl();
10412 getPositionEl : function(){
10413 return this.inputEl();
10417 alignErrorIcon : function(){
10418 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10422 initEvents : function(){
10426 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10427 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10428 if(!this.multiple && this.showToggleBtn){
10429 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10430 if(this.hideTrigger){
10431 this.trigger.setDisplayed(false);
10433 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10437 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10440 if(this.removable && !this.editable && !this.tickable){
10441 var close = this.closeTriggerEl();
10444 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10445 close.on('click', this.removeBtnClick, this, close);
10449 //this.trigger.addClassOnOver('x-form-trigger-over');
10450 //this.trigger.addClassOnClick('x-form-trigger-click');
10453 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10457 closeTriggerEl : function()
10459 var close = this.el.select('.roo-combo-removable-btn', true).first();
10460 return close ? close : false;
10463 removeBtnClick : function(e, h, el)
10465 e.preventDefault();
10467 if(this.fireEvent("remove", this) !== false){
10469 this.fireEvent("afterremove", this)
10473 createList : function()
10475 this.list = Roo.get(document.body).createChild({
10477 cls: 'typeahead typeahead-long dropdown-menu',
10478 style: 'display:none'
10481 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10486 initTrigger : function(){
10491 onDestroy : function(){
10493 this.trigger.removeAllListeners();
10494 // this.trigger.remove();
10497 // this.wrap.remove();
10499 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10503 onFocus : function(){
10504 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10506 if(!this.mimicing){
10507 this.wrap.addClass('x-trigger-wrap-focus');
10508 this.mimicing = true;
10509 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10510 if(this.monitorTab){
10511 this.el.on("keydown", this.checkTab, this);
10518 checkTab : function(e){
10519 if(e.getKey() == e.TAB){
10520 this.triggerBlur();
10525 onBlur : function(){
10530 mimicBlur : function(e, t){
10532 if(!this.wrap.contains(t) && this.validateBlur()){
10533 this.triggerBlur();
10539 triggerBlur : function(){
10540 this.mimicing = false;
10541 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10542 if(this.monitorTab){
10543 this.el.un("keydown", this.checkTab, this);
10545 //this.wrap.removeClass('x-trigger-wrap-focus');
10546 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10550 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10551 validateBlur : function(e, t){
10556 onDisable : function(){
10557 this.inputEl().dom.disabled = true;
10558 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10560 // this.wrap.addClass('x-item-disabled');
10565 onEnable : function(){
10566 this.inputEl().dom.disabled = false;
10567 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10569 // this.el.removeClass('x-item-disabled');
10574 onShow : function(){
10575 var ae = this.getActionEl();
10578 ae.dom.style.display = '';
10579 ae.dom.style.visibility = 'visible';
10585 onHide : function(){
10586 var ae = this.getActionEl();
10587 ae.dom.style.display = 'none';
10591 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10592 * by an implementing function.
10594 * @param {EventObject} e
10596 onTriggerClick : Roo.emptyFn
10600 * Ext JS Library 1.1.1
10601 * Copyright(c) 2006-2007, Ext JS, LLC.
10603 * Originally Released Under LGPL - original licence link has changed is not relivant.
10606 * <script type="text/javascript">
10611 * @class Roo.data.SortTypes
10613 * Defines the default sorting (casting?) comparison functions used when sorting data.
10615 Roo.data.SortTypes = {
10617 * Default sort that does nothing
10618 * @param {Mixed} s The value being converted
10619 * @return {Mixed} The comparison value
10621 none : function(s){
10626 * The regular expression used to strip tags
10630 stripTagsRE : /<\/?[^>]+>/gi,
10633 * Strips all HTML tags to sort on text only
10634 * @param {Mixed} s The value being converted
10635 * @return {String} The comparison value
10637 asText : function(s){
10638 return String(s).replace(this.stripTagsRE, "");
10642 * Strips all HTML tags to sort on text only - Case insensitive
10643 * @param {Mixed} s The value being converted
10644 * @return {String} The comparison value
10646 asUCText : function(s){
10647 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10651 * Case insensitive string
10652 * @param {Mixed} s The value being converted
10653 * @return {String} The comparison value
10655 asUCString : function(s) {
10656 return String(s).toUpperCase();
10661 * @param {Mixed} s The value being converted
10662 * @return {Number} The comparison value
10664 asDate : function(s) {
10668 if(s instanceof Date){
10669 return s.getTime();
10671 return Date.parse(String(s));
10676 * @param {Mixed} s The value being converted
10677 * @return {Float} The comparison value
10679 asFloat : function(s) {
10680 var val = parseFloat(String(s).replace(/,/g, ""));
10689 * @param {Mixed} s The value being converted
10690 * @return {Number} The comparison value
10692 asInt : function(s) {
10693 var val = parseInt(String(s).replace(/,/g, ""));
10701 * Ext JS Library 1.1.1
10702 * Copyright(c) 2006-2007, Ext JS, LLC.
10704 * Originally Released Under LGPL - original licence link has changed is not relivant.
10707 * <script type="text/javascript">
10711 * @class Roo.data.Record
10712 * Instances of this class encapsulate both record <em>definition</em> information, and record
10713 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10714 * to access Records cached in an {@link Roo.data.Store} object.<br>
10716 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10717 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10720 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10722 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10723 * {@link #create}. The parameters are the same.
10724 * @param {Array} data An associative Array of data values keyed by the field name.
10725 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10726 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10727 * not specified an integer id is generated.
10729 Roo.data.Record = function(data, id){
10730 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10735 * Generate a constructor for a specific record layout.
10736 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10737 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10738 * Each field definition object may contain the following properties: <ul>
10739 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10740 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10741 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10742 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10743 * is being used, then this is a string containing the javascript expression to reference the data relative to
10744 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10745 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10746 * this may be omitted.</p></li>
10747 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10748 * <ul><li>auto (Default, implies no conversion)</li>
10753 * <li>date</li></ul></p></li>
10754 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10755 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10756 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10757 * by the Reader into an object that will be stored in the Record. It is passed the
10758 * following parameters:<ul>
10759 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10761 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10763 * <br>usage:<br><pre><code>
10764 var TopicRecord = Roo.data.Record.create(
10765 {name: 'title', mapping: 'topic_title'},
10766 {name: 'author', mapping: 'username'},
10767 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10768 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10769 {name: 'lastPoster', mapping: 'user2'},
10770 {name: 'excerpt', mapping: 'post_text'}
10773 var myNewRecord = new TopicRecord({
10774 title: 'Do my job please',
10777 lastPost: new Date(),
10778 lastPoster: 'Animal',
10779 excerpt: 'No way dude!'
10781 myStore.add(myNewRecord);
10786 Roo.data.Record.create = function(o){
10787 var f = function(){
10788 f.superclass.constructor.apply(this, arguments);
10790 Roo.extend(f, Roo.data.Record);
10791 var p = f.prototype;
10792 p.fields = new Roo.util.MixedCollection(false, function(field){
10795 for(var i = 0, len = o.length; i < len; i++){
10796 p.fields.add(new Roo.data.Field(o[i]));
10798 f.getField = function(name){
10799 return p.fields.get(name);
10804 Roo.data.Record.AUTO_ID = 1000;
10805 Roo.data.Record.EDIT = 'edit';
10806 Roo.data.Record.REJECT = 'reject';
10807 Roo.data.Record.COMMIT = 'commit';
10809 Roo.data.Record.prototype = {
10811 * Readonly flag - true if this record has been modified.
10820 join : function(store){
10821 this.store = store;
10825 * Set the named field to the specified value.
10826 * @param {String} name The name of the field to set.
10827 * @param {Object} value The value to set the field to.
10829 set : function(name, value){
10830 if(this.data[name] == value){
10834 if(!this.modified){
10835 this.modified = {};
10837 if(typeof this.modified[name] == 'undefined'){
10838 this.modified[name] = this.data[name];
10840 this.data[name] = value;
10841 if(!this.editing && this.store){
10842 this.store.afterEdit(this);
10847 * Get the value of the named field.
10848 * @param {String} name The name of the field to get the value of.
10849 * @return {Object} The value of the field.
10851 get : function(name){
10852 return this.data[name];
10856 beginEdit : function(){
10857 this.editing = true;
10858 this.modified = {};
10862 cancelEdit : function(){
10863 this.editing = false;
10864 delete this.modified;
10868 endEdit : function(){
10869 this.editing = false;
10870 if(this.dirty && this.store){
10871 this.store.afterEdit(this);
10876 * Usually called by the {@link Roo.data.Store} which owns the Record.
10877 * Rejects all changes made to the Record since either creation, or the last commit operation.
10878 * Modified fields are reverted to their original values.
10880 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10881 * of reject operations.
10883 reject : function(){
10884 var m = this.modified;
10886 if(typeof m[n] != "function"){
10887 this.data[n] = m[n];
10890 this.dirty = false;
10891 delete this.modified;
10892 this.editing = false;
10894 this.store.afterReject(this);
10899 * Usually called by the {@link Roo.data.Store} which owns the Record.
10900 * Commits all changes made to the Record since either creation, or the last commit operation.
10902 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10903 * of commit operations.
10905 commit : function(){
10906 this.dirty = false;
10907 delete this.modified;
10908 this.editing = false;
10910 this.store.afterCommit(this);
10915 hasError : function(){
10916 return this.error != null;
10920 clearError : function(){
10925 * Creates a copy of this record.
10926 * @param {String} id (optional) A new record id if you don't want to use this record's id
10929 copy : function(newId) {
10930 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10934 * Ext JS Library 1.1.1
10935 * Copyright(c) 2006-2007, Ext JS, LLC.
10937 * Originally Released Under LGPL - original licence link has changed is not relivant.
10940 * <script type="text/javascript">
10946 * @class Roo.data.Store
10947 * @extends Roo.util.Observable
10948 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10949 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10951 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10952 * has no knowledge of the format of the data returned by the Proxy.<br>
10954 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10955 * instances from the data object. These records are cached and made available through accessor functions.
10957 * Creates a new Store.
10958 * @param {Object} config A config object containing the objects needed for the Store to access data,
10959 * and read the data into Records.
10961 Roo.data.Store = function(config){
10962 this.data = new Roo.util.MixedCollection(false);
10963 this.data.getKey = function(o){
10966 this.baseParams = {};
10968 this.paramNames = {
10973 "multisort" : "_multisort"
10976 if(config && config.data){
10977 this.inlineData = config.data;
10978 delete config.data;
10981 Roo.apply(this, config);
10983 if(this.reader){ // reader passed
10984 this.reader = Roo.factory(this.reader, Roo.data);
10985 this.reader.xmodule = this.xmodule || false;
10986 if(!this.recordType){
10987 this.recordType = this.reader.recordType;
10989 if(this.reader.onMetaChange){
10990 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10994 if(this.recordType){
10995 this.fields = this.recordType.prototype.fields;
10997 this.modified = [];
11001 * @event datachanged
11002 * Fires when the data cache has changed, and a widget which is using this Store
11003 * as a Record cache should refresh its view.
11004 * @param {Store} this
11006 datachanged : true,
11008 * @event metachange
11009 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11010 * @param {Store} this
11011 * @param {Object} meta The JSON metadata
11016 * Fires when Records have been added to the Store
11017 * @param {Store} this
11018 * @param {Roo.data.Record[]} records The array of Records added
11019 * @param {Number} index The index at which the record(s) were added
11024 * Fires when a Record has been removed from the Store
11025 * @param {Store} this
11026 * @param {Roo.data.Record} record The Record that was removed
11027 * @param {Number} index The index at which the record was removed
11032 * Fires when a Record has been updated
11033 * @param {Store} this
11034 * @param {Roo.data.Record} record The Record that was updated
11035 * @param {String} operation The update operation being performed. Value may be one of:
11037 Roo.data.Record.EDIT
11038 Roo.data.Record.REJECT
11039 Roo.data.Record.COMMIT
11045 * Fires when the data cache has been cleared.
11046 * @param {Store} this
11050 * @event beforeload
11051 * Fires before a request is made for a new data object. If the beforeload handler returns false
11052 * the load action will be canceled.
11053 * @param {Store} this
11054 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11058 * @event beforeloadadd
11059 * Fires after a new set of Records has been loaded.
11060 * @param {Store} this
11061 * @param {Roo.data.Record[]} records The Records that were loaded
11062 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11064 beforeloadadd : true,
11067 * Fires after a new set of Records has been loaded, before they are added to the store.
11068 * @param {Store} this
11069 * @param {Roo.data.Record[]} records The Records that were loaded
11070 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11071 * @params {Object} return from reader
11075 * @event loadexception
11076 * Fires if an exception occurs in the Proxy during loading.
11077 * Called with the signature of the Proxy's "loadexception" event.
11078 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11081 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11082 * @param {Object} load options
11083 * @param {Object} jsonData from your request (normally this contains the Exception)
11085 loadexception : true
11089 this.proxy = Roo.factory(this.proxy, Roo.data);
11090 this.proxy.xmodule = this.xmodule || false;
11091 this.relayEvents(this.proxy, ["loadexception"]);
11093 this.sortToggle = {};
11094 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11096 Roo.data.Store.superclass.constructor.call(this);
11098 if(this.inlineData){
11099 this.loadData(this.inlineData);
11100 delete this.inlineData;
11104 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11106 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11107 * without a remote query - used by combo/forms at present.
11111 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11114 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11117 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11118 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11121 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11122 * on any HTTP request
11125 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11128 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11132 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11133 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11135 remoteSort : false,
11138 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11139 * loaded or when a record is removed. (defaults to false).
11141 pruneModifiedRecords : false,
11144 lastOptions : null,
11147 * Add Records to the Store and fires the add event.
11148 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11150 add : function(records){
11151 records = [].concat(records);
11152 for(var i = 0, len = records.length; i < len; i++){
11153 records[i].join(this);
11155 var index = this.data.length;
11156 this.data.addAll(records);
11157 this.fireEvent("add", this, records, index);
11161 * Remove a Record from the Store and fires the remove event.
11162 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11164 remove : function(record){
11165 var index = this.data.indexOf(record);
11166 this.data.removeAt(index);
11168 if(this.pruneModifiedRecords){
11169 this.modified.remove(record);
11171 this.fireEvent("remove", this, record, index);
11175 * Remove all Records from the Store and fires the clear event.
11177 removeAll : function(){
11179 if(this.pruneModifiedRecords){
11180 this.modified = [];
11182 this.fireEvent("clear", this);
11186 * Inserts Records to the Store at the given index and fires the add event.
11187 * @param {Number} index The start index at which to insert the passed Records.
11188 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11190 insert : function(index, records){
11191 records = [].concat(records);
11192 for(var i = 0, len = records.length; i < len; i++){
11193 this.data.insert(index, records[i]);
11194 records[i].join(this);
11196 this.fireEvent("add", this, records, index);
11200 * Get the index within the cache of the passed Record.
11201 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11202 * @return {Number} The index of the passed Record. Returns -1 if not found.
11204 indexOf : function(record){
11205 return this.data.indexOf(record);
11209 * Get the index within the cache of the Record with the passed id.
11210 * @param {String} id The id of the Record to find.
11211 * @return {Number} The index of the Record. Returns -1 if not found.
11213 indexOfId : function(id){
11214 return this.data.indexOfKey(id);
11218 * Get the Record with the specified id.
11219 * @param {String} id The id of the Record to find.
11220 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11222 getById : function(id){
11223 return this.data.key(id);
11227 * Get the Record at the specified index.
11228 * @param {Number} index The index of the Record to find.
11229 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11231 getAt : function(index){
11232 return this.data.itemAt(index);
11236 * Returns a range of Records between specified indices.
11237 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11238 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11239 * @return {Roo.data.Record[]} An array of Records
11241 getRange : function(start, end){
11242 return this.data.getRange(start, end);
11246 storeOptions : function(o){
11247 o = Roo.apply({}, o);
11250 this.lastOptions = o;
11254 * Loads the Record cache from the configured Proxy using the configured Reader.
11256 * If using remote paging, then the first load call must specify the <em>start</em>
11257 * and <em>limit</em> properties in the options.params property to establish the initial
11258 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11260 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11261 * and this call will return before the new data has been loaded. Perform any post-processing
11262 * in a callback function, or in a "load" event handler.</strong>
11264 * @param {Object} options An object containing properties which control loading options:<ul>
11265 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11266 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11267 * passed the following arguments:<ul>
11268 * <li>r : Roo.data.Record[]</li>
11269 * <li>options: Options object from the load call</li>
11270 * <li>success: Boolean success indicator</li></ul></li>
11271 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11272 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11275 load : function(options){
11276 options = options || {};
11277 if(this.fireEvent("beforeload", this, options) !== false){
11278 this.storeOptions(options);
11279 var p = Roo.apply(options.params || {}, this.baseParams);
11280 // if meta was not loaded from remote source.. try requesting it.
11281 if (!this.reader.metaFromRemote) {
11282 p._requestMeta = 1;
11284 if(this.sortInfo && this.remoteSort){
11285 var pn = this.paramNames;
11286 p[pn["sort"]] = this.sortInfo.field;
11287 p[pn["dir"]] = this.sortInfo.direction;
11289 if (this.multiSort) {
11290 var pn = this.paramNames;
11291 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11294 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11299 * Reloads the Record cache from the configured Proxy using the configured Reader and
11300 * the options from the last load operation performed.
11301 * @param {Object} options (optional) An object containing properties which may override the options
11302 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11303 * the most recently used options are reused).
11305 reload : function(options){
11306 this.load(Roo.applyIf(options||{}, this.lastOptions));
11310 // Called as a callback by the Reader during a load operation.
11311 loadRecords : function(o, options, success){
11312 if(!o || success === false){
11313 if(success !== false){
11314 this.fireEvent("load", this, [], options, o);
11316 if(options.callback){
11317 options.callback.call(options.scope || this, [], options, false);
11321 // if data returned failure - throw an exception.
11322 if (o.success === false) {
11323 // show a message if no listener is registered.
11324 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11325 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11327 // loadmask wil be hooked into this..
11328 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11331 var r = o.records, t = o.totalRecords || r.length;
11333 this.fireEvent("beforeloadadd", this, r, options, o);
11335 if(!options || options.add !== true){
11336 if(this.pruneModifiedRecords){
11337 this.modified = [];
11339 for(var i = 0, len = r.length; i < len; i++){
11343 this.data = this.snapshot;
11344 delete this.snapshot;
11347 this.data.addAll(r);
11348 this.totalLength = t;
11350 this.fireEvent("datachanged", this);
11352 this.totalLength = Math.max(t, this.data.length+r.length);
11356 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11358 var e = new Roo.data.Record({});
11360 e.set(this.parent.displayField, this.parent.emptyTitle);
11361 e.set(this.parent.valueField, '');
11366 this.fireEvent("load", this, r, options, o);
11367 if(options.callback){
11368 options.callback.call(options.scope || this, r, options, true);
11374 * Loads data from a passed data block. A Reader which understands the format of the data
11375 * must have been configured in the constructor.
11376 * @param {Object} data The data block from which to read the Records. The format of the data expected
11377 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11378 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11380 loadData : function(o, append){
11381 var r = this.reader.readRecords(o);
11382 this.loadRecords(r, {add: append}, true);
11386 * Gets the number of cached records.
11388 * <em>If using paging, this may not be the total size of the dataset. If the data object
11389 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11390 * the data set size</em>
11392 getCount : function(){
11393 return this.data.length || 0;
11397 * Gets the total number of records in the dataset as returned by the server.
11399 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11400 * the dataset size</em>
11402 getTotalCount : function(){
11403 return this.totalLength || 0;
11407 * Returns the sort state of the Store as an object with two properties:
11409 field {String} The name of the field by which the Records are sorted
11410 direction {String} The sort order, "ASC" or "DESC"
11413 getSortState : function(){
11414 return this.sortInfo;
11418 applySort : function(){
11419 if(this.sortInfo && !this.remoteSort){
11420 var s = this.sortInfo, f = s.field;
11421 var st = this.fields.get(f).sortType;
11422 var fn = function(r1, r2){
11423 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11424 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11426 this.data.sort(s.direction, fn);
11427 if(this.snapshot && this.snapshot != this.data){
11428 this.snapshot.sort(s.direction, fn);
11434 * Sets the default sort column and order to be used by the next load operation.
11435 * @param {String} fieldName The name of the field to sort by.
11436 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11438 setDefaultSort : function(field, dir){
11439 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11443 * Sort the Records.
11444 * If remote sorting is used, the sort is performed on the server, and the cache is
11445 * reloaded. If local sorting is used, the cache is sorted internally.
11446 * @param {String} fieldName The name of the field to sort by.
11447 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11449 sort : function(fieldName, dir){
11450 var f = this.fields.get(fieldName);
11452 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11454 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11455 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11460 this.sortToggle[f.name] = dir;
11461 this.sortInfo = {field: f.name, direction: dir};
11462 if(!this.remoteSort){
11464 this.fireEvent("datachanged", this);
11466 this.load(this.lastOptions);
11471 * Calls the specified function for each of the Records in the cache.
11472 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11473 * Returning <em>false</em> aborts and exits the iteration.
11474 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11476 each : function(fn, scope){
11477 this.data.each(fn, scope);
11481 * Gets all records modified since the last commit. Modified records are persisted across load operations
11482 * (e.g., during paging).
11483 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11485 getModifiedRecords : function(){
11486 return this.modified;
11490 createFilterFn : function(property, value, anyMatch){
11491 if(!value.exec){ // not a regex
11492 value = String(value);
11493 if(value.length == 0){
11496 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11498 return function(r){
11499 return value.test(r.data[property]);
11504 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11505 * @param {String} property A field on your records
11506 * @param {Number} start The record index to start at (defaults to 0)
11507 * @param {Number} end The last record index to include (defaults to length - 1)
11508 * @return {Number} The sum
11510 sum : function(property, start, end){
11511 var rs = this.data.items, v = 0;
11512 start = start || 0;
11513 end = (end || end === 0) ? end : rs.length-1;
11515 for(var i = start; i <= end; i++){
11516 v += (rs[i].data[property] || 0);
11522 * Filter the records by a specified property.
11523 * @param {String} field A field on your records
11524 * @param {String/RegExp} value Either a string that the field
11525 * should start with or a RegExp to test against the field
11526 * @param {Boolean} anyMatch True to match any part not just the beginning
11528 filter : function(property, value, anyMatch){
11529 var fn = this.createFilterFn(property, value, anyMatch);
11530 return fn ? this.filterBy(fn) : this.clearFilter();
11534 * Filter by a function. The specified function will be called with each
11535 * record in this data source. If the function returns true the record is included,
11536 * otherwise it is filtered.
11537 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11538 * @param {Object} scope (optional) The scope of the function (defaults to this)
11540 filterBy : function(fn, scope){
11541 this.snapshot = this.snapshot || this.data;
11542 this.data = this.queryBy(fn, scope||this);
11543 this.fireEvent("datachanged", this);
11547 * Query the records by a specified property.
11548 * @param {String} field A field on your records
11549 * @param {String/RegExp} value Either a string that the field
11550 * should start with or a RegExp to test against the field
11551 * @param {Boolean} anyMatch True to match any part not just the beginning
11552 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11554 query : function(property, value, anyMatch){
11555 var fn = this.createFilterFn(property, value, anyMatch);
11556 return fn ? this.queryBy(fn) : this.data.clone();
11560 * Query by a function. The specified function will be called with each
11561 * record in this data source. If the function returns true the record is included
11563 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11564 * @param {Object} scope (optional) The scope of the function (defaults to this)
11565 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11567 queryBy : function(fn, scope){
11568 var data = this.snapshot || this.data;
11569 return data.filterBy(fn, scope||this);
11573 * Collects unique values for a particular dataIndex from this store.
11574 * @param {String} dataIndex The property to collect
11575 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11576 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11577 * @return {Array} An array of the unique values
11579 collect : function(dataIndex, allowNull, bypassFilter){
11580 var d = (bypassFilter === true && this.snapshot) ?
11581 this.snapshot.items : this.data.items;
11582 var v, sv, r = [], l = {};
11583 for(var i = 0, len = d.length; i < len; i++){
11584 v = d[i].data[dataIndex];
11586 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11595 * Revert to a view of the Record cache with no filtering applied.
11596 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11598 clearFilter : function(suppressEvent){
11599 if(this.snapshot && this.snapshot != this.data){
11600 this.data = this.snapshot;
11601 delete this.snapshot;
11602 if(suppressEvent !== true){
11603 this.fireEvent("datachanged", this);
11609 afterEdit : function(record){
11610 if(this.modified.indexOf(record) == -1){
11611 this.modified.push(record);
11613 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11617 afterReject : function(record){
11618 this.modified.remove(record);
11619 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11623 afterCommit : function(record){
11624 this.modified.remove(record);
11625 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11629 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11630 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11632 commitChanges : function(){
11633 var m = this.modified.slice(0);
11634 this.modified = [];
11635 for(var i = 0, len = m.length; i < len; i++){
11641 * Cancel outstanding changes on all changed records.
11643 rejectChanges : function(){
11644 var m = this.modified.slice(0);
11645 this.modified = [];
11646 for(var i = 0, len = m.length; i < len; i++){
11651 onMetaChange : function(meta, rtype, o){
11652 this.recordType = rtype;
11653 this.fields = rtype.prototype.fields;
11654 delete this.snapshot;
11655 this.sortInfo = meta.sortInfo || this.sortInfo;
11656 this.modified = [];
11657 this.fireEvent('metachange', this, this.reader.meta);
11660 moveIndex : function(data, type)
11662 var index = this.indexOf(data);
11664 var newIndex = index + type;
11668 this.insert(newIndex, data);
11673 * Ext JS Library 1.1.1
11674 * Copyright(c) 2006-2007, Ext JS, LLC.
11676 * Originally Released Under LGPL - original licence link has changed is not relivant.
11679 * <script type="text/javascript">
11683 * @class Roo.data.SimpleStore
11684 * @extends Roo.data.Store
11685 * Small helper class to make creating Stores from Array data easier.
11686 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11687 * @cfg {Array} fields An array of field definition objects, or field name strings.
11688 * @cfg {Array} data The multi-dimensional array of data
11690 * @param {Object} config
11692 Roo.data.SimpleStore = function(config){
11693 Roo.data.SimpleStore.superclass.constructor.call(this, {
11695 reader: new Roo.data.ArrayReader({
11698 Roo.data.Record.create(config.fields)
11700 proxy : new Roo.data.MemoryProxy(config.data)
11704 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11706 * Ext JS Library 1.1.1
11707 * Copyright(c) 2006-2007, Ext JS, LLC.
11709 * Originally Released Under LGPL - original licence link has changed is not relivant.
11712 * <script type="text/javascript">
11717 * @extends Roo.data.Store
11718 * @class Roo.data.JsonStore
11719 * Small helper class to make creating Stores for JSON data easier. <br/>
11721 var store = new Roo.data.JsonStore({
11722 url: 'get-images.php',
11724 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11727 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11728 * JsonReader and HttpProxy (unless inline data is provided).</b>
11729 * @cfg {Array} fields An array of field definition objects, or field name strings.
11731 * @param {Object} config
11733 Roo.data.JsonStore = function(c){
11734 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11735 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11736 reader: new Roo.data.JsonReader(c, c.fields)
11739 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11741 * Ext JS Library 1.1.1
11742 * Copyright(c) 2006-2007, Ext JS, LLC.
11744 * Originally Released Under LGPL - original licence link has changed is not relivant.
11747 * <script type="text/javascript">
11751 Roo.data.Field = function(config){
11752 if(typeof config == "string"){
11753 config = {name: config};
11755 Roo.apply(this, config);
11758 this.type = "auto";
11761 var st = Roo.data.SortTypes;
11762 // named sortTypes are supported, here we look them up
11763 if(typeof this.sortType == "string"){
11764 this.sortType = st[this.sortType];
11767 // set default sortType for strings and dates
11768 if(!this.sortType){
11771 this.sortType = st.asUCString;
11774 this.sortType = st.asDate;
11777 this.sortType = st.none;
11782 var stripRe = /[\$,%]/g;
11784 // prebuilt conversion function for this field, instead of
11785 // switching every time we're reading a value
11787 var cv, dateFormat = this.dateFormat;
11792 cv = function(v){ return v; };
11795 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11799 return v !== undefined && v !== null && v !== '' ?
11800 parseInt(String(v).replace(stripRe, ""), 10) : '';
11805 return v !== undefined && v !== null && v !== '' ?
11806 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11811 cv = function(v){ return v === true || v === "true" || v == 1; };
11818 if(v instanceof Date){
11822 if(dateFormat == "timestamp"){
11823 return new Date(v*1000);
11825 return Date.parseDate(v, dateFormat);
11827 var parsed = Date.parse(v);
11828 return parsed ? new Date(parsed) : null;
11837 Roo.data.Field.prototype = {
11845 * Ext JS Library 1.1.1
11846 * Copyright(c) 2006-2007, Ext JS, LLC.
11848 * Originally Released Under LGPL - original licence link has changed is not relivant.
11851 * <script type="text/javascript">
11854 // Base class for reading structured data from a data source. This class is intended to be
11855 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11858 * @class Roo.data.DataReader
11859 * Base class for reading structured data from a data source. This class is intended to be
11860 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11863 Roo.data.DataReader = function(meta, recordType){
11867 this.recordType = recordType instanceof Array ?
11868 Roo.data.Record.create(recordType) : recordType;
11871 Roo.data.DataReader.prototype = {
11873 * Create an empty record
11874 * @param {Object} data (optional) - overlay some values
11875 * @return {Roo.data.Record} record created.
11877 newRow : function(d) {
11879 this.recordType.prototype.fields.each(function(c) {
11881 case 'int' : da[c.name] = 0; break;
11882 case 'date' : da[c.name] = new Date(); break;
11883 case 'float' : da[c.name] = 0.0; break;
11884 case 'boolean' : da[c.name] = false; break;
11885 default : da[c.name] = ""; break;
11889 return new this.recordType(Roo.apply(da, d));
11894 * Ext JS Library 1.1.1
11895 * Copyright(c) 2006-2007, Ext JS, LLC.
11897 * Originally Released Under LGPL - original licence link has changed is not relivant.
11900 * <script type="text/javascript">
11904 * @class Roo.data.DataProxy
11905 * @extends Roo.data.Observable
11906 * This class is an abstract base class for implementations which provide retrieval of
11907 * unformatted data objects.<br>
11909 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11910 * (of the appropriate type which knows how to parse the data object) to provide a block of
11911 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11913 * Custom implementations must implement the load method as described in
11914 * {@link Roo.data.HttpProxy#load}.
11916 Roo.data.DataProxy = function(){
11919 * @event beforeload
11920 * Fires before a network request is made to retrieve a data object.
11921 * @param {Object} This DataProxy object.
11922 * @param {Object} params The params parameter to the load function.
11927 * Fires before the load method's callback is called.
11928 * @param {Object} This DataProxy object.
11929 * @param {Object} o The data object.
11930 * @param {Object} arg The callback argument object passed to the load function.
11934 * @event loadexception
11935 * Fires if an Exception occurs during data retrieval.
11936 * @param {Object} This DataProxy object.
11937 * @param {Object} o The data object.
11938 * @param {Object} arg The callback argument object passed to the load function.
11939 * @param {Object} e The Exception.
11941 loadexception : true
11943 Roo.data.DataProxy.superclass.constructor.call(this);
11946 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11949 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11953 * Ext JS Library 1.1.1
11954 * Copyright(c) 2006-2007, Ext JS, LLC.
11956 * Originally Released Under LGPL - original licence link has changed is not relivant.
11959 * <script type="text/javascript">
11962 * @class Roo.data.MemoryProxy
11963 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11964 * to the Reader when its load method is called.
11966 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11968 Roo.data.MemoryProxy = function(data){
11972 Roo.data.MemoryProxy.superclass.constructor.call(this);
11976 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11979 * Load data from the requested source (in this case an in-memory
11980 * data object passed to the constructor), read the data object into
11981 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11982 * process that block using the passed callback.
11983 * @param {Object} params This parameter is not used by the MemoryProxy class.
11984 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11985 * object into a block of Roo.data.Records.
11986 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11987 * The function must be passed <ul>
11988 * <li>The Record block object</li>
11989 * <li>The "arg" argument from the load function</li>
11990 * <li>A boolean success indicator</li>
11992 * @param {Object} scope The scope in which to call the callback
11993 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11995 load : function(params, reader, callback, scope, arg){
11996 params = params || {};
11999 result = reader.readRecords(this.data);
12001 this.fireEvent("loadexception", this, arg, null, e);
12002 callback.call(scope, null, arg, false);
12005 callback.call(scope, result, arg, true);
12009 update : function(params, records){
12014 * Ext JS Library 1.1.1
12015 * Copyright(c) 2006-2007, Ext JS, LLC.
12017 * Originally Released Under LGPL - original licence link has changed is not relivant.
12020 * <script type="text/javascript">
12023 * @class Roo.data.HttpProxy
12024 * @extends Roo.data.DataProxy
12025 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12026 * configured to reference a certain URL.<br><br>
12028 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12029 * from which the running page was served.<br><br>
12031 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12033 * Be aware that to enable the browser to parse an XML document, the server must set
12034 * the Content-Type header in the HTTP response to "text/xml".
12036 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12037 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12038 * will be used to make the request.
12040 Roo.data.HttpProxy = function(conn){
12041 Roo.data.HttpProxy.superclass.constructor.call(this);
12042 // is conn a conn config or a real conn?
12044 this.useAjax = !conn || !conn.events;
12048 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12049 // thse are take from connection...
12052 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12055 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12056 * extra parameters to each request made by this object. (defaults to undefined)
12059 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12060 * to each request made by this object. (defaults to undefined)
12063 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12066 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12069 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12075 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12079 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12080 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12081 * a finer-grained basis than the DataProxy events.
12083 getConnection : function(){
12084 return this.useAjax ? Roo.Ajax : this.conn;
12088 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12089 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12090 * process that block using the passed callback.
12091 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12092 * for the request to the remote server.
12093 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12094 * object into a block of Roo.data.Records.
12095 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12096 * The function must be passed <ul>
12097 * <li>The Record block object</li>
12098 * <li>The "arg" argument from the load function</li>
12099 * <li>A boolean success indicator</li>
12101 * @param {Object} scope The scope in which to call the callback
12102 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12104 load : function(params, reader, callback, scope, arg){
12105 if(this.fireEvent("beforeload", this, params) !== false){
12107 params : params || {},
12109 callback : callback,
12114 callback : this.loadResponse,
12118 Roo.applyIf(o, this.conn);
12119 if(this.activeRequest){
12120 Roo.Ajax.abort(this.activeRequest);
12122 this.activeRequest = Roo.Ajax.request(o);
12124 this.conn.request(o);
12127 callback.call(scope||this, null, arg, false);
12132 loadResponse : function(o, success, response){
12133 delete this.activeRequest;
12135 this.fireEvent("loadexception", this, o, response);
12136 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12141 result = o.reader.read(response);
12143 this.fireEvent("loadexception", this, o, response, e);
12144 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12148 this.fireEvent("load", this, o, o.request.arg);
12149 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12153 update : function(dataSet){
12158 updateResponse : function(dataSet){
12163 * Ext JS Library 1.1.1
12164 * Copyright(c) 2006-2007, Ext JS, LLC.
12166 * Originally Released Under LGPL - original licence link has changed is not relivant.
12169 * <script type="text/javascript">
12173 * @class Roo.data.ScriptTagProxy
12174 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12175 * other than the originating domain of the running page.<br><br>
12177 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12178 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12180 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12181 * source code that is used as the source inside a <script> tag.<br><br>
12183 * In order for the browser to process the returned data, the server must wrap the data object
12184 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12185 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12186 * depending on whether the callback name was passed:
12189 boolean scriptTag = false;
12190 String cb = request.getParameter("callback");
12193 response.setContentType("text/javascript");
12195 response.setContentType("application/x-json");
12197 Writer out = response.getWriter();
12199 out.write(cb + "(");
12201 out.print(dataBlock.toJsonString());
12208 * @param {Object} config A configuration object.
12210 Roo.data.ScriptTagProxy = function(config){
12211 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12212 Roo.apply(this, config);
12213 this.head = document.getElementsByTagName("head")[0];
12216 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12218 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12220 * @cfg {String} url The URL from which to request the data object.
12223 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12227 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12228 * the server the name of the callback function set up by the load call to process the returned data object.
12229 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12230 * javascript output which calls this named function passing the data object as its only parameter.
12232 callbackParam : "callback",
12234 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12235 * name to the request.
12240 * Load data from the configured URL, read the data object into
12241 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12242 * process that block using the passed callback.
12243 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12244 * for the request to the remote server.
12245 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12246 * object into a block of Roo.data.Records.
12247 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12248 * The function must be passed <ul>
12249 * <li>The Record block object</li>
12250 * <li>The "arg" argument from the load function</li>
12251 * <li>A boolean success indicator</li>
12253 * @param {Object} scope The scope in which to call the callback
12254 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12256 load : function(params, reader, callback, scope, arg){
12257 if(this.fireEvent("beforeload", this, params) !== false){
12259 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12261 var url = this.url;
12262 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12264 url += "&_dc=" + (new Date().getTime());
12266 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12269 cb : "stcCallback"+transId,
12270 scriptId : "stcScript"+transId,
12274 callback : callback,
12280 window[trans.cb] = function(o){
12281 conn.handleResponse(o, trans);
12284 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12286 if(this.autoAbort !== false){
12290 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12292 var script = document.createElement("script");
12293 script.setAttribute("src", url);
12294 script.setAttribute("type", "text/javascript");
12295 script.setAttribute("id", trans.scriptId);
12296 this.head.appendChild(script);
12298 this.trans = trans;
12300 callback.call(scope||this, null, arg, false);
12305 isLoading : function(){
12306 return this.trans ? true : false;
12310 * Abort the current server request.
12312 abort : function(){
12313 if(this.isLoading()){
12314 this.destroyTrans(this.trans);
12319 destroyTrans : function(trans, isLoaded){
12320 this.head.removeChild(document.getElementById(trans.scriptId));
12321 clearTimeout(trans.timeoutId);
12323 window[trans.cb] = undefined;
12325 delete window[trans.cb];
12328 // if hasn't been loaded, wait for load to remove it to prevent script error
12329 window[trans.cb] = function(){
12330 window[trans.cb] = undefined;
12332 delete window[trans.cb];
12339 handleResponse : function(o, trans){
12340 this.trans = false;
12341 this.destroyTrans(trans, true);
12344 result = trans.reader.readRecords(o);
12346 this.fireEvent("loadexception", this, o, trans.arg, e);
12347 trans.callback.call(trans.scope||window, null, trans.arg, false);
12350 this.fireEvent("load", this, o, trans.arg);
12351 trans.callback.call(trans.scope||window, result, trans.arg, true);
12355 handleFailure : function(trans){
12356 this.trans = false;
12357 this.destroyTrans(trans, false);
12358 this.fireEvent("loadexception", this, null, trans.arg);
12359 trans.callback.call(trans.scope||window, null, trans.arg, false);
12363 * Ext JS Library 1.1.1
12364 * Copyright(c) 2006-2007, Ext JS, LLC.
12366 * Originally Released Under LGPL - original licence link has changed is not relivant.
12369 * <script type="text/javascript">
12373 * @class Roo.data.JsonReader
12374 * @extends Roo.data.DataReader
12375 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12376 * based on mappings in a provided Roo.data.Record constructor.
12378 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12379 * in the reply previously.
12384 var RecordDef = Roo.data.Record.create([
12385 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12386 {name: 'occupation'} // This field will use "occupation" as the mapping.
12388 var myReader = new Roo.data.JsonReader({
12389 totalProperty: "results", // The property which contains the total dataset size (optional)
12390 root: "rows", // The property which contains an Array of row objects
12391 id: "id" // The property within each row object that provides an ID for the record (optional)
12395 * This would consume a JSON file like this:
12397 { 'results': 2, 'rows': [
12398 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12399 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12402 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12403 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12404 * paged from the remote server.
12405 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12406 * @cfg {String} root name of the property which contains the Array of row objects.
12407 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12408 * @cfg {Array} fields Array of field definition objects
12410 * Create a new JsonReader
12411 * @param {Object} meta Metadata configuration options
12412 * @param {Object} recordType Either an Array of field definition objects,
12413 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12415 Roo.data.JsonReader = function(meta, recordType){
12418 // set some defaults:
12419 Roo.applyIf(meta, {
12420 totalProperty: 'total',
12421 successProperty : 'success',
12426 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12428 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12431 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12432 * Used by Store query builder to append _requestMeta to params.
12435 metaFromRemote : false,
12437 * This method is only used by a DataProxy which has retrieved data from a remote server.
12438 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12439 * @return {Object} data A data block which is used by an Roo.data.Store object as
12440 * a cache of Roo.data.Records.
12442 read : function(response){
12443 var json = response.responseText;
12445 var o = /* eval:var:o */ eval("("+json+")");
12447 throw {message: "JsonReader.read: Json object not found"};
12453 this.metaFromRemote = true;
12454 this.meta = o.metaData;
12455 this.recordType = Roo.data.Record.create(o.metaData.fields);
12456 this.onMetaChange(this.meta, this.recordType, o);
12458 return this.readRecords(o);
12461 // private function a store will implement
12462 onMetaChange : function(meta, recordType, o){
12469 simpleAccess: function(obj, subsc) {
12476 getJsonAccessor: function(){
12478 return function(expr) {
12480 return(re.test(expr))
12481 ? new Function("obj", "return obj." + expr)
12486 return Roo.emptyFn;
12491 * Create a data block containing Roo.data.Records from an XML document.
12492 * @param {Object} o An object which contains an Array of row objects in the property specified
12493 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12494 * which contains the total size of the dataset.
12495 * @return {Object} data A data block which is used by an Roo.data.Store object as
12496 * a cache of Roo.data.Records.
12498 readRecords : function(o){
12500 * After any data loads, the raw JSON data is available for further custom processing.
12504 var s = this.meta, Record = this.recordType,
12505 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12507 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12509 if(s.totalProperty) {
12510 this.getTotal = this.getJsonAccessor(s.totalProperty);
12512 if(s.successProperty) {
12513 this.getSuccess = this.getJsonAccessor(s.successProperty);
12515 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12517 var g = this.getJsonAccessor(s.id);
12518 this.getId = function(rec) {
12520 return (r === undefined || r === "") ? null : r;
12523 this.getId = function(){return null;};
12526 for(var jj = 0; jj < fl; jj++){
12528 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12529 this.ef[jj] = this.getJsonAccessor(map);
12533 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12534 if(s.totalProperty){
12535 var vt = parseInt(this.getTotal(o), 10);
12540 if(s.successProperty){
12541 var vs = this.getSuccess(o);
12542 if(vs === false || vs === 'false'){
12547 for(var i = 0; i < c; i++){
12550 var id = this.getId(n);
12551 for(var j = 0; j < fl; j++){
12553 var v = this.ef[j](n);
12555 Roo.log('missing convert for ' + f.name);
12559 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12561 var record = new Record(values, id);
12563 records[i] = record;
12569 totalRecords : totalRecords
12574 * Ext JS Library 1.1.1
12575 * Copyright(c) 2006-2007, Ext JS, LLC.
12577 * Originally Released Under LGPL - original licence link has changed is not relivant.
12580 * <script type="text/javascript">
12584 * @class Roo.data.ArrayReader
12585 * @extends Roo.data.DataReader
12586 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12587 * Each element of that Array represents a row of data fields. The
12588 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12589 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12593 var RecordDef = Roo.data.Record.create([
12594 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12595 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12597 var myReader = new Roo.data.ArrayReader({
12598 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12602 * This would consume an Array like this:
12604 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12606 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12608 * Create a new JsonReader
12609 * @param {Object} meta Metadata configuration options.
12610 * @param {Object} recordType Either an Array of field definition objects
12611 * as specified to {@link Roo.data.Record#create},
12612 * or an {@link Roo.data.Record} object
12613 * created using {@link Roo.data.Record#create}.
12615 Roo.data.ArrayReader = function(meta, recordType){
12616 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12619 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12621 * Create a data block containing Roo.data.Records from an XML document.
12622 * @param {Object} o An Array of row objects which represents the dataset.
12623 * @return {Object} data A data block which is used by an Roo.data.Store object as
12624 * a cache of Roo.data.Records.
12626 readRecords : function(o){
12627 var sid = this.meta ? this.meta.id : null;
12628 var recordType = this.recordType, fields = recordType.prototype.fields;
12631 for(var i = 0; i < root.length; i++){
12634 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12635 for(var j = 0, jlen = fields.length; j < jlen; j++){
12636 var f = fields.items[j];
12637 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12638 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12640 values[f.name] = v;
12642 var record = new recordType(values, id);
12644 records[records.length] = record;
12648 totalRecords : records.length
12657 * @class Roo.bootstrap.ComboBox
12658 * @extends Roo.bootstrap.TriggerField
12659 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12660 * @cfg {Boolean} append (true|false) default false
12661 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12662 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12663 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12664 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12665 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12666 * @cfg {Boolean} animate default true
12667 * @cfg {Boolean} emptyResultText only for touch device
12668 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12669 * @cfg {String} emptyTitle default ''
12671 * Create a new ComboBox.
12672 * @param {Object} config Configuration options
12674 Roo.bootstrap.ComboBox = function(config){
12675 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12679 * Fires when the dropdown list is expanded
12680 * @param {Roo.bootstrap.ComboBox} combo This combo box
12685 * Fires when the dropdown list is collapsed
12686 * @param {Roo.bootstrap.ComboBox} combo This combo box
12690 * @event beforeselect
12691 * Fires before a list item is selected. Return false to cancel the selection.
12692 * @param {Roo.bootstrap.ComboBox} combo This combo box
12693 * @param {Roo.data.Record} record The data record returned from the underlying store
12694 * @param {Number} index The index of the selected item in the dropdown list
12696 'beforeselect' : true,
12699 * Fires when a list item is selected
12700 * @param {Roo.bootstrap.ComboBox} combo This combo box
12701 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12702 * @param {Number} index The index of the selected item in the dropdown list
12706 * @event beforequery
12707 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12708 * The event object passed has these properties:
12709 * @param {Roo.bootstrap.ComboBox} combo This combo box
12710 * @param {String} query The query
12711 * @param {Boolean} forceAll true to force "all" query
12712 * @param {Boolean} cancel true to cancel the query
12713 * @param {Object} e The query event object
12715 'beforequery': true,
12718 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12719 * @param {Roo.bootstrap.ComboBox} combo This combo box
12724 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12725 * @param {Roo.bootstrap.ComboBox} combo This combo box
12726 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12731 * Fires when the remove value from the combobox array
12732 * @param {Roo.bootstrap.ComboBox} combo This combo box
12736 * @event afterremove
12737 * Fires when the remove value from the combobox array
12738 * @param {Roo.bootstrap.ComboBox} combo This combo box
12740 'afterremove' : true,
12742 * @event specialfilter
12743 * Fires when specialfilter
12744 * @param {Roo.bootstrap.ComboBox} combo This combo box
12746 'specialfilter' : true,
12749 * Fires when tick the element
12750 * @param {Roo.bootstrap.ComboBox} combo This combo box
12754 * @event touchviewdisplay
12755 * Fires when touch view require special display (default is using displayField)
12756 * @param {Roo.bootstrap.ComboBox} combo This combo box
12757 * @param {Object} cfg set html .
12759 'touchviewdisplay' : true
12764 this.tickItems = [];
12766 this.selectedIndex = -1;
12767 if(this.mode == 'local'){
12768 if(config.queryDelay === undefined){
12769 this.queryDelay = 10;
12771 if(config.minChars === undefined){
12777 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12780 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12781 * rendering into an Roo.Editor, defaults to false)
12784 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12785 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12788 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12791 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12792 * the dropdown list (defaults to undefined, with no header element)
12796 * @cfg {String/Roo.Template} tpl The template to use to render the output
12800 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12802 listWidth: undefined,
12804 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12805 * mode = 'remote' or 'text' if mode = 'local')
12807 displayField: undefined,
12810 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12811 * mode = 'remote' or 'value' if mode = 'local').
12812 * Note: use of a valueField requires the user make a selection
12813 * in order for a value to be mapped.
12815 valueField: undefined,
12817 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12822 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12823 * field's data value (defaults to the underlying DOM element's name)
12825 hiddenName: undefined,
12827 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12831 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12833 selectedClass: 'active',
12836 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12840 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12841 * anchor positions (defaults to 'tl-bl')
12843 listAlign: 'tl-bl?',
12845 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12849 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12850 * query specified by the allQuery config option (defaults to 'query')
12852 triggerAction: 'query',
12854 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12855 * (defaults to 4, does not apply if editable = false)
12859 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12860 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12864 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12865 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12869 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12870 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12874 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12875 * when editable = true (defaults to false)
12877 selectOnFocus:false,
12879 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12881 queryParam: 'query',
12883 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12884 * when mode = 'remote' (defaults to 'Loading...')
12886 loadingText: 'Loading...',
12888 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12892 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12896 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12897 * traditional select (defaults to true)
12901 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12905 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12909 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12910 * listWidth has a higher value)
12914 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12915 * allow the user to set arbitrary text into the field (defaults to false)
12917 forceSelection:false,
12919 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12920 * if typeAhead = true (defaults to 250)
12922 typeAheadDelay : 250,
12924 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12925 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12927 valueNotFoundText : undefined,
12929 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12931 blockFocus : false,
12934 * @cfg {Boolean} disableClear Disable showing of clear button.
12936 disableClear : false,
12938 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12940 alwaysQuery : false,
12943 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12948 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12950 invalidClass : "has-warning",
12953 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12955 validClass : "has-success",
12958 * @cfg {Boolean} specialFilter (true|false) special filter default false
12960 specialFilter : false,
12963 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12965 mobileTouchView : true,
12968 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12970 useNativeIOS : false,
12972 ios_options : false,
12984 btnPosition : 'right',
12985 triggerList : true,
12986 showToggleBtn : true,
12988 emptyResultText: 'Empty',
12989 triggerText : 'Select',
12992 // element that contains real text value.. (when hidden is used..)
12994 getAutoCreate : function()
12999 * Render classic select for iso
13002 if(Roo.isIOS && this.useNativeIOS){
13003 cfg = this.getAutoCreateNativeIOS();
13011 if(Roo.isTouch && this.mobileTouchView){
13012 cfg = this.getAutoCreateTouchView();
13019 if(!this.tickable){
13020 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13025 * ComboBox with tickable selections
13028 var align = this.labelAlign || this.parentLabelAlign();
13031 cls : 'form-group roo-combobox-tickable' //input-group
13034 var btn_text_select = '';
13035 var btn_text_done = '';
13036 var btn_text_cancel = '';
13038 if (this.btn_text_show) {
13039 btn_text_select = 'Select';
13040 btn_text_done = 'Done';
13041 btn_text_cancel = 'Cancel';
13046 cls : 'tickable-buttons',
13051 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13052 //html : this.triggerText
13053 html: btn_text_select
13059 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13061 html: btn_text_done
13067 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13069 html: btn_text_cancel
13075 buttons.cn.unshift({
13077 cls: 'roo-select2-search-field-input'
13083 Roo.each(buttons.cn, function(c){
13085 c.cls += ' btn-' + _this.size;
13088 if (_this.disabled) {
13099 cls: 'form-hidden-field'
13103 cls: 'roo-select2-choices',
13107 cls: 'roo-select2-search-field',
13118 cls: 'roo-select2-container input-group roo-select2-container-multi',
13123 // cls: 'typeahead typeahead-long dropdown-menu',
13124 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13129 if(this.hasFeedback && !this.allowBlank){
13133 cls: 'glyphicon form-control-feedback'
13136 combobox.cn.push(feedback);
13140 if (align ==='left' && this.fieldLabel.length) {
13142 cfg.cls += ' roo-form-group-label-left';
13147 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13148 tooltip : 'This field is required'
13153 cls : 'control-label',
13154 html : this.fieldLabel
13166 var labelCfg = cfg.cn[1];
13167 var contentCfg = cfg.cn[2];
13170 if(this.indicatorpos == 'right'){
13176 cls : 'control-label',
13180 html : this.fieldLabel
13184 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13185 tooltip : 'This field is required'
13200 labelCfg = cfg.cn[0];
13201 contentCfg = cfg.cn[1];
13205 if(this.labelWidth > 12){
13206 labelCfg.style = "width: " + this.labelWidth + 'px';
13209 if(this.labelWidth < 13 && this.labelmd == 0){
13210 this.labelmd = this.labelWidth;
13213 if(this.labellg > 0){
13214 labelCfg.cls += ' col-lg-' + this.labellg;
13215 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13218 if(this.labelmd > 0){
13219 labelCfg.cls += ' col-md-' + this.labelmd;
13220 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13223 if(this.labelsm > 0){
13224 labelCfg.cls += ' col-sm-' + this.labelsm;
13225 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13228 if(this.labelxs > 0){
13229 labelCfg.cls += ' col-xs-' + this.labelxs;
13230 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13234 } else if ( this.fieldLabel.length) {
13235 // Roo.log(" label");
13239 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13240 tooltip : 'This field is required'
13244 //cls : 'input-group-addon',
13245 html : this.fieldLabel
13250 if(this.indicatorpos == 'right'){
13254 //cls : 'input-group-addon',
13255 html : this.fieldLabel
13259 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13260 tooltip : 'This field is required'
13269 // Roo.log(" no label && no align");
13276 ['xs','sm','md','lg'].map(function(size){
13277 if (settings[size]) {
13278 cfg.cls += ' col-' + size + '-' + settings[size];
13286 _initEventsCalled : false,
13289 initEvents: function()
13291 if (this._initEventsCalled) { // as we call render... prevent looping...
13294 this._initEventsCalled = true;
13297 throw "can not find store for combo";
13300 this.indicator = this.indicatorEl();
13302 this.store = Roo.factory(this.store, Roo.data);
13303 this.store.parent = this;
13305 // if we are building from html. then this element is so complex, that we can not really
13306 // use the rendered HTML.
13307 // so we have to trash and replace the previous code.
13308 if (Roo.XComponent.build_from_html) {
13309 // remove this element....
13310 var e = this.el.dom, k=0;
13311 while (e ) { e = e.previousSibling; ++k;}
13316 this.rendered = false;
13318 this.render(this.parent().getChildContainer(true), k);
13321 if(Roo.isIOS && this.useNativeIOS){
13322 this.initIOSView();
13330 if(Roo.isTouch && this.mobileTouchView){
13331 this.initTouchView();
13336 this.initTickableEvents();
13340 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13342 if(this.hiddenName){
13344 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13346 this.hiddenField.dom.value =
13347 this.hiddenValue !== undefined ? this.hiddenValue :
13348 this.value !== undefined ? this.value : '';
13350 // prevent input submission
13351 this.el.dom.removeAttribute('name');
13352 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13357 // this.el.dom.setAttribute('autocomplete', 'off');
13360 var cls = 'x-combo-list';
13362 //this.list = new Roo.Layer({
13363 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13369 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13370 _this.list.setWidth(lw);
13373 this.list.on('mouseover', this.onViewOver, this);
13374 this.list.on('mousemove', this.onViewMove, this);
13375 this.list.on('scroll', this.onViewScroll, this);
13378 this.list.swallowEvent('mousewheel');
13379 this.assetHeight = 0;
13382 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13383 this.assetHeight += this.header.getHeight();
13386 this.innerList = this.list.createChild({cls:cls+'-inner'});
13387 this.innerList.on('mouseover', this.onViewOver, this);
13388 this.innerList.on('mousemove', this.onViewMove, this);
13389 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13391 if(this.allowBlank && !this.pageSize && !this.disableClear){
13392 this.footer = this.list.createChild({cls:cls+'-ft'});
13393 this.pageTb = new Roo.Toolbar(this.footer);
13397 this.footer = this.list.createChild({cls:cls+'-ft'});
13398 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13399 {pageSize: this.pageSize});
13403 if (this.pageTb && this.allowBlank && !this.disableClear) {
13405 this.pageTb.add(new Roo.Toolbar.Fill(), {
13406 cls: 'x-btn-icon x-btn-clear',
13408 handler: function()
13411 _this.clearValue();
13412 _this.onSelect(false, -1);
13417 this.assetHeight += this.footer.getHeight();
13422 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13425 this.view = new Roo.View(this.list, this.tpl, {
13426 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13428 //this.view.wrapEl.setDisplayed(false);
13429 this.view.on('click', this.onViewClick, this);
13432 this.store.on('beforeload', this.onBeforeLoad, this);
13433 this.store.on('load', this.onLoad, this);
13434 this.store.on('loadexception', this.onLoadException, this);
13436 if(this.resizable){
13437 this.resizer = new Roo.Resizable(this.list, {
13438 pinned:true, handles:'se'
13440 this.resizer.on('resize', function(r, w, h){
13441 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13442 this.listWidth = w;
13443 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13444 this.restrictHeight();
13446 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13449 if(!this.editable){
13450 this.editable = true;
13451 this.setEditable(false);
13456 if (typeof(this.events.add.listeners) != 'undefined') {
13458 this.addicon = this.wrap.createChild(
13459 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13461 this.addicon.on('click', function(e) {
13462 this.fireEvent('add', this);
13465 if (typeof(this.events.edit.listeners) != 'undefined') {
13467 this.editicon = this.wrap.createChild(
13468 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13469 if (this.addicon) {
13470 this.editicon.setStyle('margin-left', '40px');
13472 this.editicon.on('click', function(e) {
13474 // we fire even if inothing is selected..
13475 this.fireEvent('edit', this, this.lastData );
13481 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13482 "up" : function(e){
13483 this.inKeyMode = true;
13487 "down" : function(e){
13488 if(!this.isExpanded()){
13489 this.onTriggerClick();
13491 this.inKeyMode = true;
13496 "enter" : function(e){
13497 // this.onViewClick();
13501 if(this.fireEvent("specialkey", this, e)){
13502 this.onViewClick(false);
13508 "esc" : function(e){
13512 "tab" : function(e){
13515 if(this.fireEvent("specialkey", this, e)){
13516 this.onViewClick(false);
13524 doRelay : function(foo, bar, hname){
13525 if(hname == 'down' || this.scope.isExpanded()){
13526 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13535 this.queryDelay = Math.max(this.queryDelay || 10,
13536 this.mode == 'local' ? 10 : 250);
13539 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13541 if(this.typeAhead){
13542 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13544 if(this.editable !== false){
13545 this.inputEl().on("keyup", this.onKeyUp, this);
13547 if(this.forceSelection){
13548 this.inputEl().on('blur', this.doForce, this);
13552 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13553 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13557 initTickableEvents: function()
13561 if(this.hiddenName){
13563 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13565 this.hiddenField.dom.value =
13566 this.hiddenValue !== undefined ? this.hiddenValue :
13567 this.value !== undefined ? this.value : '';
13569 // prevent input submission
13570 this.el.dom.removeAttribute('name');
13571 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13576 // this.list = this.el.select('ul.dropdown-menu',true).first();
13578 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13579 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13580 if(this.triggerList){
13581 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13584 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13585 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13587 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13588 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13590 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13591 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13593 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13594 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13595 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13598 this.cancelBtn.hide();
13603 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13604 _this.list.setWidth(lw);
13607 this.list.on('mouseover', this.onViewOver, this);
13608 this.list.on('mousemove', this.onViewMove, this);
13610 this.list.on('scroll', this.onViewScroll, this);
13613 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13614 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13617 this.view = new Roo.View(this.list, this.tpl, {
13622 selectedClass: this.selectedClass
13625 //this.view.wrapEl.setDisplayed(false);
13626 this.view.on('click', this.onViewClick, this);
13630 this.store.on('beforeload', this.onBeforeLoad, this);
13631 this.store.on('load', this.onLoad, this);
13632 this.store.on('loadexception', this.onLoadException, this);
13635 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13636 "up" : function(e){
13637 this.inKeyMode = true;
13641 "down" : function(e){
13642 this.inKeyMode = true;
13646 "enter" : function(e){
13647 if(this.fireEvent("specialkey", this, e)){
13648 this.onViewClick(false);
13654 "esc" : function(e){
13655 this.onTickableFooterButtonClick(e, false, false);
13658 "tab" : function(e){
13659 this.fireEvent("specialkey", this, e);
13661 this.onTickableFooterButtonClick(e, false, false);
13668 doRelay : function(e, fn, key){
13669 if(this.scope.isExpanded()){
13670 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13679 this.queryDelay = Math.max(this.queryDelay || 10,
13680 this.mode == 'local' ? 10 : 250);
13683 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13685 if(this.typeAhead){
13686 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13689 if(this.editable !== false){
13690 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13693 this.indicator = this.indicatorEl();
13695 if(this.indicator){
13696 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13697 this.indicator.hide();
13702 onDestroy : function(){
13704 this.view.setStore(null);
13705 this.view.el.removeAllListeners();
13706 this.view.el.remove();
13707 this.view.purgeListeners();
13710 this.list.dom.innerHTML = '';
13714 this.store.un('beforeload', this.onBeforeLoad, this);
13715 this.store.un('load', this.onLoad, this);
13716 this.store.un('loadexception', this.onLoadException, this);
13718 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13722 fireKey : function(e){
13723 if(e.isNavKeyPress() && !this.list.isVisible()){
13724 this.fireEvent("specialkey", this, e);
13729 onResize: function(w, h){
13730 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13732 // if(typeof w != 'number'){
13733 // // we do not handle it!?!?
13736 // var tw = this.trigger.getWidth();
13737 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13738 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13740 // this.inputEl().setWidth( this.adjustWidth('input', x));
13742 // //this.trigger.setStyle('left', x+'px');
13744 // if(this.list && this.listWidth === undefined){
13745 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13746 // this.list.setWidth(lw);
13747 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13755 * Allow or prevent the user from directly editing the field text. If false is passed,
13756 * the user will only be able to select from the items defined in the dropdown list. This method
13757 * is the runtime equivalent of setting the 'editable' config option at config time.
13758 * @param {Boolean} value True to allow the user to directly edit the field text
13760 setEditable : function(value){
13761 if(value == this.editable){
13764 this.editable = value;
13766 this.inputEl().dom.setAttribute('readOnly', true);
13767 this.inputEl().on('mousedown', this.onTriggerClick, this);
13768 this.inputEl().addClass('x-combo-noedit');
13770 this.inputEl().dom.setAttribute('readOnly', false);
13771 this.inputEl().un('mousedown', this.onTriggerClick, this);
13772 this.inputEl().removeClass('x-combo-noedit');
13778 onBeforeLoad : function(combo,opts){
13779 if(!this.hasFocus){
13783 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13785 this.restrictHeight();
13786 this.selectedIndex = -1;
13790 onLoad : function(){
13792 this.hasQuery = false;
13794 if(!this.hasFocus){
13798 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13799 this.loading.hide();
13802 if(this.store.getCount() > 0){
13805 this.restrictHeight();
13806 if(this.lastQuery == this.allQuery){
13807 if(this.editable && !this.tickable){
13808 this.inputEl().dom.select();
13812 !this.selectByValue(this.value, true) &&
13815 !this.store.lastOptions ||
13816 typeof(this.store.lastOptions.add) == 'undefined' ||
13817 this.store.lastOptions.add != true
13820 this.select(0, true);
13823 if(this.autoFocus){
13826 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13827 this.taTask.delay(this.typeAheadDelay);
13831 this.onEmptyResults();
13837 onLoadException : function()
13839 this.hasQuery = false;
13841 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13842 this.loading.hide();
13845 if(this.tickable && this.editable){
13850 // only causes errors at present
13851 //Roo.log(this.store.reader.jsonData);
13852 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13854 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13860 onTypeAhead : function(){
13861 if(this.store.getCount() > 0){
13862 var r = this.store.getAt(0);
13863 var newValue = r.data[this.displayField];
13864 var len = newValue.length;
13865 var selStart = this.getRawValue().length;
13867 if(selStart != len){
13868 this.setRawValue(newValue);
13869 this.selectText(selStart, newValue.length);
13875 onSelect : function(record, index){
13877 if(this.fireEvent('beforeselect', this, record, index) !== false){
13879 this.setFromData(index > -1 ? record.data : false);
13882 this.fireEvent('select', this, record, index);
13887 * Returns the currently selected field value or empty string if no value is set.
13888 * @return {String} value The selected value
13890 getValue : function()
13892 if(Roo.isIOS && this.useNativeIOS){
13893 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13897 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13900 if(this.valueField){
13901 return typeof this.value != 'undefined' ? this.value : '';
13903 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13907 getRawValue : function()
13909 if(Roo.isIOS && this.useNativeIOS){
13910 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13913 var v = this.inputEl().getValue();
13919 * Clears any text/value currently set in the field
13921 clearValue : function(){
13923 if(this.hiddenField){
13924 this.hiddenField.dom.value = '';
13927 this.setRawValue('');
13928 this.lastSelectionText = '';
13929 this.lastData = false;
13931 var close = this.closeTriggerEl();
13942 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13943 * will be displayed in the field. If the value does not match the data value of an existing item,
13944 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13945 * Otherwise the field will be blank (although the value will still be set).
13946 * @param {String} value The value to match
13948 setValue : function(v)
13950 if(Roo.isIOS && this.useNativeIOS){
13951 this.setIOSValue(v);
13961 if(this.valueField){
13962 var r = this.findRecord(this.valueField, v);
13964 text = r.data[this.displayField];
13965 }else if(this.valueNotFoundText !== undefined){
13966 text = this.valueNotFoundText;
13969 this.lastSelectionText = text;
13970 if(this.hiddenField){
13971 this.hiddenField.dom.value = v;
13973 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13976 var close = this.closeTriggerEl();
13979 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13985 * @property {Object} the last set data for the element
13990 * Sets the value of the field based on a object which is related to the record format for the store.
13991 * @param {Object} value the value to set as. or false on reset?
13993 setFromData : function(o){
14000 var dv = ''; // display value
14001 var vv = ''; // value value..
14003 if (this.displayField) {
14004 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14006 // this is an error condition!!!
14007 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14010 if(this.valueField){
14011 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14014 var close = this.closeTriggerEl();
14017 if(dv.length || vv * 1 > 0){
14019 this.blockFocus=true;
14025 if(this.hiddenField){
14026 this.hiddenField.dom.value = vv;
14028 this.lastSelectionText = dv;
14029 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14033 // no hidden field.. - we store the value in 'value', but still display
14034 // display field!!!!
14035 this.lastSelectionText = dv;
14036 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14043 reset : function(){
14044 // overridden so that last data is reset..
14051 this.setValue(this.originalValue);
14052 //this.clearInvalid();
14053 this.lastData = false;
14055 this.view.clearSelections();
14061 findRecord : function(prop, value){
14063 if(this.store.getCount() > 0){
14064 this.store.each(function(r){
14065 if(r.data[prop] == value){
14075 getName: function()
14077 // returns hidden if it's set..
14078 if (!this.rendered) {return ''};
14079 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14083 onViewMove : function(e, t){
14084 this.inKeyMode = false;
14088 onViewOver : function(e, t){
14089 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14092 var item = this.view.findItemFromChild(t);
14095 var index = this.view.indexOf(item);
14096 this.select(index, false);
14101 onViewClick : function(view, doFocus, el, e)
14103 var index = this.view.getSelectedIndexes()[0];
14105 var r = this.store.getAt(index);
14109 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14116 Roo.each(this.tickItems, function(v,k){
14118 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14120 _this.tickItems.splice(k, 1);
14122 if(typeof(e) == 'undefined' && view == false){
14123 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14135 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14136 this.tickItems.push(r.data);
14139 if(typeof(e) == 'undefined' && view == false){
14140 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14147 this.onSelect(r, index);
14149 if(doFocus !== false && !this.blockFocus){
14150 this.inputEl().focus();
14155 restrictHeight : function(){
14156 //this.innerList.dom.style.height = '';
14157 //var inner = this.innerList.dom;
14158 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14159 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14160 //this.list.beginUpdate();
14161 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14162 this.list.alignTo(this.inputEl(), this.listAlign);
14163 this.list.alignTo(this.inputEl(), this.listAlign);
14164 //this.list.endUpdate();
14168 onEmptyResults : function(){
14170 if(this.tickable && this.editable){
14171 this.hasFocus = false;
14172 this.restrictHeight();
14180 * Returns true if the dropdown list is expanded, else false.
14182 isExpanded : function(){
14183 return this.list.isVisible();
14187 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14188 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14189 * @param {String} value The data value of the item to select
14190 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14191 * selected item if it is not currently in view (defaults to true)
14192 * @return {Boolean} True if the value matched an item in the list, else false
14194 selectByValue : function(v, scrollIntoView){
14195 if(v !== undefined && v !== null){
14196 var r = this.findRecord(this.valueField || this.displayField, v);
14198 this.select(this.store.indexOf(r), scrollIntoView);
14206 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14207 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14208 * @param {Number} index The zero-based index of the list item to select
14209 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14210 * selected item if it is not currently in view (defaults to true)
14212 select : function(index, scrollIntoView){
14213 this.selectedIndex = index;
14214 this.view.select(index);
14215 if(scrollIntoView !== false){
14216 var el = this.view.getNode(index);
14218 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14221 this.list.scrollChildIntoView(el, false);
14227 selectNext : function(){
14228 var ct = this.store.getCount();
14230 if(this.selectedIndex == -1){
14232 }else if(this.selectedIndex < ct-1){
14233 this.select(this.selectedIndex+1);
14239 selectPrev : function(){
14240 var ct = this.store.getCount();
14242 if(this.selectedIndex == -1){
14244 }else if(this.selectedIndex != 0){
14245 this.select(this.selectedIndex-1);
14251 onKeyUp : function(e){
14252 if(this.editable !== false && !e.isSpecialKey()){
14253 this.lastKey = e.getKey();
14254 this.dqTask.delay(this.queryDelay);
14259 validateBlur : function(){
14260 return !this.list || !this.list.isVisible();
14264 initQuery : function(){
14266 var v = this.getRawValue();
14268 if(this.tickable && this.editable){
14269 v = this.tickableInputEl().getValue();
14276 doForce : function(){
14277 if(this.inputEl().dom.value.length > 0){
14278 this.inputEl().dom.value =
14279 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14285 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14286 * query allowing the query action to be canceled if needed.
14287 * @param {String} query The SQL query to execute
14288 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14289 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14290 * saved in the current store (defaults to false)
14292 doQuery : function(q, forceAll){
14294 if(q === undefined || q === null){
14299 forceAll: forceAll,
14303 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14308 forceAll = qe.forceAll;
14309 if(forceAll === true || (q.length >= this.minChars)){
14311 this.hasQuery = true;
14313 if(this.lastQuery != q || this.alwaysQuery){
14314 this.lastQuery = q;
14315 if(this.mode == 'local'){
14316 this.selectedIndex = -1;
14318 this.store.clearFilter();
14321 if(this.specialFilter){
14322 this.fireEvent('specialfilter', this);
14327 this.store.filter(this.displayField, q);
14330 this.store.fireEvent("datachanged", this.store);
14337 this.store.baseParams[this.queryParam] = q;
14339 var options = {params : this.getParams(q)};
14342 options.add = true;
14343 options.params.start = this.page * this.pageSize;
14346 this.store.load(options);
14349 * this code will make the page width larger, at the beginning, the list not align correctly,
14350 * we should expand the list on onLoad
14351 * so command out it
14356 this.selectedIndex = -1;
14361 this.loadNext = false;
14365 getParams : function(q){
14367 //p[this.queryParam] = q;
14371 p.limit = this.pageSize;
14377 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14379 collapse : function(){
14380 if(!this.isExpanded()){
14386 this.hasFocus = false;
14390 this.cancelBtn.hide();
14391 this.trigger.show();
14394 this.tickableInputEl().dom.value = '';
14395 this.tickableInputEl().blur();
14400 Roo.get(document).un('mousedown', this.collapseIf, this);
14401 Roo.get(document).un('mousewheel', this.collapseIf, this);
14402 if (!this.editable) {
14403 Roo.get(document).un('keydown', this.listKeyPress, this);
14405 this.fireEvent('collapse', this);
14411 collapseIf : function(e){
14412 var in_combo = e.within(this.el);
14413 var in_list = e.within(this.list);
14414 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14416 if (in_combo || in_list || is_list) {
14417 //e.stopPropagation();
14422 this.onTickableFooterButtonClick(e, false, false);
14430 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14432 expand : function(){
14434 if(this.isExpanded() || !this.hasFocus){
14438 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14439 this.list.setWidth(lw);
14445 this.restrictHeight();
14449 this.tickItems = Roo.apply([], this.item);
14452 this.cancelBtn.show();
14453 this.trigger.hide();
14456 this.tickableInputEl().focus();
14461 Roo.get(document).on('mousedown', this.collapseIf, this);
14462 Roo.get(document).on('mousewheel', this.collapseIf, this);
14463 if (!this.editable) {
14464 Roo.get(document).on('keydown', this.listKeyPress, this);
14467 this.fireEvent('expand', this);
14471 // Implements the default empty TriggerField.onTriggerClick function
14472 onTriggerClick : function(e)
14474 Roo.log('trigger click');
14476 if(this.disabled || !this.triggerList){
14481 this.loadNext = false;
14483 if(this.isExpanded()){
14485 if (!this.blockFocus) {
14486 this.inputEl().focus();
14490 this.hasFocus = true;
14491 if(this.triggerAction == 'all') {
14492 this.doQuery(this.allQuery, true);
14494 this.doQuery(this.getRawValue());
14496 if (!this.blockFocus) {
14497 this.inputEl().focus();
14502 onTickableTriggerClick : function(e)
14509 this.loadNext = false;
14510 this.hasFocus = true;
14512 if(this.triggerAction == 'all') {
14513 this.doQuery(this.allQuery, true);
14515 this.doQuery(this.getRawValue());
14519 onSearchFieldClick : function(e)
14521 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14522 this.onTickableFooterButtonClick(e, false, false);
14526 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14531 this.loadNext = false;
14532 this.hasFocus = true;
14534 if(this.triggerAction == 'all') {
14535 this.doQuery(this.allQuery, true);
14537 this.doQuery(this.getRawValue());
14541 listKeyPress : function(e)
14543 //Roo.log('listkeypress');
14544 // scroll to first matching element based on key pres..
14545 if (e.isSpecialKey()) {
14548 var k = String.fromCharCode(e.getKey()).toUpperCase();
14551 var csel = this.view.getSelectedNodes();
14552 var cselitem = false;
14554 var ix = this.view.indexOf(csel[0]);
14555 cselitem = this.store.getAt(ix);
14556 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14562 this.store.each(function(v) {
14564 // start at existing selection.
14565 if (cselitem.id == v.id) {
14571 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14572 match = this.store.indexOf(v);
14578 if (match === false) {
14579 return true; // no more action?
14582 this.view.select(match);
14583 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14584 sn.scrollIntoView(sn.dom.parentNode, false);
14587 onViewScroll : function(e, t){
14589 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){
14593 this.hasQuery = true;
14595 this.loading = this.list.select('.loading', true).first();
14597 if(this.loading === null){
14598 this.list.createChild({
14600 cls: 'loading roo-select2-more-results roo-select2-active',
14601 html: 'Loading more results...'
14604 this.loading = this.list.select('.loading', true).first();
14606 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14608 this.loading.hide();
14611 this.loading.show();
14616 this.loadNext = true;
14618 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14623 addItem : function(o)
14625 var dv = ''; // display value
14627 if (this.displayField) {
14628 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14630 // this is an error condition!!!
14631 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14638 var choice = this.choices.createChild({
14640 cls: 'roo-select2-search-choice',
14649 cls: 'roo-select2-search-choice-close fa fa-times',
14654 }, this.searchField);
14656 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14658 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14666 this.inputEl().dom.value = '';
14671 onRemoveItem : function(e, _self, o)
14673 e.preventDefault();
14675 this.lastItem = Roo.apply([], this.item);
14677 var index = this.item.indexOf(o.data) * 1;
14680 Roo.log('not this item?!');
14684 this.item.splice(index, 1);
14689 this.fireEvent('remove', this, e);
14695 syncValue : function()
14697 if(!this.item.length){
14704 Roo.each(this.item, function(i){
14705 if(_this.valueField){
14706 value.push(i[_this.valueField]);
14713 this.value = value.join(',');
14715 if(this.hiddenField){
14716 this.hiddenField.dom.value = this.value;
14719 this.store.fireEvent("datachanged", this.store);
14724 clearItem : function()
14726 if(!this.multiple){
14732 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14740 if(this.tickable && !Roo.isTouch){
14741 this.view.refresh();
14745 inputEl: function ()
14747 if(Roo.isIOS && this.useNativeIOS){
14748 return this.el.select('select.roo-ios-select', true).first();
14751 if(Roo.isTouch && this.mobileTouchView){
14752 return this.el.select('input.form-control',true).first();
14756 return this.searchField;
14759 return this.el.select('input.form-control',true).first();
14762 onTickableFooterButtonClick : function(e, btn, el)
14764 e.preventDefault();
14766 this.lastItem = Roo.apply([], this.item);
14768 if(btn && btn.name == 'cancel'){
14769 this.tickItems = Roo.apply([], this.item);
14778 Roo.each(this.tickItems, function(o){
14786 validate : function()
14788 if(this.getVisibilityEl().hasClass('hidden')){
14792 var v = this.getRawValue();
14795 v = this.getValue();
14798 if(this.disabled || this.allowBlank || v.length){
14803 this.markInvalid();
14807 tickableInputEl : function()
14809 if(!this.tickable || !this.editable){
14810 return this.inputEl();
14813 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14817 getAutoCreateTouchView : function()
14822 cls: 'form-group' //input-group
14828 type : this.inputType,
14829 cls : 'form-control x-combo-noedit',
14830 autocomplete: 'new-password',
14831 placeholder : this.placeholder || '',
14836 input.name = this.name;
14840 input.cls += ' input-' + this.size;
14843 if (this.disabled) {
14844 input.disabled = true;
14855 inputblock.cls += ' input-group';
14857 inputblock.cn.unshift({
14859 cls : 'input-group-addon',
14864 if(this.removable && !this.multiple){
14865 inputblock.cls += ' roo-removable';
14867 inputblock.cn.push({
14870 cls : 'roo-combo-removable-btn close'
14874 if(this.hasFeedback && !this.allowBlank){
14876 inputblock.cls += ' has-feedback';
14878 inputblock.cn.push({
14880 cls: 'glyphicon form-control-feedback'
14887 inputblock.cls += (this.before) ? '' : ' input-group';
14889 inputblock.cn.push({
14891 cls : 'input-group-addon',
14902 cls: 'form-hidden-field'
14916 cls: 'form-hidden-field'
14920 cls: 'roo-select2-choices',
14924 cls: 'roo-select2-search-field',
14937 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14943 if(!this.multiple && this.showToggleBtn){
14950 if (this.caret != false) {
14953 cls: 'fa fa-' + this.caret
14960 cls : 'input-group-addon btn dropdown-toggle',
14965 cls: 'combobox-clear',
14979 combobox.cls += ' roo-select2-container-multi';
14982 var align = this.labelAlign || this.parentLabelAlign();
14984 if (align ==='left' && this.fieldLabel.length) {
14989 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14990 tooltip : 'This field is required'
14994 cls : 'control-label',
14995 html : this.fieldLabel
15006 var labelCfg = cfg.cn[1];
15007 var contentCfg = cfg.cn[2];
15010 if(this.indicatorpos == 'right'){
15015 cls : 'control-label',
15019 html : this.fieldLabel
15023 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15024 tooltip : 'This field is required'
15037 labelCfg = cfg.cn[0];
15038 contentCfg = cfg.cn[1];
15043 if(this.labelWidth > 12){
15044 labelCfg.style = "width: " + this.labelWidth + 'px';
15047 if(this.labelWidth < 13 && this.labelmd == 0){
15048 this.labelmd = this.labelWidth;
15051 if(this.labellg > 0){
15052 labelCfg.cls += ' col-lg-' + this.labellg;
15053 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15056 if(this.labelmd > 0){
15057 labelCfg.cls += ' col-md-' + this.labelmd;
15058 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15061 if(this.labelsm > 0){
15062 labelCfg.cls += ' col-sm-' + this.labelsm;
15063 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15066 if(this.labelxs > 0){
15067 labelCfg.cls += ' col-xs-' + this.labelxs;
15068 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15072 } else if ( this.fieldLabel.length) {
15076 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15077 tooltip : 'This field is required'
15081 cls : 'control-label',
15082 html : this.fieldLabel
15093 if(this.indicatorpos == 'right'){
15097 cls : 'control-label',
15098 html : this.fieldLabel,
15102 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15103 tooltip : 'This field is required'
15120 var settings = this;
15122 ['xs','sm','md','lg'].map(function(size){
15123 if (settings[size]) {
15124 cfg.cls += ' col-' + size + '-' + settings[size];
15131 initTouchView : function()
15133 this.renderTouchView();
15135 this.touchViewEl.on('scroll', function(){
15136 this.el.dom.scrollTop = 0;
15139 this.originalValue = this.getValue();
15141 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15143 this.inputEl().on("click", this.showTouchView, this);
15144 if (this.triggerEl) {
15145 this.triggerEl.on("click", this.showTouchView, this);
15149 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15150 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15152 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15154 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15155 this.store.on('load', this.onTouchViewLoad, this);
15156 this.store.on('loadexception', this.onTouchViewLoadException, this);
15158 if(this.hiddenName){
15160 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15162 this.hiddenField.dom.value =
15163 this.hiddenValue !== undefined ? this.hiddenValue :
15164 this.value !== undefined ? this.value : '';
15166 this.el.dom.removeAttribute('name');
15167 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15171 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15172 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15175 if(this.removable && !this.multiple){
15176 var close = this.closeTriggerEl();
15178 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15179 close.on('click', this.removeBtnClick, this, close);
15183 * fix the bug in Safari iOS8
15185 this.inputEl().on("focus", function(e){
15186 document.activeElement.blur();
15194 renderTouchView : function()
15196 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15197 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15199 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15200 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15202 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15203 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15204 this.touchViewBodyEl.setStyle('overflow', 'auto');
15206 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15207 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15209 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15210 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15214 showTouchView : function()
15220 this.touchViewHeaderEl.hide();
15222 if(this.modalTitle.length){
15223 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15224 this.touchViewHeaderEl.show();
15227 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15228 this.touchViewEl.show();
15230 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15232 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15233 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15235 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15237 if(this.modalTitle.length){
15238 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15241 this.touchViewBodyEl.setHeight(bodyHeight);
15245 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15247 this.touchViewEl.addClass('in');
15250 this.doTouchViewQuery();
15254 hideTouchView : function()
15256 this.touchViewEl.removeClass('in');
15260 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15262 this.touchViewEl.setStyle('display', 'none');
15267 setTouchViewValue : function()
15274 Roo.each(this.tickItems, function(o){
15279 this.hideTouchView();
15282 doTouchViewQuery : function()
15291 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15295 if(!this.alwaysQuery || this.mode == 'local'){
15296 this.onTouchViewLoad();
15303 onTouchViewBeforeLoad : function(combo,opts)
15309 onTouchViewLoad : function()
15311 if(this.store.getCount() < 1){
15312 this.onTouchViewEmptyResults();
15316 this.clearTouchView();
15318 var rawValue = this.getRawValue();
15320 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15322 this.tickItems = [];
15324 this.store.data.each(function(d, rowIndex){
15325 var row = this.touchViewListGroup.createChild(template);
15327 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15328 row.addClass(d.data.cls);
15331 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15334 html : d.data[this.displayField]
15337 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15338 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15341 row.removeClass('selected');
15342 if(!this.multiple && this.valueField &&
15343 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15346 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15347 row.addClass('selected');
15350 if(this.multiple && this.valueField &&
15351 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15355 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15356 this.tickItems.push(d.data);
15359 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15363 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15365 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15367 if(this.modalTitle.length){
15368 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15371 var listHeight = this.touchViewListGroup.getHeight();
15375 if(firstChecked && listHeight > bodyHeight){
15376 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15381 onTouchViewLoadException : function()
15383 this.hideTouchView();
15386 onTouchViewEmptyResults : function()
15388 this.clearTouchView();
15390 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15392 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15396 clearTouchView : function()
15398 this.touchViewListGroup.dom.innerHTML = '';
15401 onTouchViewClick : function(e, el, o)
15403 e.preventDefault();
15406 var rowIndex = o.rowIndex;
15408 var r = this.store.getAt(rowIndex);
15410 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15412 if(!this.multiple){
15413 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15414 c.dom.removeAttribute('checked');
15417 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15419 this.setFromData(r.data);
15421 var close = this.closeTriggerEl();
15427 this.hideTouchView();
15429 this.fireEvent('select', this, r, rowIndex);
15434 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15435 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15436 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15440 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15441 this.addItem(r.data);
15442 this.tickItems.push(r.data);
15446 getAutoCreateNativeIOS : function()
15449 cls: 'form-group' //input-group,
15454 cls : 'roo-ios-select'
15458 combobox.name = this.name;
15461 if (this.disabled) {
15462 combobox.disabled = true;
15465 var settings = this;
15467 ['xs','sm','md','lg'].map(function(size){
15468 if (settings[size]) {
15469 cfg.cls += ' col-' + size + '-' + settings[size];
15479 initIOSView : function()
15481 this.store.on('load', this.onIOSViewLoad, this);
15486 onIOSViewLoad : function()
15488 if(this.store.getCount() < 1){
15492 this.clearIOSView();
15494 if(this.allowBlank) {
15496 var default_text = '-- SELECT --';
15498 if(this.placeholder.length){
15499 default_text = this.placeholder;
15502 if(this.emptyTitle.length){
15503 default_text += ' - ' + this.emptyTitle + ' -';
15506 var opt = this.inputEl().createChild({
15509 html : default_text
15513 o[this.valueField] = 0;
15514 o[this.displayField] = default_text;
15516 this.ios_options.push({
15523 this.store.data.each(function(d, rowIndex){
15527 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15528 html = d.data[this.displayField];
15533 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15534 value = d.data[this.valueField];
15543 if(this.value == d.data[this.valueField]){
15544 option['selected'] = true;
15547 var opt = this.inputEl().createChild(option);
15549 this.ios_options.push({
15556 this.inputEl().on('change', function(){
15557 this.fireEvent('select', this);
15562 clearIOSView: function()
15564 this.inputEl().dom.innerHTML = '';
15566 this.ios_options = [];
15569 setIOSValue: function(v)
15573 if(!this.ios_options){
15577 Roo.each(this.ios_options, function(opts){
15579 opts.el.dom.removeAttribute('selected');
15581 if(opts.data[this.valueField] != v){
15585 opts.el.dom.setAttribute('selected', true);
15591 * @cfg {Boolean} grow
15595 * @cfg {Number} growMin
15599 * @cfg {Number} growMax
15608 Roo.apply(Roo.bootstrap.ComboBox, {
15612 cls: 'modal-header',
15634 cls: 'list-group-item',
15638 cls: 'roo-combobox-list-group-item-value'
15642 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15656 listItemCheckbox : {
15658 cls: 'list-group-item',
15662 cls: 'roo-combobox-list-group-item-value'
15666 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15682 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15687 cls: 'modal-footer',
15695 cls: 'col-xs-6 text-left',
15698 cls: 'btn btn-danger roo-touch-view-cancel',
15704 cls: 'col-xs-6 text-right',
15707 cls: 'btn btn-success roo-touch-view-ok',
15718 Roo.apply(Roo.bootstrap.ComboBox, {
15720 touchViewTemplate : {
15722 cls: 'modal fade roo-combobox-touch-view',
15726 cls: 'modal-dialog',
15727 style : 'position:fixed', // we have to fix position....
15731 cls: 'modal-content',
15733 Roo.bootstrap.ComboBox.header,
15734 Roo.bootstrap.ComboBox.body,
15735 Roo.bootstrap.ComboBox.footer
15744 * Ext JS Library 1.1.1
15745 * Copyright(c) 2006-2007, Ext JS, LLC.
15747 * Originally Released Under LGPL - original licence link has changed is not relivant.
15750 * <script type="text/javascript">
15755 * @extends Roo.util.Observable
15756 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15757 * This class also supports single and multi selection modes. <br>
15758 * Create a data model bound view:
15760 var store = new Roo.data.Store(...);
15762 var view = new Roo.View({
15764 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15766 singleSelect: true,
15767 selectedClass: "ydataview-selected",
15771 // listen for node click?
15772 view.on("click", function(vw, index, node, e){
15773 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15777 dataModel.load("foobar.xml");
15779 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15781 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15782 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15784 * Note: old style constructor is still suported (container, template, config)
15787 * Create a new View
15788 * @param {Object} config The config object
15791 Roo.View = function(config, depreciated_tpl, depreciated_config){
15793 this.parent = false;
15795 if (typeof(depreciated_tpl) == 'undefined') {
15796 // new way.. - universal constructor.
15797 Roo.apply(this, config);
15798 this.el = Roo.get(this.el);
15801 this.el = Roo.get(config);
15802 this.tpl = depreciated_tpl;
15803 Roo.apply(this, depreciated_config);
15805 this.wrapEl = this.el.wrap().wrap();
15806 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15809 if(typeof(this.tpl) == "string"){
15810 this.tpl = new Roo.Template(this.tpl);
15812 // support xtype ctors..
15813 this.tpl = new Roo.factory(this.tpl, Roo);
15817 this.tpl.compile();
15822 * @event beforeclick
15823 * Fires before a click is processed. Returns false to cancel the default action.
15824 * @param {Roo.View} this
15825 * @param {Number} index The index of the target node
15826 * @param {HTMLElement} node The target node
15827 * @param {Roo.EventObject} e The raw event object
15829 "beforeclick" : true,
15832 * Fires when a template node is clicked.
15833 * @param {Roo.View} this
15834 * @param {Number} index The index of the target node
15835 * @param {HTMLElement} node The target node
15836 * @param {Roo.EventObject} e The raw event object
15841 * Fires when a template node is double clicked.
15842 * @param {Roo.View} this
15843 * @param {Number} index The index of the target node
15844 * @param {HTMLElement} node The target node
15845 * @param {Roo.EventObject} e The raw event object
15849 * @event contextmenu
15850 * Fires when a template node is right clicked.
15851 * @param {Roo.View} this
15852 * @param {Number} index The index of the target node
15853 * @param {HTMLElement} node The target node
15854 * @param {Roo.EventObject} e The raw event object
15856 "contextmenu" : true,
15858 * @event selectionchange
15859 * Fires when the selected nodes change.
15860 * @param {Roo.View} this
15861 * @param {Array} selections Array of the selected nodes
15863 "selectionchange" : true,
15866 * @event beforeselect
15867 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15868 * @param {Roo.View} this
15869 * @param {HTMLElement} node The node to be selected
15870 * @param {Array} selections Array of currently selected nodes
15872 "beforeselect" : true,
15874 * @event preparedata
15875 * Fires on every row to render, to allow you to change the data.
15876 * @param {Roo.View} this
15877 * @param {Object} data to be rendered (change this)
15879 "preparedata" : true
15887 "click": this.onClick,
15888 "dblclick": this.onDblClick,
15889 "contextmenu": this.onContextMenu,
15893 this.selections = [];
15895 this.cmp = new Roo.CompositeElementLite([]);
15897 this.store = Roo.factory(this.store, Roo.data);
15898 this.setStore(this.store, true);
15901 if ( this.footer && this.footer.xtype) {
15903 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15905 this.footer.dataSource = this.store;
15906 this.footer.container = fctr;
15907 this.footer = Roo.factory(this.footer, Roo);
15908 fctr.insertFirst(this.el);
15910 // this is a bit insane - as the paging toolbar seems to detach the el..
15911 // dom.parentNode.parentNode.parentNode
15912 // they get detached?
15916 Roo.View.superclass.constructor.call(this);
15921 Roo.extend(Roo.View, Roo.util.Observable, {
15924 * @cfg {Roo.data.Store} store Data store to load data from.
15929 * @cfg {String|Roo.Element} el The container element.
15934 * @cfg {String|Roo.Template} tpl The template used by this View
15938 * @cfg {String} dataName the named area of the template to use as the data area
15939 * Works with domtemplates roo-name="name"
15943 * @cfg {String} selectedClass The css class to add to selected nodes
15945 selectedClass : "x-view-selected",
15947 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15952 * @cfg {String} text to display on mask (default Loading)
15956 * @cfg {Boolean} multiSelect Allow multiple selection
15958 multiSelect : false,
15960 * @cfg {Boolean} singleSelect Allow single selection
15962 singleSelect: false,
15965 * @cfg {Boolean} toggleSelect - selecting
15967 toggleSelect : false,
15970 * @cfg {Boolean} tickable - selecting
15975 * Returns the element this view is bound to.
15976 * @return {Roo.Element}
15978 getEl : function(){
15979 return this.wrapEl;
15985 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15987 refresh : function(){
15988 //Roo.log('refresh');
15991 // if we are using something like 'domtemplate', then
15992 // the what gets used is:
15993 // t.applySubtemplate(NAME, data, wrapping data..)
15994 // the outer template then get' applied with
15995 // the store 'extra data'
15996 // and the body get's added to the
15997 // roo-name="data" node?
15998 // <span class='roo-tpl-{name}'></span> ?????
16002 this.clearSelections();
16003 this.el.update("");
16005 var records = this.store.getRange();
16006 if(records.length < 1) {
16008 // is this valid?? = should it render a template??
16010 this.el.update(this.emptyText);
16014 if (this.dataName) {
16015 this.el.update(t.apply(this.store.meta)); //????
16016 el = this.el.child('.roo-tpl-' + this.dataName);
16019 for(var i = 0, len = records.length; i < len; i++){
16020 var data = this.prepareData(records[i].data, i, records[i]);
16021 this.fireEvent("preparedata", this, data, i, records[i]);
16023 var d = Roo.apply({}, data);
16026 Roo.apply(d, {'roo-id' : Roo.id()});
16030 Roo.each(this.parent.item, function(item){
16031 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16034 Roo.apply(d, {'roo-data-checked' : 'checked'});
16038 html[html.length] = Roo.util.Format.trim(
16040 t.applySubtemplate(this.dataName, d, this.store.meta) :
16047 el.update(html.join(""));
16048 this.nodes = el.dom.childNodes;
16049 this.updateIndexes(0);
16054 * Function to override to reformat the data that is sent to
16055 * the template for each node.
16056 * DEPRICATED - use the preparedata event handler.
16057 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16058 * a JSON object for an UpdateManager bound view).
16060 prepareData : function(data, index, record)
16062 this.fireEvent("preparedata", this, data, index, record);
16066 onUpdate : function(ds, record){
16067 // Roo.log('on update');
16068 this.clearSelections();
16069 var index = this.store.indexOf(record);
16070 var n = this.nodes[index];
16071 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16072 n.parentNode.removeChild(n);
16073 this.updateIndexes(index, index);
16079 onAdd : function(ds, records, index)
16081 //Roo.log(['on Add', ds, records, index] );
16082 this.clearSelections();
16083 if(this.nodes.length == 0){
16087 var n = this.nodes[index];
16088 for(var i = 0, len = records.length; i < len; i++){
16089 var d = this.prepareData(records[i].data, i, records[i]);
16091 this.tpl.insertBefore(n, d);
16094 this.tpl.append(this.el, d);
16097 this.updateIndexes(index);
16100 onRemove : function(ds, record, index){
16101 // Roo.log('onRemove');
16102 this.clearSelections();
16103 var el = this.dataName ?
16104 this.el.child('.roo-tpl-' + this.dataName) :
16107 el.dom.removeChild(this.nodes[index]);
16108 this.updateIndexes(index);
16112 * Refresh an individual node.
16113 * @param {Number} index
16115 refreshNode : function(index){
16116 this.onUpdate(this.store, this.store.getAt(index));
16119 updateIndexes : function(startIndex, endIndex){
16120 var ns = this.nodes;
16121 startIndex = startIndex || 0;
16122 endIndex = endIndex || ns.length - 1;
16123 for(var i = startIndex; i <= endIndex; i++){
16124 ns[i].nodeIndex = i;
16129 * Changes the data store this view uses and refresh the view.
16130 * @param {Store} store
16132 setStore : function(store, initial){
16133 if(!initial && this.store){
16134 this.store.un("datachanged", this.refresh);
16135 this.store.un("add", this.onAdd);
16136 this.store.un("remove", this.onRemove);
16137 this.store.un("update", this.onUpdate);
16138 this.store.un("clear", this.refresh);
16139 this.store.un("beforeload", this.onBeforeLoad);
16140 this.store.un("load", this.onLoad);
16141 this.store.un("loadexception", this.onLoad);
16145 store.on("datachanged", this.refresh, this);
16146 store.on("add", this.onAdd, this);
16147 store.on("remove", this.onRemove, this);
16148 store.on("update", this.onUpdate, this);
16149 store.on("clear", this.refresh, this);
16150 store.on("beforeload", this.onBeforeLoad, this);
16151 store.on("load", this.onLoad, this);
16152 store.on("loadexception", this.onLoad, this);
16160 * onbeforeLoad - masks the loading area.
16163 onBeforeLoad : function(store,opts)
16165 //Roo.log('onBeforeLoad');
16167 this.el.update("");
16169 this.el.mask(this.mask ? this.mask : "Loading" );
16171 onLoad : function ()
16178 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16179 * @param {HTMLElement} node
16180 * @return {HTMLElement} The template node
16182 findItemFromChild : function(node){
16183 var el = this.dataName ?
16184 this.el.child('.roo-tpl-' + this.dataName,true) :
16187 if(!node || node.parentNode == el){
16190 var p = node.parentNode;
16191 while(p && p != el){
16192 if(p.parentNode == el){
16201 onClick : function(e){
16202 var item = this.findItemFromChild(e.getTarget());
16204 var index = this.indexOf(item);
16205 if(this.onItemClick(item, index, e) !== false){
16206 this.fireEvent("click", this, index, item, e);
16209 this.clearSelections();
16214 onContextMenu : function(e){
16215 var item = this.findItemFromChild(e.getTarget());
16217 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16222 onDblClick : function(e){
16223 var item = this.findItemFromChild(e.getTarget());
16225 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16229 onItemClick : function(item, index, e)
16231 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16234 if (this.toggleSelect) {
16235 var m = this.isSelected(item) ? 'unselect' : 'select';
16238 _t[m](item, true, false);
16241 if(this.multiSelect || this.singleSelect){
16242 if(this.multiSelect && e.shiftKey && this.lastSelection){
16243 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16245 this.select(item, this.multiSelect && e.ctrlKey);
16246 this.lastSelection = item;
16249 if(!this.tickable){
16250 e.preventDefault();
16258 * Get the number of selected nodes.
16261 getSelectionCount : function(){
16262 return this.selections.length;
16266 * Get the currently selected nodes.
16267 * @return {Array} An array of HTMLElements
16269 getSelectedNodes : function(){
16270 return this.selections;
16274 * Get the indexes of the selected nodes.
16277 getSelectedIndexes : function(){
16278 var indexes = [], s = this.selections;
16279 for(var i = 0, len = s.length; i < len; i++){
16280 indexes.push(s[i].nodeIndex);
16286 * Clear all selections
16287 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16289 clearSelections : function(suppressEvent){
16290 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16291 this.cmp.elements = this.selections;
16292 this.cmp.removeClass(this.selectedClass);
16293 this.selections = [];
16294 if(!suppressEvent){
16295 this.fireEvent("selectionchange", this, this.selections);
16301 * Returns true if the passed node is selected
16302 * @param {HTMLElement/Number} node The node or node index
16303 * @return {Boolean}
16305 isSelected : function(node){
16306 var s = this.selections;
16310 node = this.getNode(node);
16311 return s.indexOf(node) !== -1;
16316 * @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
16317 * @param {Boolean} keepExisting (optional) true to keep existing selections
16318 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16320 select : function(nodeInfo, keepExisting, suppressEvent){
16321 if(nodeInfo instanceof Array){
16323 this.clearSelections(true);
16325 for(var i = 0, len = nodeInfo.length; i < len; i++){
16326 this.select(nodeInfo[i], true, true);
16330 var node = this.getNode(nodeInfo);
16331 if(!node || this.isSelected(node)){
16332 return; // already selected.
16335 this.clearSelections(true);
16338 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16339 Roo.fly(node).addClass(this.selectedClass);
16340 this.selections.push(node);
16341 if(!suppressEvent){
16342 this.fireEvent("selectionchange", this, this.selections);
16350 * @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
16351 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16352 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16354 unselect : function(nodeInfo, keepExisting, suppressEvent)
16356 if(nodeInfo instanceof Array){
16357 Roo.each(this.selections, function(s) {
16358 this.unselect(s, nodeInfo);
16362 var node = this.getNode(nodeInfo);
16363 if(!node || !this.isSelected(node)){
16364 //Roo.log("not selected");
16365 return; // not selected.
16369 Roo.each(this.selections, function(s) {
16371 Roo.fly(node).removeClass(this.selectedClass);
16378 this.selections= ns;
16379 this.fireEvent("selectionchange", this, this.selections);
16383 * Gets a template node.
16384 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16385 * @return {HTMLElement} The node or null if it wasn't found
16387 getNode : function(nodeInfo){
16388 if(typeof nodeInfo == "string"){
16389 return document.getElementById(nodeInfo);
16390 }else if(typeof nodeInfo == "number"){
16391 return this.nodes[nodeInfo];
16397 * Gets a range template nodes.
16398 * @param {Number} startIndex
16399 * @param {Number} endIndex
16400 * @return {Array} An array of nodes
16402 getNodes : function(start, end){
16403 var ns = this.nodes;
16404 start = start || 0;
16405 end = typeof end == "undefined" ? ns.length - 1 : end;
16408 for(var i = start; i <= end; i++){
16412 for(var i = start; i >= end; i--){
16420 * Finds the index of the passed node
16421 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16422 * @return {Number} The index of the node or -1
16424 indexOf : function(node){
16425 node = this.getNode(node);
16426 if(typeof node.nodeIndex == "number"){
16427 return node.nodeIndex;
16429 var ns = this.nodes;
16430 for(var i = 0, len = ns.length; i < len; i++){
16441 * based on jquery fullcalendar
16445 Roo.bootstrap = Roo.bootstrap || {};
16447 * @class Roo.bootstrap.Calendar
16448 * @extends Roo.bootstrap.Component
16449 * Bootstrap Calendar class
16450 * @cfg {Boolean} loadMask (true|false) default false
16451 * @cfg {Object} header generate the user specific header of the calendar, default false
16454 * Create a new Container
16455 * @param {Object} config The config object
16460 Roo.bootstrap.Calendar = function(config){
16461 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16465 * Fires when a date is selected
16466 * @param {DatePicker} this
16467 * @param {Date} date The selected date
16471 * @event monthchange
16472 * Fires when the displayed month changes
16473 * @param {DatePicker} this
16474 * @param {Date} date The selected month
16476 'monthchange': true,
16478 * @event evententer
16479 * Fires when mouse over an event
16480 * @param {Calendar} this
16481 * @param {event} Event
16483 'evententer': true,
16485 * @event eventleave
16486 * Fires when the mouse leaves an
16487 * @param {Calendar} this
16490 'eventleave': true,
16492 * @event eventclick
16493 * Fires when the mouse click an
16494 * @param {Calendar} this
16503 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16506 * @cfg {Number} startDay
16507 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16515 getAutoCreate : function(){
16518 var fc_button = function(name, corner, style, content ) {
16519 return Roo.apply({},{
16521 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16523 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16526 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16537 style : 'width:100%',
16544 cls : 'fc-header-left',
16546 fc_button('prev', 'left', 'arrow', '‹' ),
16547 fc_button('next', 'right', 'arrow', '›' ),
16548 { tag: 'span', cls: 'fc-header-space' },
16549 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16557 cls : 'fc-header-center',
16561 cls: 'fc-header-title',
16564 html : 'month / year'
16572 cls : 'fc-header-right',
16574 /* fc_button('month', 'left', '', 'month' ),
16575 fc_button('week', '', '', 'week' ),
16576 fc_button('day', 'right', '', 'day' )
16588 header = this.header;
16591 var cal_heads = function() {
16593 // fixme - handle this.
16595 for (var i =0; i < Date.dayNames.length; i++) {
16596 var d = Date.dayNames[i];
16599 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16600 html : d.substring(0,3)
16604 ret[0].cls += ' fc-first';
16605 ret[6].cls += ' fc-last';
16608 var cal_cell = function(n) {
16611 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16616 cls: 'fc-day-number',
16620 cls: 'fc-day-content',
16624 style: 'position: relative;' // height: 17px;
16636 var cal_rows = function() {
16639 for (var r = 0; r < 6; r++) {
16646 for (var i =0; i < Date.dayNames.length; i++) {
16647 var d = Date.dayNames[i];
16648 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16651 row.cn[0].cls+=' fc-first';
16652 row.cn[0].cn[0].style = 'min-height:90px';
16653 row.cn[6].cls+=' fc-last';
16657 ret[0].cls += ' fc-first';
16658 ret[4].cls += ' fc-prev-last';
16659 ret[5].cls += ' fc-last';
16666 cls: 'fc-border-separate',
16667 style : 'width:100%',
16675 cls : 'fc-first fc-last',
16693 cls : 'fc-content',
16694 style : "position: relative;",
16697 cls : 'fc-view fc-view-month fc-grid',
16698 style : 'position: relative',
16699 unselectable : 'on',
16702 cls : 'fc-event-container',
16703 style : 'position:absolute;z-index:8;top:0;left:0;'
16721 initEvents : function()
16724 throw "can not find store for calendar";
16730 style: "text-align:center",
16734 style: "background-color:white;width:50%;margin:250 auto",
16738 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16749 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16751 var size = this.el.select('.fc-content', true).first().getSize();
16752 this.maskEl.setSize(size.width, size.height);
16753 this.maskEl.enableDisplayMode("block");
16754 if(!this.loadMask){
16755 this.maskEl.hide();
16758 this.store = Roo.factory(this.store, Roo.data);
16759 this.store.on('load', this.onLoad, this);
16760 this.store.on('beforeload', this.onBeforeLoad, this);
16764 this.cells = this.el.select('.fc-day',true);
16765 //Roo.log(this.cells);
16766 this.textNodes = this.el.query('.fc-day-number');
16767 this.cells.addClassOnOver('fc-state-hover');
16769 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16770 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16771 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16772 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16774 this.on('monthchange', this.onMonthChange, this);
16776 this.update(new Date().clearTime());
16779 resize : function() {
16780 var sz = this.el.getSize();
16782 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16783 this.el.select('.fc-day-content div',true).setHeight(34);
16788 showPrevMonth : function(e){
16789 this.update(this.activeDate.add("mo", -1));
16791 showToday : function(e){
16792 this.update(new Date().clearTime());
16795 showNextMonth : function(e){
16796 this.update(this.activeDate.add("mo", 1));
16800 showPrevYear : function(){
16801 this.update(this.activeDate.add("y", -1));
16805 showNextYear : function(){
16806 this.update(this.activeDate.add("y", 1));
16811 update : function(date)
16813 var vd = this.activeDate;
16814 this.activeDate = date;
16815 // if(vd && this.el){
16816 // var t = date.getTime();
16817 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16818 // Roo.log('using add remove');
16820 // this.fireEvent('monthchange', this, date);
16822 // this.cells.removeClass("fc-state-highlight");
16823 // this.cells.each(function(c){
16824 // if(c.dateValue == t){
16825 // c.addClass("fc-state-highlight");
16826 // setTimeout(function(){
16827 // try{c.dom.firstChild.focus();}catch(e){}
16837 var days = date.getDaysInMonth();
16839 var firstOfMonth = date.getFirstDateOfMonth();
16840 var startingPos = firstOfMonth.getDay()-this.startDay;
16842 if(startingPos < this.startDay){
16846 var pm = date.add(Date.MONTH, -1);
16847 var prevStart = pm.getDaysInMonth()-startingPos;
16849 this.cells = this.el.select('.fc-day',true);
16850 this.textNodes = this.el.query('.fc-day-number');
16851 this.cells.addClassOnOver('fc-state-hover');
16853 var cells = this.cells.elements;
16854 var textEls = this.textNodes;
16856 Roo.each(cells, function(cell){
16857 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16860 days += startingPos;
16862 // convert everything to numbers so it's fast
16863 var day = 86400000;
16864 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16867 //Roo.log(prevStart);
16869 var today = new Date().clearTime().getTime();
16870 var sel = date.clearTime().getTime();
16871 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16872 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16873 var ddMatch = this.disabledDatesRE;
16874 var ddText = this.disabledDatesText;
16875 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16876 var ddaysText = this.disabledDaysText;
16877 var format = this.format;
16879 var setCellClass = function(cal, cell){
16883 //Roo.log('set Cell Class');
16885 var t = d.getTime();
16889 cell.dateValue = t;
16891 cell.className += " fc-today";
16892 cell.className += " fc-state-highlight";
16893 cell.title = cal.todayText;
16896 // disable highlight in other month..
16897 //cell.className += " fc-state-highlight";
16902 cell.className = " fc-state-disabled";
16903 cell.title = cal.minText;
16907 cell.className = " fc-state-disabled";
16908 cell.title = cal.maxText;
16912 if(ddays.indexOf(d.getDay()) != -1){
16913 cell.title = ddaysText;
16914 cell.className = " fc-state-disabled";
16917 if(ddMatch && format){
16918 var fvalue = d.dateFormat(format);
16919 if(ddMatch.test(fvalue)){
16920 cell.title = ddText.replace("%0", fvalue);
16921 cell.className = " fc-state-disabled";
16925 if (!cell.initialClassName) {
16926 cell.initialClassName = cell.dom.className;
16929 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16934 for(; i < startingPos; i++) {
16935 textEls[i].innerHTML = (++prevStart);
16936 d.setDate(d.getDate()+1);
16938 cells[i].className = "fc-past fc-other-month";
16939 setCellClass(this, cells[i]);
16944 for(; i < days; i++){
16945 intDay = i - startingPos + 1;
16946 textEls[i].innerHTML = (intDay);
16947 d.setDate(d.getDate()+1);
16949 cells[i].className = ''; // "x-date-active";
16950 setCellClass(this, cells[i]);
16954 for(; i < 42; i++) {
16955 textEls[i].innerHTML = (++extraDays);
16956 d.setDate(d.getDate()+1);
16958 cells[i].className = "fc-future fc-other-month";
16959 setCellClass(this, cells[i]);
16962 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16964 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16966 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16967 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16969 if(totalRows != 6){
16970 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16971 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16974 this.fireEvent('monthchange', this, date);
16978 if(!this.internalRender){
16979 var main = this.el.dom.firstChild;
16980 var w = main.offsetWidth;
16981 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16982 Roo.fly(main).setWidth(w);
16983 this.internalRender = true;
16984 // opera does not respect the auto grow header center column
16985 // then, after it gets a width opera refuses to recalculate
16986 // without a second pass
16987 if(Roo.isOpera && !this.secondPass){
16988 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16989 this.secondPass = true;
16990 this.update.defer(10, this, [date]);
16997 findCell : function(dt) {
16998 dt = dt.clearTime().getTime();
17000 this.cells.each(function(c){
17001 //Roo.log("check " +c.dateValue + '?=' + dt);
17002 if(c.dateValue == dt){
17012 findCells : function(ev) {
17013 var s = ev.start.clone().clearTime().getTime();
17015 var e= ev.end.clone().clearTime().getTime();
17018 this.cells.each(function(c){
17019 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17021 if(c.dateValue > e){
17024 if(c.dateValue < s){
17033 // findBestRow: function(cells)
17037 // for (var i =0 ; i < cells.length;i++) {
17038 // ret = Math.max(cells[i].rows || 0,ret);
17045 addItem : function(ev)
17047 // look for vertical location slot in
17048 var cells = this.findCells(ev);
17050 // ev.row = this.findBestRow(cells);
17052 // work out the location.
17056 for(var i =0; i < cells.length; i++) {
17058 cells[i].row = cells[0].row;
17061 cells[i].row = cells[i].row + 1;
17071 if (crow.start.getY() == cells[i].getY()) {
17073 crow.end = cells[i];
17090 cells[0].events.push(ev);
17092 this.calevents.push(ev);
17095 clearEvents: function() {
17097 if(!this.calevents){
17101 Roo.each(this.cells.elements, function(c){
17107 Roo.each(this.calevents, function(e) {
17108 Roo.each(e.els, function(el) {
17109 el.un('mouseenter' ,this.onEventEnter, this);
17110 el.un('mouseleave' ,this.onEventLeave, this);
17115 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17121 renderEvents: function()
17125 this.cells.each(function(c) {
17134 if(c.row != c.events.length){
17135 r = 4 - (4 - (c.row - c.events.length));
17138 c.events = ev.slice(0, r);
17139 c.more = ev.slice(r);
17141 if(c.more.length && c.more.length == 1){
17142 c.events.push(c.more.pop());
17145 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17149 this.cells.each(function(c) {
17151 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17154 for (var e = 0; e < c.events.length; e++){
17155 var ev = c.events[e];
17156 var rows = ev.rows;
17158 for(var i = 0; i < rows.length; i++) {
17160 // how many rows should it span..
17163 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17164 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17166 unselectable : "on",
17169 cls: 'fc-event-inner',
17173 // cls: 'fc-event-time',
17174 // html : cells.length > 1 ? '' : ev.time
17178 cls: 'fc-event-title',
17179 html : String.format('{0}', ev.title)
17186 cls: 'ui-resizable-handle ui-resizable-e',
17187 html : '  '
17194 cfg.cls += ' fc-event-start';
17196 if ((i+1) == rows.length) {
17197 cfg.cls += ' fc-event-end';
17200 var ctr = _this.el.select('.fc-event-container',true).first();
17201 var cg = ctr.createChild(cfg);
17203 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17204 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17206 var r = (c.more.length) ? 1 : 0;
17207 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17208 cg.setWidth(ebox.right - sbox.x -2);
17210 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17211 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17212 cg.on('click', _this.onEventClick, _this, ev);
17223 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17224 style : 'position: absolute',
17225 unselectable : "on",
17228 cls: 'fc-event-inner',
17232 cls: 'fc-event-title',
17240 cls: 'ui-resizable-handle ui-resizable-e',
17241 html : '  '
17247 var ctr = _this.el.select('.fc-event-container',true).first();
17248 var cg = ctr.createChild(cfg);
17250 var sbox = c.select('.fc-day-content',true).first().getBox();
17251 var ebox = c.select('.fc-day-content',true).first().getBox();
17253 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17254 cg.setWidth(ebox.right - sbox.x -2);
17256 cg.on('click', _this.onMoreEventClick, _this, c.more);
17266 onEventEnter: function (e, el,event,d) {
17267 this.fireEvent('evententer', this, el, event);
17270 onEventLeave: function (e, el,event,d) {
17271 this.fireEvent('eventleave', this, el, event);
17274 onEventClick: function (e, el,event,d) {
17275 this.fireEvent('eventclick', this, el, event);
17278 onMonthChange: function () {
17282 onMoreEventClick: function(e, el, more)
17286 this.calpopover.placement = 'right';
17287 this.calpopover.setTitle('More');
17289 this.calpopover.setContent('');
17291 var ctr = this.calpopover.el.select('.popover-content', true).first();
17293 Roo.each(more, function(m){
17295 cls : 'fc-event-hori fc-event-draggable',
17298 var cg = ctr.createChild(cfg);
17300 cg.on('click', _this.onEventClick, _this, m);
17303 this.calpopover.show(el);
17308 onLoad: function ()
17310 this.calevents = [];
17313 if(this.store.getCount() > 0){
17314 this.store.data.each(function(d){
17317 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17318 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17319 time : d.data.start_time,
17320 title : d.data.title,
17321 description : d.data.description,
17322 venue : d.data.venue
17327 this.renderEvents();
17329 if(this.calevents.length && this.loadMask){
17330 this.maskEl.hide();
17334 onBeforeLoad: function()
17336 this.clearEvents();
17338 this.maskEl.show();
17352 * @class Roo.bootstrap.Popover
17353 * @extends Roo.bootstrap.Component
17354 * Bootstrap Popover class
17355 * @cfg {String} html contents of the popover (or false to use children..)
17356 * @cfg {String} title of popover (or false to hide)
17357 * @cfg {String} placement how it is placed
17358 * @cfg {String} trigger click || hover (or false to trigger manually)
17359 * @cfg {String} over what (parent or false to trigger manually.)
17360 * @cfg {Number} delay - delay before showing
17363 * Create a new Popover
17364 * @param {Object} config The config object
17367 Roo.bootstrap.Popover = function(config){
17368 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17374 * After the popover show
17376 * @param {Roo.bootstrap.Popover} this
17381 * After the popover hide
17383 * @param {Roo.bootstrap.Popover} this
17389 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17391 title: 'Fill in a title',
17394 placement : 'right',
17395 trigger : 'hover', // hover
17401 can_build_overlaid : false,
17403 getChildContainer : function()
17405 return this.el.select('.popover-content',true).first();
17408 getAutoCreate : function(){
17411 cls : 'popover roo-dynamic',
17412 style: 'display:block',
17418 cls : 'popover-inner',
17422 cls: 'popover-title',
17426 cls : 'popover-content',
17437 setTitle: function(str)
17440 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17442 setContent: function(str)
17445 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17447 // as it get's added to the bottom of the page.
17448 onRender : function(ct, position)
17450 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17452 var cfg = Roo.apply({}, this.getAutoCreate());
17456 cfg.cls += ' ' + this.cls;
17459 cfg.style = this.style;
17461 //Roo.log("adding to ");
17462 this.el = Roo.get(document.body).createChild(cfg, position);
17463 // Roo.log(this.el);
17468 initEvents : function()
17470 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17471 this.el.enableDisplayMode('block');
17473 if (this.over === false) {
17476 if (this.triggers === false) {
17479 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17480 var triggers = this.trigger ? this.trigger.split(' ') : [];
17481 Roo.each(triggers, function(trigger) {
17483 if (trigger == 'click') {
17484 on_el.on('click', this.toggle, this);
17485 } else if (trigger != 'manual') {
17486 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17487 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17489 on_el.on(eventIn ,this.enter, this);
17490 on_el.on(eventOut, this.leave, this);
17501 toggle : function () {
17502 this.hoverState == 'in' ? this.leave() : this.enter();
17505 enter : function () {
17507 clearTimeout(this.timeout);
17509 this.hoverState = 'in';
17511 if (!this.delay || !this.delay.show) {
17516 this.timeout = setTimeout(function () {
17517 if (_t.hoverState == 'in') {
17520 }, this.delay.show)
17523 leave : function() {
17524 clearTimeout(this.timeout);
17526 this.hoverState = 'out';
17528 if (!this.delay || !this.delay.hide) {
17533 this.timeout = setTimeout(function () {
17534 if (_t.hoverState == 'out') {
17537 }, this.delay.hide)
17540 show : function (on_el)
17543 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17547 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17548 if (this.html !== false) {
17549 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17551 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17552 if (!this.title.length) {
17553 this.el.select('.popover-title',true).hide();
17556 var placement = typeof this.placement == 'function' ?
17557 this.placement.call(this, this.el, on_el) :
17560 var autoToken = /\s?auto?\s?/i;
17561 var autoPlace = autoToken.test(placement);
17563 placement = placement.replace(autoToken, '') || 'top';
17567 //this.el.setXY([0,0]);
17569 this.el.dom.style.display='block';
17570 this.el.addClass(placement);
17572 //this.el.appendTo(on_el);
17574 var p = this.getPosition();
17575 var box = this.el.getBox();
17580 var align = Roo.bootstrap.Popover.alignment[placement];
17583 this.el.alignTo(on_el, align[0],align[1]);
17584 //var arrow = this.el.select('.arrow',true).first();
17585 //arrow.set(align[2],
17587 this.el.addClass('in');
17590 if (this.el.hasClass('fade')) {
17594 this.hoverState = 'in';
17596 this.fireEvent('show', this);
17601 this.el.setXY([0,0]);
17602 this.el.removeClass('in');
17604 this.hoverState = null;
17606 this.fireEvent('hide', this);
17611 Roo.bootstrap.Popover.alignment = {
17612 'left' : ['r-l', [-10,0], 'right'],
17613 'right' : ['l-r', [10,0], 'left'],
17614 'bottom' : ['t-b', [0,10], 'top'],
17615 'top' : [ 'b-t', [0,-10], 'bottom']
17626 * @class Roo.bootstrap.Progress
17627 * @extends Roo.bootstrap.Component
17628 * Bootstrap Progress class
17629 * @cfg {Boolean} striped striped of the progress bar
17630 * @cfg {Boolean} active animated of the progress bar
17634 * Create a new Progress
17635 * @param {Object} config The config object
17638 Roo.bootstrap.Progress = function(config){
17639 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17642 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17647 getAutoCreate : function(){
17655 cfg.cls += ' progress-striped';
17659 cfg.cls += ' active';
17678 * @class Roo.bootstrap.ProgressBar
17679 * @extends Roo.bootstrap.Component
17680 * Bootstrap ProgressBar class
17681 * @cfg {Number} aria_valuenow aria-value now
17682 * @cfg {Number} aria_valuemin aria-value min
17683 * @cfg {Number} aria_valuemax aria-value max
17684 * @cfg {String} label label for the progress bar
17685 * @cfg {String} panel (success | info | warning | danger )
17686 * @cfg {String} role role of the progress bar
17687 * @cfg {String} sr_only text
17691 * Create a new ProgressBar
17692 * @param {Object} config The config object
17695 Roo.bootstrap.ProgressBar = function(config){
17696 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17699 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17703 aria_valuemax : 100,
17709 getAutoCreate : function()
17714 cls: 'progress-bar',
17715 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17727 cfg.role = this.role;
17730 if(this.aria_valuenow){
17731 cfg['aria-valuenow'] = this.aria_valuenow;
17734 if(this.aria_valuemin){
17735 cfg['aria-valuemin'] = this.aria_valuemin;
17738 if(this.aria_valuemax){
17739 cfg['aria-valuemax'] = this.aria_valuemax;
17742 if(this.label && !this.sr_only){
17743 cfg.html = this.label;
17747 cfg.cls += ' progress-bar-' + this.panel;
17753 update : function(aria_valuenow)
17755 this.aria_valuenow = aria_valuenow;
17757 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17772 * @class Roo.bootstrap.TabGroup
17773 * @extends Roo.bootstrap.Column
17774 * Bootstrap Column class
17775 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17776 * @cfg {Boolean} carousel true to make the group behave like a carousel
17777 * @cfg {Boolean} bullets show bullets for the panels
17778 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17779 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17780 * @cfg {Boolean} showarrow (true|false) show arrow default true
17783 * Create a new TabGroup
17784 * @param {Object} config The config object
17787 Roo.bootstrap.TabGroup = function(config){
17788 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17790 this.navId = Roo.id();
17793 Roo.bootstrap.TabGroup.register(this);
17797 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17800 transition : false,
17805 slideOnTouch : false,
17808 getAutoCreate : function()
17810 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17812 cfg.cls += ' tab-content';
17814 if (this.carousel) {
17815 cfg.cls += ' carousel slide';
17818 cls : 'carousel-inner',
17822 if(this.bullets && !Roo.isTouch){
17825 cls : 'carousel-bullets',
17829 if(this.bullets_cls){
17830 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17837 cfg.cn[0].cn.push(bullets);
17840 if(this.showarrow){
17841 cfg.cn[0].cn.push({
17843 class : 'carousel-arrow',
17847 class : 'carousel-prev',
17851 class : 'fa fa-chevron-left'
17857 class : 'carousel-next',
17861 class : 'fa fa-chevron-right'
17874 initEvents: function()
17876 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17877 // this.el.on("touchstart", this.onTouchStart, this);
17880 if(this.autoslide){
17883 this.slideFn = window.setInterval(function() {
17884 _this.showPanelNext();
17888 if(this.showarrow){
17889 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17890 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17896 // onTouchStart : function(e, el, o)
17898 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17902 // this.showPanelNext();
17906 getChildContainer : function()
17908 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17912 * register a Navigation item
17913 * @param {Roo.bootstrap.NavItem} the navitem to add
17915 register : function(item)
17917 this.tabs.push( item);
17918 item.navId = this.navId; // not really needed..
17923 getActivePanel : function()
17926 Roo.each(this.tabs, function(t) {
17936 getPanelByName : function(n)
17939 Roo.each(this.tabs, function(t) {
17940 if (t.tabId == n) {
17948 indexOfPanel : function(p)
17951 Roo.each(this.tabs, function(t,i) {
17952 if (t.tabId == p.tabId) {
17961 * show a specific panel
17962 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17963 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17965 showPanel : function (pan)
17967 if(this.transition || typeof(pan) == 'undefined'){
17968 Roo.log("waiting for the transitionend");
17972 if (typeof(pan) == 'number') {
17973 pan = this.tabs[pan];
17976 if (typeof(pan) == 'string') {
17977 pan = this.getPanelByName(pan);
17980 var cur = this.getActivePanel();
17983 Roo.log('pan or acitve pan is undefined');
17987 if (pan.tabId == this.getActivePanel().tabId) {
17991 if (false === cur.fireEvent('beforedeactivate')) {
17995 if(this.bullets > 0 && !Roo.isTouch){
17996 this.setActiveBullet(this.indexOfPanel(pan));
17999 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18001 this.transition = true;
18002 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18003 var lr = dir == 'next' ? 'left' : 'right';
18004 pan.el.addClass(dir); // or prev
18005 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18006 cur.el.addClass(lr); // or right
18007 pan.el.addClass(lr);
18010 cur.el.on('transitionend', function() {
18011 Roo.log("trans end?");
18013 pan.el.removeClass([lr,dir]);
18014 pan.setActive(true);
18016 cur.el.removeClass([lr]);
18017 cur.setActive(false);
18019 _this.transition = false;
18021 }, this, { single: true } );
18026 cur.setActive(false);
18027 pan.setActive(true);
18032 showPanelNext : function()
18034 var i = this.indexOfPanel(this.getActivePanel());
18036 if (i >= this.tabs.length - 1 && !this.autoslide) {
18040 if (i >= this.tabs.length - 1 && this.autoslide) {
18044 this.showPanel(this.tabs[i+1]);
18047 showPanelPrev : function()
18049 var i = this.indexOfPanel(this.getActivePanel());
18051 if (i < 1 && !this.autoslide) {
18055 if (i < 1 && this.autoslide) {
18056 i = this.tabs.length;
18059 this.showPanel(this.tabs[i-1]);
18063 addBullet: function()
18065 if(!this.bullets || Roo.isTouch){
18068 var ctr = this.el.select('.carousel-bullets',true).first();
18069 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18070 var bullet = ctr.createChild({
18071 cls : 'bullet bullet-' + i
18072 },ctr.dom.lastChild);
18077 bullet.on('click', (function(e, el, o, ii, t){
18079 e.preventDefault();
18081 this.showPanel(ii);
18083 if(this.autoslide && this.slideFn){
18084 clearInterval(this.slideFn);
18085 this.slideFn = window.setInterval(function() {
18086 _this.showPanelNext();
18090 }).createDelegate(this, [i, bullet], true));
18095 setActiveBullet : function(i)
18101 Roo.each(this.el.select('.bullet', true).elements, function(el){
18102 el.removeClass('selected');
18105 var bullet = this.el.select('.bullet-' + i, true).first();
18111 bullet.addClass('selected');
18122 Roo.apply(Roo.bootstrap.TabGroup, {
18126 * register a Navigation Group
18127 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18129 register : function(navgrp)
18131 this.groups[navgrp.navId] = navgrp;
18135 * fetch a Navigation Group based on the navigation ID
18136 * if one does not exist , it will get created.
18137 * @param {string} the navgroup to add
18138 * @returns {Roo.bootstrap.NavGroup} the navgroup
18140 get: function(navId) {
18141 if (typeof(this.groups[navId]) == 'undefined') {
18142 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18144 return this.groups[navId] ;
18159 * @class Roo.bootstrap.TabPanel
18160 * @extends Roo.bootstrap.Component
18161 * Bootstrap TabPanel class
18162 * @cfg {Boolean} active panel active
18163 * @cfg {String} html panel content
18164 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18165 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18166 * @cfg {String} href click to link..
18170 * Create a new TabPanel
18171 * @param {Object} config The config object
18174 Roo.bootstrap.TabPanel = function(config){
18175 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18179 * Fires when the active status changes
18180 * @param {Roo.bootstrap.TabPanel} this
18181 * @param {Boolean} state the new state
18186 * @event beforedeactivate
18187 * Fires before a tab is de-activated - can be used to do validation on a form.
18188 * @param {Roo.bootstrap.TabPanel} this
18189 * @return {Boolean} false if there is an error
18192 'beforedeactivate': true
18195 this.tabId = this.tabId || Roo.id();
18199 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18207 getAutoCreate : function(){
18210 // item is needed for carousel - not sure if it has any effect otherwise
18211 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18212 html: this.html || ''
18216 cfg.cls += ' active';
18220 cfg.tabId = this.tabId;
18227 initEvents: function()
18229 var p = this.parent();
18231 this.navId = this.navId || p.navId;
18233 if (typeof(this.navId) != 'undefined') {
18234 // not really needed.. but just in case.. parent should be a NavGroup.
18235 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18239 var i = tg.tabs.length - 1;
18241 if(this.active && tg.bullets > 0 && i < tg.bullets){
18242 tg.setActiveBullet(i);
18246 this.el.on('click', this.onClick, this);
18249 this.el.on("touchstart", this.onTouchStart, this);
18250 this.el.on("touchmove", this.onTouchMove, this);
18251 this.el.on("touchend", this.onTouchEnd, this);
18256 onRender : function(ct, position)
18258 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18261 setActive : function(state)
18263 Roo.log("panel - set active " + this.tabId + "=" + state);
18265 this.active = state;
18267 this.el.removeClass('active');
18269 } else if (!this.el.hasClass('active')) {
18270 this.el.addClass('active');
18273 this.fireEvent('changed', this, state);
18276 onClick : function(e)
18278 e.preventDefault();
18280 if(!this.href.length){
18284 window.location.href = this.href;
18293 onTouchStart : function(e)
18295 this.swiping = false;
18297 this.startX = e.browserEvent.touches[0].clientX;
18298 this.startY = e.browserEvent.touches[0].clientY;
18301 onTouchMove : function(e)
18303 this.swiping = true;
18305 this.endX = e.browserEvent.touches[0].clientX;
18306 this.endY = e.browserEvent.touches[0].clientY;
18309 onTouchEnd : function(e)
18316 var tabGroup = this.parent();
18318 if(this.endX > this.startX){ // swiping right
18319 tabGroup.showPanelPrev();
18323 if(this.startX > this.endX){ // swiping left
18324 tabGroup.showPanelNext();
18343 * @class Roo.bootstrap.DateField
18344 * @extends Roo.bootstrap.Input
18345 * Bootstrap DateField class
18346 * @cfg {Number} weekStart default 0
18347 * @cfg {String} viewMode default empty, (months|years)
18348 * @cfg {String} minViewMode default empty, (months|years)
18349 * @cfg {Number} startDate default -Infinity
18350 * @cfg {Number} endDate default Infinity
18351 * @cfg {Boolean} todayHighlight default false
18352 * @cfg {Boolean} todayBtn default false
18353 * @cfg {Boolean} calendarWeeks default false
18354 * @cfg {Object} daysOfWeekDisabled default empty
18355 * @cfg {Boolean} singleMode default false (true | false)
18357 * @cfg {Boolean} keyboardNavigation default true
18358 * @cfg {String} language default en
18361 * Create a new DateField
18362 * @param {Object} config The config object
18365 Roo.bootstrap.DateField = function(config){
18366 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18370 * Fires when this field show.
18371 * @param {Roo.bootstrap.DateField} this
18372 * @param {Mixed} date The date value
18377 * Fires when this field hide.
18378 * @param {Roo.bootstrap.DateField} this
18379 * @param {Mixed} date The date value
18384 * Fires when select a date.
18385 * @param {Roo.bootstrap.DateField} this
18386 * @param {Mixed} date The date value
18390 * @event beforeselect
18391 * Fires when before select a date.
18392 * @param {Roo.bootstrap.DateField} this
18393 * @param {Mixed} date The date value
18395 beforeselect : true
18399 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18402 * @cfg {String} format
18403 * The default date format string which can be overriden for localization support. The format must be
18404 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18408 * @cfg {String} altFormats
18409 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18410 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18412 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18420 todayHighlight : false,
18426 keyboardNavigation: true,
18428 calendarWeeks: false,
18430 startDate: -Infinity,
18434 daysOfWeekDisabled: [],
18438 singleMode : false,
18440 UTCDate: function()
18442 return new Date(Date.UTC.apply(Date, arguments));
18445 UTCToday: function()
18447 var today = new Date();
18448 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18451 getDate: function() {
18452 var d = this.getUTCDate();
18453 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18456 getUTCDate: function() {
18460 setDate: function(d) {
18461 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18464 setUTCDate: function(d) {
18466 this.setValue(this.formatDate(this.date));
18469 onRender: function(ct, position)
18472 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18474 this.language = this.language || 'en';
18475 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18476 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18478 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18479 this.format = this.format || 'm/d/y';
18480 this.isInline = false;
18481 this.isInput = true;
18482 this.component = this.el.select('.add-on', true).first() || false;
18483 this.component = (this.component && this.component.length === 0) ? false : this.component;
18484 this.hasInput = this.component && this.inputEl().length;
18486 if (typeof(this.minViewMode === 'string')) {
18487 switch (this.minViewMode) {
18489 this.minViewMode = 1;
18492 this.minViewMode = 2;
18495 this.minViewMode = 0;
18500 if (typeof(this.viewMode === 'string')) {
18501 switch (this.viewMode) {
18514 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18516 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18518 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18520 this.picker().on('mousedown', this.onMousedown, this);
18521 this.picker().on('click', this.onClick, this);
18523 this.picker().addClass('datepicker-dropdown');
18525 this.startViewMode = this.viewMode;
18527 if(this.singleMode){
18528 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18529 v.setVisibilityMode(Roo.Element.DISPLAY);
18533 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18534 v.setStyle('width', '189px');
18538 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18539 if(!this.calendarWeeks){
18544 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18545 v.attr('colspan', function(i, val){
18546 return parseInt(val) + 1;
18551 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18553 this.setStartDate(this.startDate);
18554 this.setEndDate(this.endDate);
18556 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18563 if(this.isInline) {
18568 picker : function()
18570 return this.pickerEl;
18571 // return this.el.select('.datepicker', true).first();
18574 fillDow: function()
18576 var dowCnt = this.weekStart;
18585 if(this.calendarWeeks){
18593 while (dowCnt < this.weekStart + 7) {
18597 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18601 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18604 fillMonths: function()
18607 var months = this.picker().select('>.datepicker-months td', true).first();
18609 months.dom.innerHTML = '';
18615 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18618 months.createChild(month);
18625 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;
18627 if (this.date < this.startDate) {
18628 this.viewDate = new Date(this.startDate);
18629 } else if (this.date > this.endDate) {
18630 this.viewDate = new Date(this.endDate);
18632 this.viewDate = new Date(this.date);
18640 var d = new Date(this.viewDate),
18641 year = d.getUTCFullYear(),
18642 month = d.getUTCMonth(),
18643 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18644 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18645 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18646 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18647 currentDate = this.date && this.date.valueOf(),
18648 today = this.UTCToday();
18650 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18652 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18654 // this.picker.select('>tfoot th.today').
18655 // .text(dates[this.language].today)
18656 // .toggle(this.todayBtn !== false);
18658 this.updateNavArrows();
18661 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18663 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18665 prevMonth.setUTCDate(day);
18667 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18669 var nextMonth = new Date(prevMonth);
18671 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18673 nextMonth = nextMonth.valueOf();
18675 var fillMonths = false;
18677 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18679 while(prevMonth.valueOf() <= nextMonth) {
18682 if (prevMonth.getUTCDay() === this.weekStart) {
18684 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18692 if(this.calendarWeeks){
18693 // ISO 8601: First week contains first thursday.
18694 // ISO also states week starts on Monday, but we can be more abstract here.
18696 // Start of current week: based on weekstart/current date
18697 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18698 // Thursday of this week
18699 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18700 // First Thursday of year, year from thursday
18701 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18702 // Calendar week: ms between thursdays, div ms per day, div 7 days
18703 calWeek = (th - yth) / 864e5 / 7 + 1;
18705 fillMonths.cn.push({
18713 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18715 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18718 if (this.todayHighlight &&
18719 prevMonth.getUTCFullYear() == today.getFullYear() &&
18720 prevMonth.getUTCMonth() == today.getMonth() &&
18721 prevMonth.getUTCDate() == today.getDate()) {
18722 clsName += ' today';
18725 if (currentDate && prevMonth.valueOf() === currentDate) {
18726 clsName += ' active';
18729 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18730 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18731 clsName += ' disabled';
18734 fillMonths.cn.push({
18736 cls: 'day ' + clsName,
18737 html: prevMonth.getDate()
18740 prevMonth.setDate(prevMonth.getDate()+1);
18743 var currentYear = this.date && this.date.getUTCFullYear();
18744 var currentMonth = this.date && this.date.getUTCMonth();
18746 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18748 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18749 v.removeClass('active');
18751 if(currentYear === year && k === currentMonth){
18752 v.addClass('active');
18755 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18756 v.addClass('disabled');
18762 year = parseInt(year/10, 10) * 10;
18764 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18766 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18769 for (var i = -1; i < 11; i++) {
18770 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18772 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18780 showMode: function(dir)
18783 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18786 Roo.each(this.picker().select('>div',true).elements, function(v){
18787 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18790 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18795 if(this.isInline) {
18799 this.picker().removeClass(['bottom', 'top']);
18801 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18803 * place to the top of element!
18807 this.picker().addClass('top');
18808 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18813 this.picker().addClass('bottom');
18815 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18818 parseDate : function(value)
18820 if(!value || value instanceof Date){
18823 var v = Date.parseDate(value, this.format);
18824 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18825 v = Date.parseDate(value, 'Y-m-d');
18827 if(!v && this.altFormats){
18828 if(!this.altFormatsArray){
18829 this.altFormatsArray = this.altFormats.split("|");
18831 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18832 v = Date.parseDate(value, this.altFormatsArray[i]);
18838 formatDate : function(date, fmt)
18840 return (!date || !(date instanceof Date)) ?
18841 date : date.dateFormat(fmt || this.format);
18844 onFocus : function()
18846 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18850 onBlur : function()
18852 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18854 var d = this.inputEl().getValue();
18861 showPopup : function()
18863 this.picker().show();
18867 this.fireEvent('showpopup', this, this.date);
18870 hidePopup : function()
18872 if(this.isInline) {
18875 this.picker().hide();
18876 this.viewMode = this.startViewMode;
18879 this.fireEvent('hidepopup', this, this.date);
18883 onMousedown: function(e)
18885 e.stopPropagation();
18886 e.preventDefault();
18891 Roo.bootstrap.DateField.superclass.keyup.call(this);
18895 setValue: function(v)
18897 if(this.fireEvent('beforeselect', this, v) !== false){
18898 var d = new Date(this.parseDate(v) ).clearTime();
18900 if(isNaN(d.getTime())){
18901 this.date = this.viewDate = '';
18902 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18906 v = this.formatDate(d);
18908 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18910 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18914 this.fireEvent('select', this, this.date);
18918 getValue: function()
18920 return this.formatDate(this.date);
18923 fireKey: function(e)
18925 if (!this.picker().isVisible()){
18926 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18932 var dateChanged = false,
18934 newDate, newViewDate;
18939 e.preventDefault();
18943 if (!this.keyboardNavigation) {
18946 dir = e.keyCode == 37 ? -1 : 1;
18949 newDate = this.moveYear(this.date, dir);
18950 newViewDate = this.moveYear(this.viewDate, dir);
18951 } else if (e.shiftKey){
18952 newDate = this.moveMonth(this.date, dir);
18953 newViewDate = this.moveMonth(this.viewDate, dir);
18955 newDate = new Date(this.date);
18956 newDate.setUTCDate(this.date.getUTCDate() + dir);
18957 newViewDate = new Date(this.viewDate);
18958 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18960 if (this.dateWithinRange(newDate)){
18961 this.date = newDate;
18962 this.viewDate = newViewDate;
18963 this.setValue(this.formatDate(this.date));
18965 e.preventDefault();
18966 dateChanged = true;
18971 if (!this.keyboardNavigation) {
18974 dir = e.keyCode == 38 ? -1 : 1;
18976 newDate = this.moveYear(this.date, dir);
18977 newViewDate = this.moveYear(this.viewDate, dir);
18978 } else if (e.shiftKey){
18979 newDate = this.moveMonth(this.date, dir);
18980 newViewDate = this.moveMonth(this.viewDate, dir);
18982 newDate = new Date(this.date);
18983 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18984 newViewDate = new Date(this.viewDate);
18985 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18987 if (this.dateWithinRange(newDate)){
18988 this.date = newDate;
18989 this.viewDate = newViewDate;
18990 this.setValue(this.formatDate(this.date));
18992 e.preventDefault();
18993 dateChanged = true;
18997 this.setValue(this.formatDate(this.date));
18999 e.preventDefault();
19002 this.setValue(this.formatDate(this.date));
19016 onClick: function(e)
19018 e.stopPropagation();
19019 e.preventDefault();
19021 var target = e.getTarget();
19023 if(target.nodeName.toLowerCase() === 'i'){
19024 target = Roo.get(target).dom.parentNode;
19027 var nodeName = target.nodeName;
19028 var className = target.className;
19029 var html = target.innerHTML;
19030 //Roo.log(nodeName);
19032 switch(nodeName.toLowerCase()) {
19034 switch(className) {
19040 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19041 switch(this.viewMode){
19043 this.viewDate = this.moveMonth(this.viewDate, dir);
19047 this.viewDate = this.moveYear(this.viewDate, dir);
19053 var date = new Date();
19054 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19056 this.setValue(this.formatDate(this.date));
19063 if (className.indexOf('disabled') < 0) {
19064 this.viewDate.setUTCDate(1);
19065 if (className.indexOf('month') > -1) {
19066 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19068 var year = parseInt(html, 10) || 0;
19069 this.viewDate.setUTCFullYear(year);
19073 if(this.singleMode){
19074 this.setValue(this.formatDate(this.viewDate));
19085 //Roo.log(className);
19086 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19087 var day = parseInt(html, 10) || 1;
19088 var year = this.viewDate.getUTCFullYear(),
19089 month = this.viewDate.getUTCMonth();
19091 if (className.indexOf('old') > -1) {
19098 } else if (className.indexOf('new') > -1) {
19106 //Roo.log([year,month,day]);
19107 this.date = this.UTCDate(year, month, day,0,0,0,0);
19108 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19110 //Roo.log(this.formatDate(this.date));
19111 this.setValue(this.formatDate(this.date));
19118 setStartDate: function(startDate)
19120 this.startDate = startDate || -Infinity;
19121 if (this.startDate !== -Infinity) {
19122 this.startDate = this.parseDate(this.startDate);
19125 this.updateNavArrows();
19128 setEndDate: function(endDate)
19130 this.endDate = endDate || Infinity;
19131 if (this.endDate !== Infinity) {
19132 this.endDate = this.parseDate(this.endDate);
19135 this.updateNavArrows();
19138 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19140 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19141 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19142 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19144 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19145 return parseInt(d, 10);
19148 this.updateNavArrows();
19151 updateNavArrows: function()
19153 if(this.singleMode){
19157 var d = new Date(this.viewDate),
19158 year = d.getUTCFullYear(),
19159 month = d.getUTCMonth();
19161 Roo.each(this.picker().select('.prev', true).elements, function(v){
19163 switch (this.viewMode) {
19166 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19172 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19179 Roo.each(this.picker().select('.next', true).elements, function(v){
19181 switch (this.viewMode) {
19184 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19190 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19198 moveMonth: function(date, dir)
19203 var new_date = new Date(date.valueOf()),
19204 day = new_date.getUTCDate(),
19205 month = new_date.getUTCMonth(),
19206 mag = Math.abs(dir),
19208 dir = dir > 0 ? 1 : -1;
19211 // If going back one month, make sure month is not current month
19212 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19214 return new_date.getUTCMonth() == month;
19216 // If going forward one month, make sure month is as expected
19217 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19219 return new_date.getUTCMonth() != new_month;
19221 new_month = month + dir;
19222 new_date.setUTCMonth(new_month);
19223 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19224 if (new_month < 0 || new_month > 11) {
19225 new_month = (new_month + 12) % 12;
19228 // For magnitudes >1, move one month at a time...
19229 for (var i=0; i<mag; i++) {
19230 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19231 new_date = this.moveMonth(new_date, dir);
19233 // ...then reset the day, keeping it in the new month
19234 new_month = new_date.getUTCMonth();
19235 new_date.setUTCDate(day);
19237 return new_month != new_date.getUTCMonth();
19240 // Common date-resetting loop -- if date is beyond end of month, make it
19243 new_date.setUTCDate(--day);
19244 new_date.setUTCMonth(new_month);
19249 moveYear: function(date, dir)
19251 return this.moveMonth(date, dir*12);
19254 dateWithinRange: function(date)
19256 return date >= this.startDate && date <= this.endDate;
19262 this.picker().remove();
19265 validateValue : function(value)
19267 if(this.getVisibilityEl().hasClass('hidden')){
19271 if(value.length < 1) {
19272 if(this.allowBlank){
19278 if(value.length < this.minLength){
19281 if(value.length > this.maxLength){
19285 var vt = Roo.form.VTypes;
19286 if(!vt[this.vtype](value, this)){
19290 if(typeof this.validator == "function"){
19291 var msg = this.validator(value);
19297 if(this.regex && !this.regex.test(value)){
19301 if(typeof(this.parseDate(value)) == 'undefined'){
19305 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19309 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19319 this.date = this.viewDate = '';
19321 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19326 Roo.apply(Roo.bootstrap.DateField, {
19337 html: '<i class="fa fa-arrow-left"/>'
19347 html: '<i class="fa fa-arrow-right"/>'
19389 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19390 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19391 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19392 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19393 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19406 navFnc: 'FullYear',
19411 navFnc: 'FullYear',
19416 Roo.apply(Roo.bootstrap.DateField, {
19420 cls: 'datepicker dropdown-menu roo-dynamic',
19424 cls: 'datepicker-days',
19428 cls: 'table-condensed',
19430 Roo.bootstrap.DateField.head,
19434 Roo.bootstrap.DateField.footer
19441 cls: 'datepicker-months',
19445 cls: 'table-condensed',
19447 Roo.bootstrap.DateField.head,
19448 Roo.bootstrap.DateField.content,
19449 Roo.bootstrap.DateField.footer
19456 cls: 'datepicker-years',
19460 cls: 'table-condensed',
19462 Roo.bootstrap.DateField.head,
19463 Roo.bootstrap.DateField.content,
19464 Roo.bootstrap.DateField.footer
19483 * @class Roo.bootstrap.TimeField
19484 * @extends Roo.bootstrap.Input
19485 * Bootstrap DateField class
19489 * Create a new TimeField
19490 * @param {Object} config The config object
19493 Roo.bootstrap.TimeField = function(config){
19494 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19498 * Fires when this field show.
19499 * @param {Roo.bootstrap.DateField} thisthis
19500 * @param {Mixed} date The date value
19505 * Fires when this field hide.
19506 * @param {Roo.bootstrap.DateField} this
19507 * @param {Mixed} date The date value
19512 * Fires when select a date.
19513 * @param {Roo.bootstrap.DateField} this
19514 * @param {Mixed} date The date value
19520 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19523 * @cfg {String} format
19524 * The default time format string which can be overriden for localization support. The format must be
19525 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19529 onRender: function(ct, position)
19532 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19534 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19536 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19538 this.pop = this.picker().select('>.datepicker-time',true).first();
19539 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19541 this.picker().on('mousedown', this.onMousedown, this);
19542 this.picker().on('click', this.onClick, this);
19544 this.picker().addClass('datepicker-dropdown');
19549 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19550 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19551 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19552 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19553 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19554 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19558 fireKey: function(e){
19559 if (!this.picker().isVisible()){
19560 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19566 e.preventDefault();
19574 this.onTogglePeriod();
19577 this.onIncrementMinutes();
19580 this.onDecrementMinutes();
19589 onClick: function(e) {
19590 e.stopPropagation();
19591 e.preventDefault();
19594 picker : function()
19596 return this.el.select('.datepicker', true).first();
19599 fillTime: function()
19601 var time = this.pop.select('tbody', true).first();
19603 time.dom.innerHTML = '';
19618 cls: 'hours-up glyphicon glyphicon-chevron-up'
19638 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19659 cls: 'timepicker-hour',
19674 cls: 'timepicker-minute',
19689 cls: 'btn btn-primary period',
19711 cls: 'hours-down glyphicon glyphicon-chevron-down'
19731 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19749 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19756 var hours = this.time.getHours();
19757 var minutes = this.time.getMinutes();
19770 hours = hours - 12;
19774 hours = '0' + hours;
19778 minutes = '0' + minutes;
19781 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19782 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19783 this.pop.select('button', true).first().dom.innerHTML = period;
19789 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19791 var cls = ['bottom'];
19793 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19800 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19805 this.picker().addClass(cls.join('-'));
19809 Roo.each(cls, function(c){
19811 _this.picker().setTop(_this.inputEl().getHeight());
19815 _this.picker().setTop(0 - _this.picker().getHeight());
19820 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19824 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19831 onFocus : function()
19833 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19837 onBlur : function()
19839 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19845 this.picker().show();
19850 this.fireEvent('show', this, this.date);
19855 this.picker().hide();
19858 this.fireEvent('hide', this, this.date);
19861 setTime : function()
19864 this.setValue(this.time.format(this.format));
19866 this.fireEvent('select', this, this.date);
19871 onMousedown: function(e){
19872 e.stopPropagation();
19873 e.preventDefault();
19876 onIncrementHours: function()
19878 Roo.log('onIncrementHours');
19879 this.time = this.time.add(Date.HOUR, 1);
19884 onDecrementHours: function()
19886 Roo.log('onDecrementHours');
19887 this.time = this.time.add(Date.HOUR, -1);
19891 onIncrementMinutes: function()
19893 Roo.log('onIncrementMinutes');
19894 this.time = this.time.add(Date.MINUTE, 1);
19898 onDecrementMinutes: function()
19900 Roo.log('onDecrementMinutes');
19901 this.time = this.time.add(Date.MINUTE, -1);
19905 onTogglePeriod: function()
19907 Roo.log('onTogglePeriod');
19908 this.time = this.time.add(Date.HOUR, 12);
19915 Roo.apply(Roo.bootstrap.TimeField, {
19945 cls: 'btn btn-info ok',
19957 Roo.apply(Roo.bootstrap.TimeField, {
19961 cls: 'datepicker dropdown-menu',
19965 cls: 'datepicker-time',
19969 cls: 'table-condensed',
19971 Roo.bootstrap.TimeField.content,
19972 Roo.bootstrap.TimeField.footer
19991 * @class Roo.bootstrap.MonthField
19992 * @extends Roo.bootstrap.Input
19993 * Bootstrap MonthField class
19995 * @cfg {String} language default en
19998 * Create a new MonthField
19999 * @param {Object} config The config object
20002 Roo.bootstrap.MonthField = function(config){
20003 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20008 * Fires when this field show.
20009 * @param {Roo.bootstrap.MonthField} this
20010 * @param {Mixed} date The date value
20015 * Fires when this field hide.
20016 * @param {Roo.bootstrap.MonthField} this
20017 * @param {Mixed} date The date value
20022 * Fires when select a date.
20023 * @param {Roo.bootstrap.MonthField} this
20024 * @param {String} oldvalue The old value
20025 * @param {String} newvalue The new value
20031 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20033 onRender: function(ct, position)
20036 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20038 this.language = this.language || 'en';
20039 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20040 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20042 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20043 this.isInline = false;
20044 this.isInput = true;
20045 this.component = this.el.select('.add-on', true).first() || false;
20046 this.component = (this.component && this.component.length === 0) ? false : this.component;
20047 this.hasInput = this.component && this.inputEL().length;
20049 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20051 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20053 this.picker().on('mousedown', this.onMousedown, this);
20054 this.picker().on('click', this.onClick, this);
20056 this.picker().addClass('datepicker-dropdown');
20058 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20059 v.setStyle('width', '189px');
20066 if(this.isInline) {
20072 setValue: function(v, suppressEvent)
20074 var o = this.getValue();
20076 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20080 if(suppressEvent !== true){
20081 this.fireEvent('select', this, o, v);
20086 getValue: function()
20091 onClick: function(e)
20093 e.stopPropagation();
20094 e.preventDefault();
20096 var target = e.getTarget();
20098 if(target.nodeName.toLowerCase() === 'i'){
20099 target = Roo.get(target).dom.parentNode;
20102 var nodeName = target.nodeName;
20103 var className = target.className;
20104 var html = target.innerHTML;
20106 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20110 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20112 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20118 picker : function()
20120 return this.pickerEl;
20123 fillMonths: function()
20126 var months = this.picker().select('>.datepicker-months td', true).first();
20128 months.dom.innerHTML = '';
20134 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20137 months.createChild(month);
20146 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20147 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20150 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20151 e.removeClass('active');
20153 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20154 e.addClass('active');
20161 if(this.isInline) {
20165 this.picker().removeClass(['bottom', 'top']);
20167 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20169 * place to the top of element!
20173 this.picker().addClass('top');
20174 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20179 this.picker().addClass('bottom');
20181 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20184 onFocus : function()
20186 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20190 onBlur : function()
20192 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20194 var d = this.inputEl().getValue();
20203 this.picker().show();
20204 this.picker().select('>.datepicker-months', true).first().show();
20208 this.fireEvent('show', this, this.date);
20213 if(this.isInline) {
20216 this.picker().hide();
20217 this.fireEvent('hide', this, this.date);
20221 onMousedown: function(e)
20223 e.stopPropagation();
20224 e.preventDefault();
20229 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20233 fireKey: function(e)
20235 if (!this.picker().isVisible()){
20236 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20247 e.preventDefault();
20251 dir = e.keyCode == 37 ? -1 : 1;
20253 this.vIndex = this.vIndex + dir;
20255 if(this.vIndex < 0){
20259 if(this.vIndex > 11){
20263 if(isNaN(this.vIndex)){
20267 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20273 dir = e.keyCode == 38 ? -1 : 1;
20275 this.vIndex = this.vIndex + dir * 4;
20277 if(this.vIndex < 0){
20281 if(this.vIndex > 11){
20285 if(isNaN(this.vIndex)){
20289 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20294 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20295 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20299 e.preventDefault();
20302 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20303 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20319 this.picker().remove();
20324 Roo.apply(Roo.bootstrap.MonthField, {
20343 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20344 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20349 Roo.apply(Roo.bootstrap.MonthField, {
20353 cls: 'datepicker dropdown-menu roo-dynamic',
20357 cls: 'datepicker-months',
20361 cls: 'table-condensed',
20363 Roo.bootstrap.DateField.content
20383 * @class Roo.bootstrap.CheckBox
20384 * @extends Roo.bootstrap.Input
20385 * Bootstrap CheckBox class
20387 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20388 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20389 * @cfg {String} boxLabel The text that appears beside the checkbox
20390 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20391 * @cfg {Boolean} checked initnal the element
20392 * @cfg {Boolean} inline inline the element (default false)
20393 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20394 * @cfg {String} tooltip label tooltip
20397 * Create a new CheckBox
20398 * @param {Object} config The config object
20401 Roo.bootstrap.CheckBox = function(config){
20402 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20407 * Fires when the element is checked or unchecked.
20408 * @param {Roo.bootstrap.CheckBox} this This input
20409 * @param {Boolean} checked The new checked value
20414 * Fires when the element is click.
20415 * @param {Roo.bootstrap.CheckBox} this This input
20422 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20424 inputType: 'checkbox',
20433 getAutoCreate : function()
20435 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20441 cfg.cls = 'form-group ' + this.inputType; //input-group
20444 cfg.cls += ' ' + this.inputType + '-inline';
20450 type : this.inputType,
20451 value : this.inputValue,
20452 cls : 'roo-' + this.inputType, //'form-box',
20453 placeholder : this.placeholder || ''
20457 if(this.inputType != 'radio'){
20461 cls : 'roo-hidden-value',
20462 value : this.checked ? this.inputValue : this.valueOff
20467 if (this.weight) { // Validity check?
20468 cfg.cls += " " + this.inputType + "-" + this.weight;
20471 if (this.disabled) {
20472 input.disabled=true;
20476 input.checked = this.checked;
20481 input.name = this.name;
20483 if(this.inputType != 'radio'){
20484 hidden.name = this.name;
20485 input.name = '_hidden_' + this.name;
20490 input.cls += ' input-' + this.size;
20495 ['xs','sm','md','lg'].map(function(size){
20496 if (settings[size]) {
20497 cfg.cls += ' col-' + size + '-' + settings[size];
20501 var inputblock = input;
20503 if (this.before || this.after) {
20506 cls : 'input-group',
20511 inputblock.cn.push({
20513 cls : 'input-group-addon',
20518 inputblock.cn.push(input);
20520 if(this.inputType != 'radio'){
20521 inputblock.cn.push(hidden);
20525 inputblock.cn.push({
20527 cls : 'input-group-addon',
20534 if (align ==='left' && this.fieldLabel.length) {
20535 // Roo.log("left and has label");
20540 cls : 'control-label',
20541 html : this.fieldLabel
20551 if(this.labelWidth > 12){
20552 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20555 if(this.labelWidth < 13 && this.labelmd == 0){
20556 this.labelmd = this.labelWidth;
20559 if(this.labellg > 0){
20560 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20561 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20564 if(this.labelmd > 0){
20565 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20566 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20569 if(this.labelsm > 0){
20570 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20571 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20574 if(this.labelxs > 0){
20575 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20576 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20579 } else if ( this.fieldLabel.length) {
20580 // Roo.log(" label");
20584 tag: this.boxLabel ? 'span' : 'label',
20586 cls: 'control-label box-input-label',
20587 //cls : 'input-group-addon',
20588 html : this.fieldLabel
20597 // Roo.log(" no label && no align");
20598 cfg.cn = [ inputblock ] ;
20604 var boxLabelCfg = {
20606 //'for': id, // box label is handled by onclick - so no for...
20608 html: this.boxLabel
20612 boxLabelCfg.tooltip = this.tooltip;
20615 cfg.cn.push(boxLabelCfg);
20618 if(this.inputType != 'radio'){
20619 cfg.cn.push(hidden);
20627 * return the real input element.
20629 inputEl: function ()
20631 return this.el.select('input.roo-' + this.inputType,true).first();
20633 hiddenEl: function ()
20635 return this.el.select('input.roo-hidden-value',true).first();
20638 labelEl: function()
20640 return this.el.select('label.control-label',true).first();
20642 /* depricated... */
20646 return this.labelEl();
20649 boxLabelEl: function()
20651 return this.el.select('label.box-label',true).first();
20654 initEvents : function()
20656 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20658 this.inputEl().on('click', this.onClick, this);
20660 if (this.boxLabel) {
20661 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20664 this.startValue = this.getValue();
20667 Roo.bootstrap.CheckBox.register(this);
20671 onClick : function(e)
20673 if(this.fireEvent('click', this, e) !== false){
20674 this.setChecked(!this.checked);
20679 setChecked : function(state,suppressEvent)
20681 this.startValue = this.getValue();
20683 if(this.inputType == 'radio'){
20685 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20686 e.dom.checked = false;
20689 this.inputEl().dom.checked = true;
20691 this.inputEl().dom.value = this.inputValue;
20693 if(suppressEvent !== true){
20694 this.fireEvent('check', this, true);
20702 this.checked = state;
20704 this.inputEl().dom.checked = state;
20707 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20709 if(suppressEvent !== true){
20710 this.fireEvent('check', this, state);
20716 getValue : function()
20718 if(this.inputType == 'radio'){
20719 return this.getGroupValue();
20722 return this.hiddenEl().dom.value;
20726 getGroupValue : function()
20728 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20732 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20735 setValue : function(v,suppressEvent)
20737 if(this.inputType == 'radio'){
20738 this.setGroupValue(v, suppressEvent);
20742 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20747 setGroupValue : function(v, suppressEvent)
20749 this.startValue = this.getValue();
20751 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20752 e.dom.checked = false;
20754 if(e.dom.value == v){
20755 e.dom.checked = true;
20759 if(suppressEvent !== true){
20760 this.fireEvent('check', this, true);
20768 validate : function()
20770 if(this.getVisibilityEl().hasClass('hidden')){
20776 (this.inputType == 'radio' && this.validateRadio()) ||
20777 (this.inputType == 'checkbox' && this.validateCheckbox())
20783 this.markInvalid();
20787 validateRadio : function()
20789 if(this.getVisibilityEl().hasClass('hidden')){
20793 if(this.allowBlank){
20799 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20800 if(!e.dom.checked){
20812 validateCheckbox : function()
20815 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20816 //return (this.getValue() == this.inputValue) ? true : false;
20819 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20827 for(var i in group){
20828 if(group[i].el.isVisible(true)){
20836 for(var i in group){
20841 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20848 * Mark this field as valid
20850 markValid : function()
20854 this.fireEvent('valid', this);
20856 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20859 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20866 if(this.inputType == 'radio'){
20867 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20868 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20869 e.findParent('.form-group', false, true).addClass(_this.validClass);
20876 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20877 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20881 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20887 for(var i in group){
20888 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20889 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20894 * Mark this field as invalid
20895 * @param {String} msg The validation message
20897 markInvalid : function(msg)
20899 if(this.allowBlank){
20905 this.fireEvent('invalid', this, msg);
20907 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20910 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20914 label.markInvalid();
20917 if(this.inputType == 'radio'){
20918 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20919 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20920 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20927 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20928 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20932 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20938 for(var i in group){
20939 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20940 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20945 clearInvalid : function()
20947 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20949 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20951 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20953 if (label && label.iconEl) {
20954 label.iconEl.removeClass(label.validClass);
20955 label.iconEl.removeClass(label.invalidClass);
20959 disable : function()
20961 if(this.inputType != 'radio'){
20962 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20969 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20970 _this.getActionEl().addClass(this.disabledClass);
20971 e.dom.disabled = true;
20975 this.disabled = true;
20976 this.fireEvent("disable", this);
20980 enable : function()
20982 if(this.inputType != 'radio'){
20983 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20990 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20991 _this.getActionEl().removeClass(this.disabledClass);
20992 e.dom.disabled = false;
20996 this.disabled = false;
20997 this.fireEvent("enable", this);
21001 setBoxLabel : function(v)
21006 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21012 Roo.apply(Roo.bootstrap.CheckBox, {
21017 * register a CheckBox Group
21018 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21020 register : function(checkbox)
21022 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21023 this.groups[checkbox.groupId] = {};
21026 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21030 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21034 * fetch a CheckBox Group based on the group ID
21035 * @param {string} the group ID
21036 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21038 get: function(groupId) {
21039 if (typeof(this.groups[groupId]) == 'undefined') {
21043 return this.groups[groupId] ;
21056 * @class Roo.bootstrap.Radio
21057 * @extends Roo.bootstrap.Component
21058 * Bootstrap Radio class
21059 * @cfg {String} boxLabel - the label associated
21060 * @cfg {String} value - the value of radio
21063 * Create a new Radio
21064 * @param {Object} config The config object
21066 Roo.bootstrap.Radio = function(config){
21067 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21071 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21077 getAutoCreate : function()
21081 cls : 'form-group radio',
21086 html : this.boxLabel
21094 initEvents : function()
21096 this.parent().register(this);
21098 this.el.on('click', this.onClick, this);
21102 onClick : function(e)
21104 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21105 this.setChecked(true);
21109 setChecked : function(state, suppressEvent)
21111 this.parent().setValue(this.value, suppressEvent);
21115 setBoxLabel : function(v)
21120 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21135 * @class Roo.bootstrap.SecurePass
21136 * @extends Roo.bootstrap.Input
21137 * Bootstrap SecurePass class
21141 * Create a new SecurePass
21142 * @param {Object} config The config object
21145 Roo.bootstrap.SecurePass = function (config) {
21146 // these go here, so the translation tool can replace them..
21148 PwdEmpty: "Please type a password, and then retype it to confirm.",
21149 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21150 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21151 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21152 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21153 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21154 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21155 TooWeak: "Your password is Too Weak."
21157 this.meterLabel = "Password strength:";
21158 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21159 this.meterClass = [
21160 "roo-password-meter-tooweak",
21161 "roo-password-meter-weak",
21162 "roo-password-meter-medium",
21163 "roo-password-meter-strong",
21164 "roo-password-meter-grey"
21169 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21172 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21174 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21176 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21177 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21178 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21179 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21180 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21181 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21182 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21192 * @cfg {String/Object} Label for the strength meter (defaults to
21193 * 'Password strength:')
21198 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21199 * ['Weak', 'Medium', 'Strong'])
21202 pwdStrengths: false,
21215 initEvents: function ()
21217 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21219 if (this.el.is('input[type=password]') && Roo.isSafari) {
21220 this.el.on('keydown', this.SafariOnKeyDown, this);
21223 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21226 onRender: function (ct, position)
21228 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21229 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21230 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21232 this.trigger.createChild({
21237 cls: 'roo-password-meter-grey col-xs-12',
21240 //width: this.meterWidth + 'px'
21244 cls: 'roo-password-meter-text'
21250 if (this.hideTrigger) {
21251 this.trigger.setDisplayed(false);
21253 this.setSize(this.width || '', this.height || '');
21256 onDestroy: function ()
21258 if (this.trigger) {
21259 this.trigger.removeAllListeners();
21260 this.trigger.remove();
21263 this.wrap.remove();
21265 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21268 checkStrength: function ()
21270 var pwd = this.inputEl().getValue();
21271 if (pwd == this._lastPwd) {
21276 if (this.ClientSideStrongPassword(pwd)) {
21278 } else if (this.ClientSideMediumPassword(pwd)) {
21280 } else if (this.ClientSideWeakPassword(pwd)) {
21286 Roo.log('strength1: ' + strength);
21288 //var pm = this.trigger.child('div/div/div').dom;
21289 var pm = this.trigger.child('div/div');
21290 pm.removeClass(this.meterClass);
21291 pm.addClass(this.meterClass[strength]);
21294 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21296 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21298 this._lastPwd = pwd;
21302 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21304 this._lastPwd = '';
21306 var pm = this.trigger.child('div/div');
21307 pm.removeClass(this.meterClass);
21308 pm.addClass('roo-password-meter-grey');
21311 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21314 this.inputEl().dom.type='password';
21317 validateValue: function (value)
21320 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21323 if (value.length == 0) {
21324 if (this.allowBlank) {
21325 this.clearInvalid();
21329 this.markInvalid(this.errors.PwdEmpty);
21330 this.errorMsg = this.errors.PwdEmpty;
21338 if ('[\x21-\x7e]*'.match(value)) {
21339 this.markInvalid(this.errors.PwdBadChar);
21340 this.errorMsg = this.errors.PwdBadChar;
21343 if (value.length < 6) {
21344 this.markInvalid(this.errors.PwdShort);
21345 this.errorMsg = this.errors.PwdShort;
21348 if (value.length > 16) {
21349 this.markInvalid(this.errors.PwdLong);
21350 this.errorMsg = this.errors.PwdLong;
21354 if (this.ClientSideStrongPassword(value)) {
21356 } else if (this.ClientSideMediumPassword(value)) {
21358 } else if (this.ClientSideWeakPassword(value)) {
21365 if (strength < 2) {
21366 //this.markInvalid(this.errors.TooWeak);
21367 this.errorMsg = this.errors.TooWeak;
21372 console.log('strength2: ' + strength);
21374 //var pm = this.trigger.child('div/div/div').dom;
21376 var pm = this.trigger.child('div/div');
21377 pm.removeClass(this.meterClass);
21378 pm.addClass(this.meterClass[strength]);
21380 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21382 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21384 this.errorMsg = '';
21388 CharacterSetChecks: function (type)
21391 this.fResult = false;
21394 isctype: function (character, type)
21397 case this.kCapitalLetter:
21398 if (character >= 'A' && character <= 'Z') {
21403 case this.kSmallLetter:
21404 if (character >= 'a' && character <= 'z') {
21410 if (character >= '0' && character <= '9') {
21415 case this.kPunctuation:
21416 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21427 IsLongEnough: function (pwd, size)
21429 return !(pwd == null || isNaN(size) || pwd.length < size);
21432 SpansEnoughCharacterSets: function (word, nb)
21434 if (!this.IsLongEnough(word, nb))
21439 var characterSetChecks = new Array(
21440 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21441 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21444 for (var index = 0; index < word.length; ++index) {
21445 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21446 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21447 characterSetChecks[nCharSet].fResult = true;
21454 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21455 if (characterSetChecks[nCharSet].fResult) {
21460 if (nCharSets < nb) {
21466 ClientSideStrongPassword: function (pwd)
21468 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21471 ClientSideMediumPassword: function (pwd)
21473 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21476 ClientSideWeakPassword: function (pwd)
21478 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21481 })//<script type="text/javascript">
21484 * Based Ext JS Library 1.1.1
21485 * Copyright(c) 2006-2007, Ext JS, LLC.
21491 * @class Roo.HtmlEditorCore
21492 * @extends Roo.Component
21493 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21495 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21498 Roo.HtmlEditorCore = function(config){
21501 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21506 * @event initialize
21507 * Fires when the editor is fully initialized (including the iframe)
21508 * @param {Roo.HtmlEditorCore} this
21513 * Fires when the editor is first receives the focus. Any insertion must wait
21514 * until after this event.
21515 * @param {Roo.HtmlEditorCore} this
21519 * @event beforesync
21520 * Fires before the textarea is updated with content from the editor iframe. Return false
21521 * to cancel the sync.
21522 * @param {Roo.HtmlEditorCore} this
21523 * @param {String} html
21527 * @event beforepush
21528 * Fires before the iframe editor is updated with content from the textarea. Return false
21529 * to cancel the push.
21530 * @param {Roo.HtmlEditorCore} this
21531 * @param {String} html
21536 * Fires when the textarea is updated with content from the editor iframe.
21537 * @param {Roo.HtmlEditorCore} this
21538 * @param {String} html
21543 * Fires when the iframe editor is updated with content from the textarea.
21544 * @param {Roo.HtmlEditorCore} this
21545 * @param {String} html
21550 * @event editorevent
21551 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21552 * @param {Roo.HtmlEditorCore} this
21558 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21560 // defaults : white / black...
21561 this.applyBlacklists();
21568 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21572 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21578 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21583 * @cfg {Number} height (in pixels)
21587 * @cfg {Number} width (in pixels)
21592 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21595 stylesheets: false,
21600 // private properties
21601 validationEvent : false,
21603 initialized : false,
21605 sourceEditMode : false,
21606 onFocus : Roo.emptyFn,
21608 hideMode:'offsets',
21612 // blacklist + whitelisted elements..
21619 * Protected method that will not generally be called directly. It
21620 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21621 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21623 getDocMarkup : function(){
21627 // inherit styels from page...??
21628 if (this.stylesheets === false) {
21630 Roo.get(document.head).select('style').each(function(node) {
21631 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21634 Roo.get(document.head).select('link').each(function(node) {
21635 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21638 } else if (!this.stylesheets.length) {
21640 st = '<style type="text/css">' +
21641 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21644 st = '<style type="text/css">' +
21649 st += '<style type="text/css">' +
21650 'IMG { cursor: pointer } ' +
21653 var cls = 'roo-htmleditor-body';
21655 if(this.bodyCls.length){
21656 cls += ' ' + this.bodyCls;
21659 return '<html><head>' + st +
21660 //<style type="text/css">' +
21661 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21663 ' </head><body class="' + cls + '"></body></html>';
21667 onRender : function(ct, position)
21670 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21671 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21674 this.el.dom.style.border = '0 none';
21675 this.el.dom.setAttribute('tabIndex', -1);
21676 this.el.addClass('x-hidden hide');
21680 if(Roo.isIE){ // fix IE 1px bogus margin
21681 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21685 this.frameId = Roo.id();
21689 var iframe = this.owner.wrap.createChild({
21691 cls: 'form-control', // bootstrap..
21693 name: this.frameId,
21694 frameBorder : 'no',
21695 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21700 this.iframe = iframe.dom;
21702 this.assignDocWin();
21704 this.doc.designMode = 'on';
21707 this.doc.write(this.getDocMarkup());
21711 var task = { // must defer to wait for browser to be ready
21713 //console.log("run task?" + this.doc.readyState);
21714 this.assignDocWin();
21715 if(this.doc.body || this.doc.readyState == 'complete'){
21717 this.doc.designMode="on";
21721 Roo.TaskMgr.stop(task);
21722 this.initEditor.defer(10, this);
21729 Roo.TaskMgr.start(task);
21734 onResize : function(w, h)
21736 Roo.log('resize: ' +w + ',' + h );
21737 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21741 if(typeof w == 'number'){
21743 this.iframe.style.width = w + 'px';
21745 if(typeof h == 'number'){
21747 this.iframe.style.height = h + 'px';
21749 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21756 * Toggles the editor between standard and source edit mode.
21757 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21759 toggleSourceEdit : function(sourceEditMode){
21761 this.sourceEditMode = sourceEditMode === true;
21763 if(this.sourceEditMode){
21765 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21768 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21769 //this.iframe.className = '';
21772 //this.setSize(this.owner.wrap.getSize());
21773 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21780 * Protected method that will not generally be called directly. If you need/want
21781 * custom HTML cleanup, this is the method you should override.
21782 * @param {String} html The HTML to be cleaned
21783 * return {String} The cleaned HTML
21785 cleanHtml : function(html){
21786 html = String(html);
21787 if(html.length > 5){
21788 if(Roo.isSafari){ // strip safari nonsense
21789 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21792 if(html == ' '){
21799 * HTML Editor -> Textarea
21800 * Protected method that will not generally be called directly. Syncs the contents
21801 * of the editor iframe with the textarea.
21803 syncValue : function(){
21804 if(this.initialized){
21805 var bd = (this.doc.body || this.doc.documentElement);
21806 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21807 var html = bd.innerHTML;
21809 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21810 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21812 html = '<div style="'+m[0]+'">' + html + '</div>';
21815 html = this.cleanHtml(html);
21816 // fix up the special chars.. normaly like back quotes in word...
21817 // however we do not want to do this with chinese..
21818 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21819 var cc = b.charCodeAt();
21821 (cc >= 0x4E00 && cc < 0xA000 ) ||
21822 (cc >= 0x3400 && cc < 0x4E00 ) ||
21823 (cc >= 0xf900 && cc < 0xfb00 )
21829 if(this.owner.fireEvent('beforesync', this, html) !== false){
21830 this.el.dom.value = html;
21831 this.owner.fireEvent('sync', this, html);
21837 * Protected method that will not generally be called directly. Pushes the value of the textarea
21838 * into the iframe editor.
21840 pushValue : function(){
21841 if(this.initialized){
21842 var v = this.el.dom.value.trim();
21844 // if(v.length < 1){
21848 if(this.owner.fireEvent('beforepush', this, v) !== false){
21849 var d = (this.doc.body || this.doc.documentElement);
21851 this.cleanUpPaste();
21852 this.el.dom.value = d.innerHTML;
21853 this.owner.fireEvent('push', this, v);
21859 deferFocus : function(){
21860 this.focus.defer(10, this);
21864 focus : function(){
21865 if(this.win && !this.sourceEditMode){
21872 assignDocWin: function()
21874 var iframe = this.iframe;
21877 this.doc = iframe.contentWindow.document;
21878 this.win = iframe.contentWindow;
21880 // if (!Roo.get(this.frameId)) {
21883 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21884 // this.win = Roo.get(this.frameId).dom.contentWindow;
21886 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21890 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21891 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21896 initEditor : function(){
21897 //console.log("INIT EDITOR");
21898 this.assignDocWin();
21902 this.doc.designMode="on";
21904 this.doc.write(this.getDocMarkup());
21907 var dbody = (this.doc.body || this.doc.documentElement);
21908 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21909 // this copies styles from the containing element into thsi one..
21910 // not sure why we need all of this..
21911 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21913 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21914 //ss['background-attachment'] = 'fixed'; // w3c
21915 dbody.bgProperties = 'fixed'; // ie
21916 //Roo.DomHelper.applyStyles(dbody, ss);
21917 Roo.EventManager.on(this.doc, {
21918 //'mousedown': this.onEditorEvent,
21919 'mouseup': this.onEditorEvent,
21920 'dblclick': this.onEditorEvent,
21921 'click': this.onEditorEvent,
21922 'keyup': this.onEditorEvent,
21927 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21929 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21930 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21932 this.initialized = true;
21934 this.owner.fireEvent('initialize', this);
21939 onDestroy : function(){
21945 //for (var i =0; i < this.toolbars.length;i++) {
21946 // // fixme - ask toolbars for heights?
21947 // this.toolbars[i].onDestroy();
21950 //this.wrap.dom.innerHTML = '';
21951 //this.wrap.remove();
21956 onFirstFocus : function(){
21958 this.assignDocWin();
21961 this.activated = true;
21964 if(Roo.isGecko){ // prevent silly gecko errors
21966 var s = this.win.getSelection();
21967 if(!s.focusNode || s.focusNode.nodeType != 3){
21968 var r = s.getRangeAt(0);
21969 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21974 this.execCmd('useCSS', true);
21975 this.execCmd('styleWithCSS', false);
21978 this.owner.fireEvent('activate', this);
21982 adjustFont: function(btn){
21983 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21984 //if(Roo.isSafari){ // safari
21987 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21988 if(Roo.isSafari){ // safari
21989 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21990 v = (v < 10) ? 10 : v;
21991 v = (v > 48) ? 48 : v;
21992 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21997 v = Math.max(1, v+adjust);
21999 this.execCmd('FontSize', v );
22002 onEditorEvent : function(e)
22004 this.owner.fireEvent('editorevent', this, e);
22005 // this.updateToolbar();
22006 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22009 insertTag : function(tg)
22011 // could be a bit smarter... -> wrap the current selected tRoo..
22012 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22014 range = this.createRange(this.getSelection());
22015 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22016 wrappingNode.appendChild(range.extractContents());
22017 range.insertNode(wrappingNode);
22024 this.execCmd("formatblock", tg);
22028 insertText : function(txt)
22032 var range = this.createRange();
22033 range.deleteContents();
22034 //alert(Sender.getAttribute('label'));
22036 range.insertNode(this.doc.createTextNode(txt));
22042 * Executes a Midas editor command on the editor document and performs necessary focus and
22043 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22044 * @param {String} cmd The Midas command
22045 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22047 relayCmd : function(cmd, value){
22049 this.execCmd(cmd, value);
22050 this.owner.fireEvent('editorevent', this);
22051 //this.updateToolbar();
22052 this.owner.deferFocus();
22056 * Executes a Midas editor command directly on the editor document.
22057 * For visual commands, you should use {@link #relayCmd} instead.
22058 * <b>This should only be called after the editor is initialized.</b>
22059 * @param {String} cmd The Midas command
22060 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22062 execCmd : function(cmd, value){
22063 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22070 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22072 * @param {String} text | dom node..
22074 insertAtCursor : function(text)
22077 if(!this.activated){
22083 var r = this.doc.selection.createRange();
22094 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22098 // from jquery ui (MIT licenced)
22100 var win = this.win;
22102 if (win.getSelection && win.getSelection().getRangeAt) {
22103 range = win.getSelection().getRangeAt(0);
22104 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22105 range.insertNode(node);
22106 } else if (win.document.selection && win.document.selection.createRange) {
22107 // no firefox support
22108 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22109 win.document.selection.createRange().pasteHTML(txt);
22111 // no firefox support
22112 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22113 this.execCmd('InsertHTML', txt);
22122 mozKeyPress : function(e){
22124 var c = e.getCharCode(), cmd;
22127 c = String.fromCharCode(c).toLowerCase();
22141 this.cleanUpPaste.defer(100, this);
22149 e.preventDefault();
22157 fixKeys : function(){ // load time branching for fastest keydown performance
22159 return function(e){
22160 var k = e.getKey(), r;
22163 r = this.doc.selection.createRange();
22166 r.pasteHTML('    ');
22173 r = this.doc.selection.createRange();
22175 var target = r.parentElement();
22176 if(!target || target.tagName.toLowerCase() != 'li'){
22178 r.pasteHTML('<br />');
22184 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22185 this.cleanUpPaste.defer(100, this);
22191 }else if(Roo.isOpera){
22192 return function(e){
22193 var k = e.getKey();
22197 this.execCmd('InsertHTML','    ');
22200 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22201 this.cleanUpPaste.defer(100, this);
22206 }else if(Roo.isSafari){
22207 return function(e){
22208 var k = e.getKey();
22212 this.execCmd('InsertText','\t');
22216 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22217 this.cleanUpPaste.defer(100, this);
22225 getAllAncestors: function()
22227 var p = this.getSelectedNode();
22230 a.push(p); // push blank onto stack..
22231 p = this.getParentElement();
22235 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22239 a.push(this.doc.body);
22243 lastSelNode : false,
22246 getSelection : function()
22248 this.assignDocWin();
22249 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22252 getSelectedNode: function()
22254 // this may only work on Gecko!!!
22256 // should we cache this!!!!
22261 var range = this.createRange(this.getSelection()).cloneRange();
22264 var parent = range.parentElement();
22266 var testRange = range.duplicate();
22267 testRange.moveToElementText(parent);
22268 if (testRange.inRange(range)) {
22271 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22274 parent = parent.parentElement;
22279 // is ancestor a text element.
22280 var ac = range.commonAncestorContainer;
22281 if (ac.nodeType == 3) {
22282 ac = ac.parentNode;
22285 var ar = ac.childNodes;
22288 var other_nodes = [];
22289 var has_other_nodes = false;
22290 for (var i=0;i<ar.length;i++) {
22291 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22294 // fullly contained node.
22296 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22301 // probably selected..
22302 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22303 other_nodes.push(ar[i]);
22307 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22312 has_other_nodes = true;
22314 if (!nodes.length && other_nodes.length) {
22315 nodes= other_nodes;
22317 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22323 createRange: function(sel)
22325 // this has strange effects when using with
22326 // top toolbar - not sure if it's a great idea.
22327 //this.editor.contentWindow.focus();
22328 if (typeof sel != "undefined") {
22330 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22332 return this.doc.createRange();
22335 return this.doc.createRange();
22338 getParentElement: function()
22341 this.assignDocWin();
22342 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22344 var range = this.createRange(sel);
22347 var p = range.commonAncestorContainer;
22348 while (p.nodeType == 3) { // text node
22359 * Range intersection.. the hard stuff...
22363 * [ -- selected range --- ]
22367 * if end is before start or hits it. fail.
22368 * if start is after end or hits it fail.
22370 * if either hits (but other is outside. - then it's not
22376 // @see http://www.thismuchiknow.co.uk/?p=64.
22377 rangeIntersectsNode : function(range, node)
22379 var nodeRange = node.ownerDocument.createRange();
22381 nodeRange.selectNode(node);
22383 nodeRange.selectNodeContents(node);
22386 var rangeStartRange = range.cloneRange();
22387 rangeStartRange.collapse(true);
22389 var rangeEndRange = range.cloneRange();
22390 rangeEndRange.collapse(false);
22392 var nodeStartRange = nodeRange.cloneRange();
22393 nodeStartRange.collapse(true);
22395 var nodeEndRange = nodeRange.cloneRange();
22396 nodeEndRange.collapse(false);
22398 return rangeStartRange.compareBoundaryPoints(
22399 Range.START_TO_START, nodeEndRange) == -1 &&
22400 rangeEndRange.compareBoundaryPoints(
22401 Range.START_TO_START, nodeStartRange) == 1;
22405 rangeCompareNode : function(range, node)
22407 var nodeRange = node.ownerDocument.createRange();
22409 nodeRange.selectNode(node);
22411 nodeRange.selectNodeContents(node);
22415 range.collapse(true);
22417 nodeRange.collapse(true);
22419 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22420 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22422 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22424 var nodeIsBefore = ss == 1;
22425 var nodeIsAfter = ee == -1;
22427 if (nodeIsBefore && nodeIsAfter) {
22430 if (!nodeIsBefore && nodeIsAfter) {
22431 return 1; //right trailed.
22434 if (nodeIsBefore && !nodeIsAfter) {
22435 return 2; // left trailed.
22441 // private? - in a new class?
22442 cleanUpPaste : function()
22444 // cleans up the whole document..
22445 Roo.log('cleanuppaste');
22447 this.cleanUpChildren(this.doc.body);
22448 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22449 if (clean != this.doc.body.innerHTML) {
22450 this.doc.body.innerHTML = clean;
22455 cleanWordChars : function(input) {// change the chars to hex code
22456 var he = Roo.HtmlEditorCore;
22458 var output = input;
22459 Roo.each(he.swapCodes, function(sw) {
22460 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22462 output = output.replace(swapper, sw[1]);
22469 cleanUpChildren : function (n)
22471 if (!n.childNodes.length) {
22474 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22475 this.cleanUpChild(n.childNodes[i]);
22482 cleanUpChild : function (node)
22485 //console.log(node);
22486 if (node.nodeName == "#text") {
22487 // clean up silly Windows -- stuff?
22490 if (node.nodeName == "#comment") {
22491 node.parentNode.removeChild(node);
22492 // clean up silly Windows -- stuff?
22495 var lcname = node.tagName.toLowerCase();
22496 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22497 // whitelist of tags..
22499 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22501 node.parentNode.removeChild(node);
22506 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22508 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22509 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22511 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22512 // remove_keep_children = true;
22515 if (remove_keep_children) {
22516 this.cleanUpChildren(node);
22517 // inserts everything just before this node...
22518 while (node.childNodes.length) {
22519 var cn = node.childNodes[0];
22520 node.removeChild(cn);
22521 node.parentNode.insertBefore(cn, node);
22523 node.parentNode.removeChild(node);
22527 if (!node.attributes || !node.attributes.length) {
22528 this.cleanUpChildren(node);
22532 function cleanAttr(n,v)
22535 if (v.match(/^\./) || v.match(/^\//)) {
22538 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22541 if (v.match(/^#/)) {
22544 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22545 node.removeAttribute(n);
22549 var cwhite = this.cwhite;
22550 var cblack = this.cblack;
22552 function cleanStyle(n,v)
22554 if (v.match(/expression/)) { //XSS?? should we even bother..
22555 node.removeAttribute(n);
22559 var parts = v.split(/;/);
22562 Roo.each(parts, function(p) {
22563 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22567 var l = p.split(':').shift().replace(/\s+/g,'');
22568 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22570 if ( cwhite.length && cblack.indexOf(l) > -1) {
22571 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22572 //node.removeAttribute(n);
22576 // only allow 'c whitelisted system attributes'
22577 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22578 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22579 //node.removeAttribute(n);
22589 if (clean.length) {
22590 node.setAttribute(n, clean.join(';'));
22592 node.removeAttribute(n);
22598 for (var i = node.attributes.length-1; i > -1 ; i--) {
22599 var a = node.attributes[i];
22602 if (a.name.toLowerCase().substr(0,2)=='on') {
22603 node.removeAttribute(a.name);
22606 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22607 node.removeAttribute(a.name);
22610 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22611 cleanAttr(a.name,a.value); // fixme..
22614 if (a.name == 'style') {
22615 cleanStyle(a.name,a.value);
22618 /// clean up MS crap..
22619 // tecnically this should be a list of valid class'es..
22622 if (a.name == 'class') {
22623 if (a.value.match(/^Mso/)) {
22624 node.className = '';
22627 if (a.value.match(/^body$/)) {
22628 node.className = '';
22639 this.cleanUpChildren(node);
22645 * Clean up MS wordisms...
22647 cleanWord : function(node)
22652 this.cleanWord(this.doc.body);
22655 if (node.nodeName == "#text") {
22656 // clean up silly Windows -- stuff?
22659 if (node.nodeName == "#comment") {
22660 node.parentNode.removeChild(node);
22661 // clean up silly Windows -- stuff?
22665 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22666 node.parentNode.removeChild(node);
22670 // remove - but keep children..
22671 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22672 while (node.childNodes.length) {
22673 var cn = node.childNodes[0];
22674 node.removeChild(cn);
22675 node.parentNode.insertBefore(cn, node);
22677 node.parentNode.removeChild(node);
22678 this.iterateChildren(node, this.cleanWord);
22682 if (node.className.length) {
22684 var cn = node.className.split(/\W+/);
22686 Roo.each(cn, function(cls) {
22687 if (cls.match(/Mso[a-zA-Z]+/)) {
22692 node.className = cna.length ? cna.join(' ') : '';
22694 node.removeAttribute("class");
22698 if (node.hasAttribute("lang")) {
22699 node.removeAttribute("lang");
22702 if (node.hasAttribute("style")) {
22704 var styles = node.getAttribute("style").split(";");
22706 Roo.each(styles, function(s) {
22707 if (!s.match(/:/)) {
22710 var kv = s.split(":");
22711 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22714 // what ever is left... we allow.
22717 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22718 if (!nstyle.length) {
22719 node.removeAttribute('style');
22722 this.iterateChildren(node, this.cleanWord);
22728 * iterateChildren of a Node, calling fn each time, using this as the scole..
22729 * @param {DomNode} node node to iterate children of.
22730 * @param {Function} fn method of this class to call on each item.
22732 iterateChildren : function(node, fn)
22734 if (!node.childNodes.length) {
22737 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22738 fn.call(this, node.childNodes[i])
22744 * cleanTableWidths.
22746 * Quite often pasting from word etc.. results in tables with column and widths.
22747 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22750 cleanTableWidths : function(node)
22755 this.cleanTableWidths(this.doc.body);
22760 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22763 Roo.log(node.tagName);
22764 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22765 this.iterateChildren(node, this.cleanTableWidths);
22768 if (node.hasAttribute('width')) {
22769 node.removeAttribute('width');
22773 if (node.hasAttribute("style")) {
22776 var styles = node.getAttribute("style").split(";");
22778 Roo.each(styles, function(s) {
22779 if (!s.match(/:/)) {
22782 var kv = s.split(":");
22783 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22786 // what ever is left... we allow.
22789 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22790 if (!nstyle.length) {
22791 node.removeAttribute('style');
22795 this.iterateChildren(node, this.cleanTableWidths);
22803 domToHTML : function(currentElement, depth, nopadtext) {
22805 depth = depth || 0;
22806 nopadtext = nopadtext || false;
22808 if (!currentElement) {
22809 return this.domToHTML(this.doc.body);
22812 //Roo.log(currentElement);
22814 var allText = false;
22815 var nodeName = currentElement.nodeName;
22816 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22818 if (nodeName == '#text') {
22820 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22825 if (nodeName != 'BODY') {
22828 // Prints the node tagName, such as <A>, <IMG>, etc
22831 for(i = 0; i < currentElement.attributes.length;i++) {
22833 var aname = currentElement.attributes.item(i).name;
22834 if (!currentElement.attributes.item(i).value.length) {
22837 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22840 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22849 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22852 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22857 // Traverse the tree
22859 var currentElementChild = currentElement.childNodes.item(i);
22860 var allText = true;
22861 var innerHTML = '';
22863 while (currentElementChild) {
22864 // Formatting code (indent the tree so it looks nice on the screen)
22865 var nopad = nopadtext;
22866 if (lastnode == 'SPAN') {
22870 if (currentElementChild.nodeName == '#text') {
22871 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22872 toadd = nopadtext ? toadd : toadd.trim();
22873 if (!nopad && toadd.length > 80) {
22874 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22876 innerHTML += toadd;
22879 currentElementChild = currentElement.childNodes.item(i);
22885 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22887 // Recursively traverse the tree structure of the child node
22888 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22889 lastnode = currentElementChild.nodeName;
22891 currentElementChild=currentElement.childNodes.item(i);
22897 // The remaining code is mostly for formatting the tree
22898 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22903 ret+= "</"+tagName+">";
22909 applyBlacklists : function()
22911 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22912 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22916 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22917 if (b.indexOf(tag) > -1) {
22920 this.white.push(tag);
22924 Roo.each(w, function(tag) {
22925 if (b.indexOf(tag) > -1) {
22928 if (this.white.indexOf(tag) > -1) {
22931 this.white.push(tag);
22936 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22937 if (w.indexOf(tag) > -1) {
22940 this.black.push(tag);
22944 Roo.each(b, function(tag) {
22945 if (w.indexOf(tag) > -1) {
22948 if (this.black.indexOf(tag) > -1) {
22951 this.black.push(tag);
22956 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22957 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22961 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22962 if (b.indexOf(tag) > -1) {
22965 this.cwhite.push(tag);
22969 Roo.each(w, function(tag) {
22970 if (b.indexOf(tag) > -1) {
22973 if (this.cwhite.indexOf(tag) > -1) {
22976 this.cwhite.push(tag);
22981 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22982 if (w.indexOf(tag) > -1) {
22985 this.cblack.push(tag);
22989 Roo.each(b, function(tag) {
22990 if (w.indexOf(tag) > -1) {
22993 if (this.cblack.indexOf(tag) > -1) {
22996 this.cblack.push(tag);
23001 setStylesheets : function(stylesheets)
23003 if(typeof(stylesheets) == 'string'){
23004 Roo.get(this.iframe.contentDocument.head).createChild({
23006 rel : 'stylesheet',
23015 Roo.each(stylesheets, function(s) {
23020 Roo.get(_this.iframe.contentDocument.head).createChild({
23022 rel : 'stylesheet',
23031 removeStylesheets : function()
23035 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23040 setStyle : function(style)
23042 Roo.get(this.iframe.contentDocument.head).createChild({
23051 // hide stuff that is not compatible
23065 * @event specialkey
23069 * @cfg {String} fieldClass @hide
23072 * @cfg {String} focusClass @hide
23075 * @cfg {String} autoCreate @hide
23078 * @cfg {String} inputType @hide
23081 * @cfg {String} invalidClass @hide
23084 * @cfg {String} invalidText @hide
23087 * @cfg {String} msgFx @hide
23090 * @cfg {String} validateOnBlur @hide
23094 Roo.HtmlEditorCore.white = [
23095 'area', 'br', 'img', 'input', 'hr', 'wbr',
23097 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23098 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23099 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23100 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23101 'table', 'ul', 'xmp',
23103 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23106 'dir', 'menu', 'ol', 'ul', 'dl',
23112 Roo.HtmlEditorCore.black = [
23113 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23115 'base', 'basefont', 'bgsound', 'blink', 'body',
23116 'frame', 'frameset', 'head', 'html', 'ilayer',
23117 'iframe', 'layer', 'link', 'meta', 'object',
23118 'script', 'style' ,'title', 'xml' // clean later..
23120 Roo.HtmlEditorCore.clean = [
23121 'script', 'style', 'title', 'xml'
23123 Roo.HtmlEditorCore.remove = [
23128 Roo.HtmlEditorCore.ablack = [
23132 Roo.HtmlEditorCore.aclean = [
23133 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23137 Roo.HtmlEditorCore.pwhite= [
23138 'http', 'https', 'mailto'
23141 // white listed style attributes.
23142 Roo.HtmlEditorCore.cwhite= [
23143 // 'text-align', /// default is to allow most things..
23149 // black listed style attributes.
23150 Roo.HtmlEditorCore.cblack= [
23151 // 'font-size' -- this can be set by the project
23155 Roo.HtmlEditorCore.swapCodes =[
23174 * @class Roo.bootstrap.HtmlEditor
23175 * @extends Roo.bootstrap.TextArea
23176 * Bootstrap HtmlEditor class
23179 * Create a new HtmlEditor
23180 * @param {Object} config The config object
23183 Roo.bootstrap.HtmlEditor = function(config){
23184 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23185 if (!this.toolbars) {
23186 this.toolbars = [];
23189 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23192 * @event initialize
23193 * Fires when the editor is fully initialized (including the iframe)
23194 * @param {HtmlEditor} this
23199 * Fires when the editor is first receives the focus. Any insertion must wait
23200 * until after this event.
23201 * @param {HtmlEditor} this
23205 * @event beforesync
23206 * Fires before the textarea is updated with content from the editor iframe. Return false
23207 * to cancel the sync.
23208 * @param {HtmlEditor} this
23209 * @param {String} html
23213 * @event beforepush
23214 * Fires before the iframe editor is updated with content from the textarea. Return false
23215 * to cancel the push.
23216 * @param {HtmlEditor} this
23217 * @param {String} html
23222 * Fires when the textarea is updated with content from the editor iframe.
23223 * @param {HtmlEditor} this
23224 * @param {String} html
23229 * Fires when the iframe editor is updated with content from the textarea.
23230 * @param {HtmlEditor} this
23231 * @param {String} html
23235 * @event editmodechange
23236 * Fires when the editor switches edit modes
23237 * @param {HtmlEditor} this
23238 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23240 editmodechange: true,
23242 * @event editorevent
23243 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23244 * @param {HtmlEditor} this
23248 * @event firstfocus
23249 * Fires when on first focus - needed by toolbars..
23250 * @param {HtmlEditor} this
23255 * Auto save the htmlEditor value as a file into Events
23256 * @param {HtmlEditor} this
23260 * @event savedpreview
23261 * preview the saved version of htmlEditor
23262 * @param {HtmlEditor} this
23269 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23273 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23278 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23283 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23288 * @cfg {Number} height (in pixels)
23292 * @cfg {Number} width (in pixels)
23297 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23300 stylesheets: false,
23305 // private properties
23306 validationEvent : false,
23308 initialized : false,
23311 onFocus : Roo.emptyFn,
23313 hideMode:'offsets',
23315 tbContainer : false,
23319 toolbarContainer :function() {
23320 return this.wrap.select('.x-html-editor-tb',true).first();
23324 * Protected method that will not generally be called directly. It
23325 * is called when the editor creates its toolbar. Override this method if you need to
23326 * add custom toolbar buttons.
23327 * @param {HtmlEditor} editor
23329 createToolbar : function(){
23330 Roo.log('renewing');
23331 Roo.log("create toolbars");
23333 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23334 this.toolbars[0].render(this.toolbarContainer());
23338 // if (!editor.toolbars || !editor.toolbars.length) {
23339 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23342 // for (var i =0 ; i < editor.toolbars.length;i++) {
23343 // editor.toolbars[i] = Roo.factory(
23344 // typeof(editor.toolbars[i]) == 'string' ?
23345 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23346 // Roo.bootstrap.HtmlEditor);
23347 // editor.toolbars[i].init(editor);
23353 onRender : function(ct, position)
23355 // Roo.log("Call onRender: " + this.xtype);
23357 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23359 this.wrap = this.inputEl().wrap({
23360 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23363 this.editorcore.onRender(ct, position);
23365 if (this.resizable) {
23366 this.resizeEl = new Roo.Resizable(this.wrap, {
23370 minHeight : this.height,
23371 height: this.height,
23372 handles : this.resizable,
23375 resize : function(r, w, h) {
23376 _t.onResize(w,h); // -something
23382 this.createToolbar(this);
23385 if(!this.width && this.resizable){
23386 this.setSize(this.wrap.getSize());
23388 if (this.resizeEl) {
23389 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23390 // should trigger onReize..
23396 onResize : function(w, h)
23398 Roo.log('resize: ' +w + ',' + h );
23399 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23403 if(this.inputEl() ){
23404 if(typeof w == 'number'){
23405 var aw = w - this.wrap.getFrameWidth('lr');
23406 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23409 if(typeof h == 'number'){
23410 var tbh = -11; // fixme it needs to tool bar size!
23411 for (var i =0; i < this.toolbars.length;i++) {
23412 // fixme - ask toolbars for heights?
23413 tbh += this.toolbars[i].el.getHeight();
23414 //if (this.toolbars[i].footer) {
23415 // tbh += this.toolbars[i].footer.el.getHeight();
23423 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23424 ah -= 5; // knock a few pixes off for look..
23425 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23429 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23430 this.editorcore.onResize(ew,eh);
23435 * Toggles the editor between standard and source edit mode.
23436 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23438 toggleSourceEdit : function(sourceEditMode)
23440 this.editorcore.toggleSourceEdit(sourceEditMode);
23442 if(this.editorcore.sourceEditMode){
23443 Roo.log('editor - showing textarea');
23446 // Roo.log(this.syncValue());
23448 this.inputEl().removeClass(['hide', 'x-hidden']);
23449 this.inputEl().dom.removeAttribute('tabIndex');
23450 this.inputEl().focus();
23452 Roo.log('editor - hiding textarea');
23454 // Roo.log(this.pushValue());
23457 this.inputEl().addClass(['hide', 'x-hidden']);
23458 this.inputEl().dom.setAttribute('tabIndex', -1);
23459 //this.deferFocus();
23462 if(this.resizable){
23463 this.setSize(this.wrap.getSize());
23466 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23469 // private (for BoxComponent)
23470 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23472 // private (for BoxComponent)
23473 getResizeEl : function(){
23477 // private (for BoxComponent)
23478 getPositionEl : function(){
23483 initEvents : function(){
23484 this.originalValue = this.getValue();
23488 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23491 // markInvalid : Roo.emptyFn,
23493 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23496 // clearInvalid : Roo.emptyFn,
23498 setValue : function(v){
23499 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23500 this.editorcore.pushValue();
23505 deferFocus : function(){
23506 this.focus.defer(10, this);
23510 focus : function(){
23511 this.editorcore.focus();
23517 onDestroy : function(){
23523 for (var i =0; i < this.toolbars.length;i++) {
23524 // fixme - ask toolbars for heights?
23525 this.toolbars[i].onDestroy();
23528 this.wrap.dom.innerHTML = '';
23529 this.wrap.remove();
23534 onFirstFocus : function(){
23535 //Roo.log("onFirstFocus");
23536 this.editorcore.onFirstFocus();
23537 for (var i =0; i < this.toolbars.length;i++) {
23538 this.toolbars[i].onFirstFocus();
23544 syncValue : function()
23546 this.editorcore.syncValue();
23549 pushValue : function()
23551 this.editorcore.pushValue();
23555 // hide stuff that is not compatible
23569 * @event specialkey
23573 * @cfg {String} fieldClass @hide
23576 * @cfg {String} focusClass @hide
23579 * @cfg {String} autoCreate @hide
23582 * @cfg {String} inputType @hide
23585 * @cfg {String} invalidClass @hide
23588 * @cfg {String} invalidText @hide
23591 * @cfg {String} msgFx @hide
23594 * @cfg {String} validateOnBlur @hide
23603 Roo.namespace('Roo.bootstrap.htmleditor');
23605 * @class Roo.bootstrap.HtmlEditorToolbar1
23610 new Roo.bootstrap.HtmlEditor({
23613 new Roo.bootstrap.HtmlEditorToolbar1({
23614 disable : { fonts: 1 , format: 1, ..., ... , ...],
23620 * @cfg {Object} disable List of elements to disable..
23621 * @cfg {Array} btns List of additional buttons.
23625 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23628 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23631 Roo.apply(this, config);
23633 // default disabled, based on 'good practice'..
23634 this.disable = this.disable || {};
23635 Roo.applyIf(this.disable, {
23638 specialElements : true
23640 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23642 this.editor = config.editor;
23643 this.editorcore = config.editor.editorcore;
23645 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23647 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23648 // dont call parent... till later.
23650 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23655 editorcore : false,
23660 "h1","h2","h3","h4","h5","h6",
23662 "abbr", "acronym", "address", "cite", "samp", "var",
23666 onRender : function(ct, position)
23668 // Roo.log("Call onRender: " + this.xtype);
23670 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23672 this.el.dom.style.marginBottom = '0';
23674 var editorcore = this.editorcore;
23675 var editor= this.editor;
23678 var btn = function(id,cmd , toggle, handler, html){
23680 var event = toggle ? 'toggle' : 'click';
23685 xns: Roo.bootstrap,
23688 enableToggle:toggle !== false,
23690 pressed : toggle ? false : null,
23693 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23694 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23700 // var cb_box = function...
23705 xns: Roo.bootstrap,
23706 glyphicon : 'font',
23710 xns: Roo.bootstrap,
23714 Roo.each(this.formats, function(f) {
23715 style.menu.items.push({
23717 xns: Roo.bootstrap,
23718 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23723 editorcore.insertTag(this.tagname);
23730 children.push(style);
23732 btn('bold',false,true);
23733 btn('italic',false,true);
23734 btn('align-left', 'justifyleft',true);
23735 btn('align-center', 'justifycenter',true);
23736 btn('align-right' , 'justifyright',true);
23737 btn('link', false, false, function(btn) {
23738 //Roo.log("create link?");
23739 var url = prompt(this.createLinkText, this.defaultLinkValue);
23740 if(url && url != 'http:/'+'/'){
23741 this.editorcore.relayCmd('createlink', url);
23744 btn('list','insertunorderedlist',true);
23745 btn('pencil', false,true, function(btn){
23747 this.toggleSourceEdit(btn.pressed);
23750 if (this.editor.btns.length > 0) {
23751 for (var i = 0; i<this.editor.btns.length; i++) {
23752 children.push(this.editor.btns[i]);
23760 xns: Roo.bootstrap,
23765 xns: Roo.bootstrap,
23770 cog.menu.items.push({
23772 xns: Roo.bootstrap,
23773 html : Clean styles,
23778 editorcore.insertTag(this.tagname);
23787 this.xtype = 'NavSimplebar';
23789 for(var i=0;i< children.length;i++) {
23791 this.buttons.add(this.addxtypeChild(children[i]));
23795 editor.on('editorevent', this.updateToolbar, this);
23797 onBtnClick : function(id)
23799 this.editorcore.relayCmd(id);
23800 this.editorcore.focus();
23804 * Protected method that will not generally be called directly. It triggers
23805 * a toolbar update by reading the markup state of the current selection in the editor.
23807 updateToolbar: function(){
23809 if(!this.editorcore.activated){
23810 this.editor.onFirstFocus(); // is this neeed?
23814 var btns = this.buttons;
23815 var doc = this.editorcore.doc;
23816 btns.get('bold').setActive(doc.queryCommandState('bold'));
23817 btns.get('italic').setActive(doc.queryCommandState('italic'));
23818 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23820 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23821 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23822 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23824 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23825 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23828 var ans = this.editorcore.getAllAncestors();
23829 if (this.formatCombo) {
23832 var store = this.formatCombo.store;
23833 this.formatCombo.setValue("");
23834 for (var i =0; i < ans.length;i++) {
23835 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23837 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23845 // hides menus... - so this cant be on a menu...
23846 Roo.bootstrap.MenuMgr.hideAll();
23848 Roo.bootstrap.MenuMgr.hideAll();
23849 //this.editorsyncValue();
23851 onFirstFocus: function() {
23852 this.buttons.each(function(item){
23856 toggleSourceEdit : function(sourceEditMode){
23859 if(sourceEditMode){
23860 Roo.log("disabling buttons");
23861 this.buttons.each( function(item){
23862 if(item.cmd != 'pencil'){
23868 Roo.log("enabling buttons");
23869 if(this.editorcore.initialized){
23870 this.buttons.each( function(item){
23876 Roo.log("calling toggole on editor");
23877 // tell the editor that it's been pressed..
23878 this.editor.toggleSourceEdit(sourceEditMode);
23888 * @class Roo.bootstrap.Table.AbstractSelectionModel
23889 * @extends Roo.util.Observable
23890 * Abstract base class for grid SelectionModels. It provides the interface that should be
23891 * implemented by descendant classes. This class should not be directly instantiated.
23894 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23895 this.locked = false;
23896 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23900 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23901 /** @ignore Called by the grid automatically. Do not call directly. */
23902 init : function(grid){
23908 * Locks the selections.
23911 this.locked = true;
23915 * Unlocks the selections.
23917 unlock : function(){
23918 this.locked = false;
23922 * Returns true if the selections are locked.
23923 * @return {Boolean}
23925 isLocked : function(){
23926 return this.locked;
23930 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23931 * @class Roo.bootstrap.Table.RowSelectionModel
23932 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23933 * It supports multiple selections and keyboard selection/navigation.
23935 * @param {Object} config
23938 Roo.bootstrap.Table.RowSelectionModel = function(config){
23939 Roo.apply(this, config);
23940 this.selections = new Roo.util.MixedCollection(false, function(o){
23945 this.lastActive = false;
23949 * @event selectionchange
23950 * Fires when the selection changes
23951 * @param {SelectionModel} this
23953 "selectionchange" : true,
23955 * @event afterselectionchange
23956 * Fires after the selection changes (eg. by key press or clicking)
23957 * @param {SelectionModel} this
23959 "afterselectionchange" : true,
23961 * @event beforerowselect
23962 * Fires when a row is selected being selected, return false to cancel.
23963 * @param {SelectionModel} this
23964 * @param {Number} rowIndex The selected index
23965 * @param {Boolean} keepExisting False if other selections will be cleared
23967 "beforerowselect" : true,
23970 * Fires when a row is selected.
23971 * @param {SelectionModel} this
23972 * @param {Number} rowIndex The selected index
23973 * @param {Roo.data.Record} r The record
23975 "rowselect" : true,
23977 * @event rowdeselect
23978 * Fires when a row is deselected.
23979 * @param {SelectionModel} this
23980 * @param {Number} rowIndex The selected index
23982 "rowdeselect" : true
23984 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23985 this.locked = false;
23988 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23990 * @cfg {Boolean} singleSelect
23991 * True to allow selection of only one row at a time (defaults to false)
23993 singleSelect : false,
23996 initEvents : function()
23999 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24000 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24001 //}else{ // allow click to work like normal
24002 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24004 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24005 this.grid.on("rowclick", this.handleMouseDown, this);
24007 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24008 "up" : function(e){
24010 this.selectPrevious(e.shiftKey);
24011 }else if(this.last !== false && this.lastActive !== false){
24012 var last = this.last;
24013 this.selectRange(this.last, this.lastActive-1);
24014 this.grid.getView().focusRow(this.lastActive);
24015 if(last !== false){
24019 this.selectFirstRow();
24021 this.fireEvent("afterselectionchange", this);
24023 "down" : function(e){
24025 this.selectNext(e.shiftKey);
24026 }else if(this.last !== false && this.lastActive !== false){
24027 var last = this.last;
24028 this.selectRange(this.last, this.lastActive+1);
24029 this.grid.getView().focusRow(this.lastActive);
24030 if(last !== false){
24034 this.selectFirstRow();
24036 this.fireEvent("afterselectionchange", this);
24040 this.grid.store.on('load', function(){
24041 this.selections.clear();
24044 var view = this.grid.view;
24045 view.on("refresh", this.onRefresh, this);
24046 view.on("rowupdated", this.onRowUpdated, this);
24047 view.on("rowremoved", this.onRemove, this);
24052 onRefresh : function()
24054 var ds = this.grid.store, i, v = this.grid.view;
24055 var s = this.selections;
24056 s.each(function(r){
24057 if((i = ds.indexOfId(r.id)) != -1){
24066 onRemove : function(v, index, r){
24067 this.selections.remove(r);
24071 onRowUpdated : function(v, index, r){
24072 if(this.isSelected(r)){
24073 v.onRowSelect(index);
24079 * @param {Array} records The records to select
24080 * @param {Boolean} keepExisting (optional) True to keep existing selections
24082 selectRecords : function(records, keepExisting)
24085 this.clearSelections();
24087 var ds = this.grid.store;
24088 for(var i = 0, len = records.length; i < len; i++){
24089 this.selectRow(ds.indexOf(records[i]), true);
24094 * Gets the number of selected rows.
24097 getCount : function(){
24098 return this.selections.length;
24102 * Selects the first row in the grid.
24104 selectFirstRow : function(){
24109 * Select the last row.
24110 * @param {Boolean} keepExisting (optional) True to keep existing selections
24112 selectLastRow : function(keepExisting){
24113 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24114 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24118 * Selects the row immediately following the last selected row.
24119 * @param {Boolean} keepExisting (optional) True to keep existing selections
24121 selectNext : function(keepExisting)
24123 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24124 this.selectRow(this.last+1, keepExisting);
24125 this.grid.getView().focusRow(this.last);
24130 * Selects the row that precedes the last selected row.
24131 * @param {Boolean} keepExisting (optional) True to keep existing selections
24133 selectPrevious : function(keepExisting){
24135 this.selectRow(this.last-1, keepExisting);
24136 this.grid.getView().focusRow(this.last);
24141 * Returns the selected records
24142 * @return {Array} Array of selected records
24144 getSelections : function(){
24145 return [].concat(this.selections.items);
24149 * Returns the first selected record.
24152 getSelected : function(){
24153 return this.selections.itemAt(0);
24158 * Clears all selections.
24160 clearSelections : function(fast)
24166 var ds = this.grid.store;
24167 var s = this.selections;
24168 s.each(function(r){
24169 this.deselectRow(ds.indexOfId(r.id));
24173 this.selections.clear();
24180 * Selects all rows.
24182 selectAll : function(){
24186 this.selections.clear();
24187 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24188 this.selectRow(i, true);
24193 * Returns True if there is a selection.
24194 * @return {Boolean}
24196 hasSelection : function(){
24197 return this.selections.length > 0;
24201 * Returns True if the specified row is selected.
24202 * @param {Number/Record} record The record or index of the record to check
24203 * @return {Boolean}
24205 isSelected : function(index){
24206 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24207 return (r && this.selections.key(r.id) ? true : false);
24211 * Returns True if the specified record id is selected.
24212 * @param {String} id The id of record to check
24213 * @return {Boolean}
24215 isIdSelected : function(id){
24216 return (this.selections.key(id) ? true : false);
24221 handleMouseDBClick : function(e, t){
24225 handleMouseDown : function(e, t)
24227 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24228 if(this.isLocked() || rowIndex < 0 ){
24231 if(e.shiftKey && this.last !== false){
24232 var last = this.last;
24233 this.selectRange(last, rowIndex, e.ctrlKey);
24234 this.last = last; // reset the last
24238 var isSelected = this.isSelected(rowIndex);
24239 //Roo.log("select row:" + rowIndex);
24241 this.deselectRow(rowIndex);
24243 this.selectRow(rowIndex, true);
24247 if(e.button !== 0 && isSelected){
24248 alert('rowIndex 2: ' + rowIndex);
24249 view.focusRow(rowIndex);
24250 }else if(e.ctrlKey && isSelected){
24251 this.deselectRow(rowIndex);
24252 }else if(!isSelected){
24253 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24254 view.focusRow(rowIndex);
24258 this.fireEvent("afterselectionchange", this);
24261 handleDragableRowClick : function(grid, rowIndex, e)
24263 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24264 this.selectRow(rowIndex, false);
24265 grid.view.focusRow(rowIndex);
24266 this.fireEvent("afterselectionchange", this);
24271 * Selects multiple rows.
24272 * @param {Array} rows Array of the indexes of the row to select
24273 * @param {Boolean} keepExisting (optional) True to keep existing selections
24275 selectRows : function(rows, keepExisting){
24277 this.clearSelections();
24279 for(var i = 0, len = rows.length; i < len; i++){
24280 this.selectRow(rows[i], true);
24285 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24286 * @param {Number} startRow The index of the first row in the range
24287 * @param {Number} endRow The index of the last row in the range
24288 * @param {Boolean} keepExisting (optional) True to retain existing selections
24290 selectRange : function(startRow, endRow, keepExisting){
24295 this.clearSelections();
24297 if(startRow <= endRow){
24298 for(var i = startRow; i <= endRow; i++){
24299 this.selectRow(i, true);
24302 for(var i = startRow; i >= endRow; i--){
24303 this.selectRow(i, true);
24309 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24310 * @param {Number} startRow The index of the first row in the range
24311 * @param {Number} endRow The index of the last row in the range
24313 deselectRange : function(startRow, endRow, preventViewNotify){
24317 for(var i = startRow; i <= endRow; i++){
24318 this.deselectRow(i, preventViewNotify);
24324 * @param {Number} row The index of the row to select
24325 * @param {Boolean} keepExisting (optional) True to keep existing selections
24327 selectRow : function(index, keepExisting, preventViewNotify)
24329 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24332 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24333 if(!keepExisting || this.singleSelect){
24334 this.clearSelections();
24337 var r = this.grid.store.getAt(index);
24338 //console.log('selectRow - record id :' + r.id);
24340 this.selections.add(r);
24341 this.last = this.lastActive = index;
24342 if(!preventViewNotify){
24343 var proxy = new Roo.Element(
24344 this.grid.getRowDom(index)
24346 proxy.addClass('bg-info info');
24348 this.fireEvent("rowselect", this, index, r);
24349 this.fireEvent("selectionchange", this);
24355 * @param {Number} row The index of the row to deselect
24357 deselectRow : function(index, preventViewNotify)
24362 if(this.last == index){
24365 if(this.lastActive == index){
24366 this.lastActive = false;
24369 var r = this.grid.store.getAt(index);
24374 this.selections.remove(r);
24375 //.console.log('deselectRow - record id :' + r.id);
24376 if(!preventViewNotify){
24378 var proxy = new Roo.Element(
24379 this.grid.getRowDom(index)
24381 proxy.removeClass('bg-info info');
24383 this.fireEvent("rowdeselect", this, index);
24384 this.fireEvent("selectionchange", this);
24388 restoreLast : function(){
24390 this.last = this._last;
24395 acceptsNav : function(row, col, cm){
24396 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24400 onEditorKey : function(field, e){
24401 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24406 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24408 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24410 }else if(k == e.ENTER && !e.ctrlKey){
24414 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24416 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24418 }else if(k == e.ESC){
24422 g.startEditing(newCell[0], newCell[1]);
24428 * Ext JS Library 1.1.1
24429 * Copyright(c) 2006-2007, Ext JS, LLC.
24431 * Originally Released Under LGPL - original licence link has changed is not relivant.
24434 * <script type="text/javascript">
24438 * @class Roo.bootstrap.PagingToolbar
24439 * @extends Roo.bootstrap.NavSimplebar
24440 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24442 * Create a new PagingToolbar
24443 * @param {Object} config The config object
24444 * @param {Roo.data.Store} store
24446 Roo.bootstrap.PagingToolbar = function(config)
24448 // old args format still supported... - xtype is prefered..
24449 // created from xtype...
24451 this.ds = config.dataSource;
24453 if (config.store && !this.ds) {
24454 this.store= Roo.factory(config.store, Roo.data);
24455 this.ds = this.store;
24456 this.ds.xmodule = this.xmodule || false;
24459 this.toolbarItems = [];
24460 if (config.items) {
24461 this.toolbarItems = config.items;
24464 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24469 this.bind(this.ds);
24472 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24476 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24478 * @cfg {Roo.data.Store} dataSource
24479 * The underlying data store providing the paged data
24482 * @cfg {String/HTMLElement/Element} container
24483 * container The id or element that will contain the toolbar
24486 * @cfg {Boolean} displayInfo
24487 * True to display the displayMsg (defaults to false)
24490 * @cfg {Number} pageSize
24491 * The number of records to display per page (defaults to 20)
24495 * @cfg {String} displayMsg
24496 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24498 displayMsg : 'Displaying {0} - {1} of {2}',
24500 * @cfg {String} emptyMsg
24501 * The message to display when no records are found (defaults to "No data to display")
24503 emptyMsg : 'No data to display',
24505 * Customizable piece of the default paging text (defaults to "Page")
24508 beforePageText : "Page",
24510 * Customizable piece of the default paging text (defaults to "of %0")
24513 afterPageText : "of {0}",
24515 * Customizable piece of the default paging text (defaults to "First Page")
24518 firstText : "First Page",
24520 * Customizable piece of the default paging text (defaults to "Previous Page")
24523 prevText : "Previous Page",
24525 * Customizable piece of the default paging text (defaults to "Next Page")
24528 nextText : "Next Page",
24530 * Customizable piece of the default paging text (defaults to "Last Page")
24533 lastText : "Last Page",
24535 * Customizable piece of the default paging text (defaults to "Refresh")
24538 refreshText : "Refresh",
24542 onRender : function(ct, position)
24544 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24545 this.navgroup.parentId = this.id;
24546 this.navgroup.onRender(this.el, null);
24547 // add the buttons to the navgroup
24549 if(this.displayInfo){
24550 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24551 this.displayEl = this.el.select('.x-paging-info', true).first();
24552 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24553 // this.displayEl = navel.el.select('span',true).first();
24559 Roo.each(_this.buttons, function(e){ // this might need to use render????
24560 Roo.factory(e).render(_this.el);
24564 Roo.each(_this.toolbarItems, function(e) {
24565 _this.navgroup.addItem(e);
24569 this.first = this.navgroup.addItem({
24570 tooltip: this.firstText,
24572 icon : 'fa fa-backward',
24574 preventDefault: true,
24575 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24578 this.prev = this.navgroup.addItem({
24579 tooltip: this.prevText,
24581 icon : 'fa fa-step-backward',
24583 preventDefault: true,
24584 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24586 //this.addSeparator();
24589 var field = this.navgroup.addItem( {
24591 cls : 'x-paging-position',
24593 html : this.beforePageText +
24594 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24595 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24598 this.field = field.el.select('input', true).first();
24599 this.field.on("keydown", this.onPagingKeydown, this);
24600 this.field.on("focus", function(){this.dom.select();});
24603 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24604 //this.field.setHeight(18);
24605 //this.addSeparator();
24606 this.next = this.navgroup.addItem({
24607 tooltip: this.nextText,
24609 html : ' <i class="fa fa-step-forward">',
24611 preventDefault: true,
24612 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24614 this.last = this.navgroup.addItem({
24615 tooltip: this.lastText,
24616 icon : 'fa fa-forward',
24619 preventDefault: true,
24620 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24622 //this.addSeparator();
24623 this.loading = this.navgroup.addItem({
24624 tooltip: this.refreshText,
24625 icon: 'fa fa-refresh',
24626 preventDefault: true,
24627 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24633 updateInfo : function(){
24634 if(this.displayEl){
24635 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24636 var msg = count == 0 ?
24640 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24642 this.displayEl.update(msg);
24647 onLoad : function(ds, r, o)
24649 this.cursor = o.params.start ? o.params.start : 0;
24651 var d = this.getPageData(),
24656 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24657 this.field.dom.value = ap;
24658 this.first.setDisabled(ap == 1);
24659 this.prev.setDisabled(ap == 1);
24660 this.next.setDisabled(ap == ps);
24661 this.last.setDisabled(ap == ps);
24662 this.loading.enable();
24667 getPageData : function(){
24668 var total = this.ds.getTotalCount();
24671 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24672 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24677 onLoadError : function(){
24678 this.loading.enable();
24682 onPagingKeydown : function(e){
24683 var k = e.getKey();
24684 var d = this.getPageData();
24686 var v = this.field.dom.value, pageNum;
24687 if(!v || isNaN(pageNum = parseInt(v, 10))){
24688 this.field.dom.value = d.activePage;
24691 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24692 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24695 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))
24697 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24698 this.field.dom.value = pageNum;
24699 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24702 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24704 var v = this.field.dom.value, pageNum;
24705 var increment = (e.shiftKey) ? 10 : 1;
24706 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24709 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24710 this.field.dom.value = d.activePage;
24713 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24715 this.field.dom.value = parseInt(v, 10) + increment;
24716 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24717 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24724 beforeLoad : function(){
24726 this.loading.disable();
24731 onClick : function(which){
24740 ds.load({params:{start: 0, limit: this.pageSize}});
24743 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24746 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24749 var total = ds.getTotalCount();
24750 var extra = total % this.pageSize;
24751 var lastStart = extra ? (total - extra) : total-this.pageSize;
24752 ds.load({params:{start: lastStart, limit: this.pageSize}});
24755 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24761 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24762 * @param {Roo.data.Store} store The data store to unbind
24764 unbind : function(ds){
24765 ds.un("beforeload", this.beforeLoad, this);
24766 ds.un("load", this.onLoad, this);
24767 ds.un("loadexception", this.onLoadError, this);
24768 ds.un("remove", this.updateInfo, this);
24769 ds.un("add", this.updateInfo, this);
24770 this.ds = undefined;
24774 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24775 * @param {Roo.data.Store} store The data store to bind
24777 bind : function(ds){
24778 ds.on("beforeload", this.beforeLoad, this);
24779 ds.on("load", this.onLoad, this);
24780 ds.on("loadexception", this.onLoadError, this);
24781 ds.on("remove", this.updateInfo, this);
24782 ds.on("add", this.updateInfo, this);
24793 * @class Roo.bootstrap.MessageBar
24794 * @extends Roo.bootstrap.Component
24795 * Bootstrap MessageBar class
24796 * @cfg {String} html contents of the MessageBar
24797 * @cfg {String} weight (info | success | warning | danger) default info
24798 * @cfg {String} beforeClass insert the bar before the given class
24799 * @cfg {Boolean} closable (true | false) default false
24800 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24803 * Create a new Element
24804 * @param {Object} config The config object
24807 Roo.bootstrap.MessageBar = function(config){
24808 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24811 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24817 beforeClass: 'bootstrap-sticky-wrap',
24819 getAutoCreate : function(){
24823 cls: 'alert alert-dismissable alert-' + this.weight,
24828 html: this.html || ''
24834 cfg.cls += ' alert-messages-fixed';
24848 onRender : function(ct, position)
24850 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24853 var cfg = Roo.apply({}, this.getAutoCreate());
24857 cfg.cls += ' ' + this.cls;
24860 cfg.style = this.style;
24862 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24864 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24867 this.el.select('>button.close').on('click', this.hide, this);
24873 if (!this.rendered) {
24879 this.fireEvent('show', this);
24885 if (!this.rendered) {
24891 this.fireEvent('hide', this);
24894 update : function()
24896 // var e = this.el.dom.firstChild;
24898 // if(this.closable){
24899 // e = e.nextSibling;
24902 // e.data = this.html || '';
24904 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24920 * @class Roo.bootstrap.Graph
24921 * @extends Roo.bootstrap.Component
24922 * Bootstrap Graph class
24926 @cfg {String} graphtype bar | vbar | pie
24927 @cfg {number} g_x coodinator | centre x (pie)
24928 @cfg {number} g_y coodinator | centre y (pie)
24929 @cfg {number} g_r radius (pie)
24930 @cfg {number} g_height height of the chart (respected by all elements in the set)
24931 @cfg {number} g_width width of the chart (respected by all elements in the set)
24932 @cfg {Object} title The title of the chart
24935 -opts (object) options for the chart
24937 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24938 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24940 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.
24941 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24943 o stretch (boolean)
24945 -opts (object) options for the pie
24948 o startAngle (number)
24949 o endAngle (number)
24953 * Create a new Input
24954 * @param {Object} config The config object
24957 Roo.bootstrap.Graph = function(config){
24958 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24964 * The img click event for the img.
24965 * @param {Roo.EventObject} e
24971 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24982 //g_colors: this.colors,
24989 getAutoCreate : function(){
25000 onRender : function(ct,position){
25003 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25005 if (typeof(Raphael) == 'undefined') {
25006 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25010 this.raphael = Raphael(this.el.dom);
25012 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25013 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25014 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25015 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25017 r.text(160, 10, "Single Series Chart").attr(txtattr);
25018 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25019 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25020 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25022 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25023 r.barchart(330, 10, 300, 220, data1);
25024 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25025 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25028 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25029 // r.barchart(30, 30, 560, 250, xdata, {
25030 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25031 // axis : "0 0 1 1",
25032 // axisxlabels : xdata
25033 // //yvalues : cols,
25036 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25038 // this.load(null,xdata,{
25039 // axis : "0 0 1 1",
25040 // axisxlabels : xdata
25045 load : function(graphtype,xdata,opts)
25047 this.raphael.clear();
25049 graphtype = this.graphtype;
25054 var r = this.raphael,
25055 fin = function () {
25056 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25058 fout = function () {
25059 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25061 pfin = function() {
25062 this.sector.stop();
25063 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25066 this.label[0].stop();
25067 this.label[0].attr({ r: 7.5 });
25068 this.label[1].attr({ "font-weight": 800 });
25071 pfout = function() {
25072 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25075 this.label[0].animate({ r: 5 }, 500, "bounce");
25076 this.label[1].attr({ "font-weight": 400 });
25082 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25085 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25088 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25089 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25091 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25098 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25103 setTitle: function(o)
25108 initEvents: function() {
25111 this.el.on('click', this.onClick, this);
25115 onClick : function(e)
25117 Roo.log('img onclick');
25118 this.fireEvent('click', this, e);
25130 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25133 * @class Roo.bootstrap.dash.NumberBox
25134 * @extends Roo.bootstrap.Component
25135 * Bootstrap NumberBox class
25136 * @cfg {String} headline Box headline
25137 * @cfg {String} content Box content
25138 * @cfg {String} icon Box icon
25139 * @cfg {String} footer Footer text
25140 * @cfg {String} fhref Footer href
25143 * Create a new NumberBox
25144 * @param {Object} config The config object
25148 Roo.bootstrap.dash.NumberBox = function(config){
25149 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25153 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25162 getAutoCreate : function(){
25166 cls : 'small-box ',
25174 cls : 'roo-headline',
25175 html : this.headline
25179 cls : 'roo-content',
25180 html : this.content
25194 cls : 'ion ' + this.icon
25203 cls : 'small-box-footer',
25204 href : this.fhref || '#',
25208 cfg.cn.push(footer);
25215 onRender : function(ct,position){
25216 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25223 setHeadline: function (value)
25225 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25228 setFooter: function (value, href)
25230 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25233 this.el.select('a.small-box-footer',true).first().attr('href', href);
25238 setContent: function (value)
25240 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25243 initEvents: function()
25257 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25260 * @class Roo.bootstrap.dash.TabBox
25261 * @extends Roo.bootstrap.Component
25262 * Bootstrap TabBox class
25263 * @cfg {String} title Title of the TabBox
25264 * @cfg {String} icon Icon of the TabBox
25265 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25266 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25269 * Create a new TabBox
25270 * @param {Object} config The config object
25274 Roo.bootstrap.dash.TabBox = function(config){
25275 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25280 * When a pane is added
25281 * @param {Roo.bootstrap.dash.TabPane} pane
25285 * @event activatepane
25286 * When a pane is activated
25287 * @param {Roo.bootstrap.dash.TabPane} pane
25289 "activatepane" : true
25297 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25302 tabScrollable : false,
25304 getChildContainer : function()
25306 return this.el.select('.tab-content', true).first();
25309 getAutoCreate : function(){
25313 cls: 'pull-left header',
25321 cls: 'fa ' + this.icon
25327 cls: 'nav nav-tabs pull-right',
25333 if(this.tabScrollable){
25340 cls: 'nav nav-tabs pull-right',
25351 cls: 'nav-tabs-custom',
25356 cls: 'tab-content no-padding',
25364 initEvents : function()
25366 //Roo.log('add add pane handler');
25367 this.on('addpane', this.onAddPane, this);
25370 * Updates the box title
25371 * @param {String} html to set the title to.
25373 setTitle : function(value)
25375 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25377 onAddPane : function(pane)
25379 this.panes.push(pane);
25380 //Roo.log('addpane');
25382 // tabs are rendere left to right..
25383 if(!this.showtabs){
25387 var ctr = this.el.select('.nav-tabs', true).first();
25390 var existing = ctr.select('.nav-tab',true);
25391 var qty = existing.getCount();;
25394 var tab = ctr.createChild({
25396 cls : 'nav-tab' + (qty ? '' : ' active'),
25404 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25407 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25409 pane.el.addClass('active');
25414 onTabClick : function(ev,un,ob,pane)
25416 //Roo.log('tab - prev default');
25417 ev.preventDefault();
25420 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25421 pane.tab.addClass('active');
25422 //Roo.log(pane.title);
25423 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25424 // technically we should have a deactivate event.. but maybe add later.
25425 // and it should not de-activate the selected tab...
25426 this.fireEvent('activatepane', pane);
25427 pane.el.addClass('active');
25428 pane.fireEvent('activate');
25433 getActivePane : function()
25436 Roo.each(this.panes, function(p) {
25437 if(p.el.hasClass('active')){
25458 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25460 * @class Roo.bootstrap.TabPane
25461 * @extends Roo.bootstrap.Component
25462 * Bootstrap TabPane class
25463 * @cfg {Boolean} active (false | true) Default false
25464 * @cfg {String} title title of panel
25468 * Create a new TabPane
25469 * @param {Object} config The config object
25472 Roo.bootstrap.dash.TabPane = function(config){
25473 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25479 * When a pane is activated
25480 * @param {Roo.bootstrap.dash.TabPane} pane
25487 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25492 // the tabBox that this is attached to.
25495 getAutoCreate : function()
25503 cfg.cls += ' active';
25508 initEvents : function()
25510 //Roo.log('trigger add pane handler');
25511 this.parent().fireEvent('addpane', this)
25515 * Updates the tab title
25516 * @param {String} html to set the title to.
25518 setTitle: function(str)
25524 this.tab.select('a', true).first().dom.innerHTML = str;
25541 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25544 * @class Roo.bootstrap.menu.Menu
25545 * @extends Roo.bootstrap.Component
25546 * Bootstrap Menu class - container for Menu
25547 * @cfg {String} html Text of the menu
25548 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25549 * @cfg {String} icon Font awesome icon
25550 * @cfg {String} pos Menu align to (top | bottom) default bottom
25554 * Create a new Menu
25555 * @param {Object} config The config object
25559 Roo.bootstrap.menu.Menu = function(config){
25560 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25564 * @event beforeshow
25565 * Fires before this menu is displayed
25566 * @param {Roo.bootstrap.menu.Menu} this
25570 * @event beforehide
25571 * Fires before this menu is hidden
25572 * @param {Roo.bootstrap.menu.Menu} this
25577 * Fires after this menu is displayed
25578 * @param {Roo.bootstrap.menu.Menu} this
25583 * Fires after this menu is hidden
25584 * @param {Roo.bootstrap.menu.Menu} this
25589 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25590 * @param {Roo.bootstrap.menu.Menu} this
25591 * @param {Roo.EventObject} e
25598 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25602 weight : 'default',
25607 getChildContainer : function() {
25608 if(this.isSubMenu){
25612 return this.el.select('ul.dropdown-menu', true).first();
25615 getAutoCreate : function()
25620 cls : 'roo-menu-text',
25628 cls : 'fa ' + this.icon
25639 cls : 'dropdown-button btn btn-' + this.weight,
25644 cls : 'dropdown-toggle btn btn-' + this.weight,
25654 cls : 'dropdown-menu'
25660 if(this.pos == 'top'){
25661 cfg.cls += ' dropup';
25664 if(this.isSubMenu){
25667 cls : 'dropdown-menu'
25674 onRender : function(ct, position)
25676 this.isSubMenu = ct.hasClass('dropdown-submenu');
25678 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25681 initEvents : function()
25683 if(this.isSubMenu){
25687 this.hidden = true;
25689 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25690 this.triggerEl.on('click', this.onTriggerPress, this);
25692 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25693 this.buttonEl.on('click', this.onClick, this);
25699 if(this.isSubMenu){
25703 return this.el.select('ul.dropdown-menu', true).first();
25706 onClick : function(e)
25708 this.fireEvent("click", this, e);
25711 onTriggerPress : function(e)
25713 if (this.isVisible()) {
25720 isVisible : function(){
25721 return !this.hidden;
25726 this.fireEvent("beforeshow", this);
25728 this.hidden = false;
25729 this.el.addClass('open');
25731 Roo.get(document).on("mouseup", this.onMouseUp, this);
25733 this.fireEvent("show", this);
25740 this.fireEvent("beforehide", this);
25742 this.hidden = true;
25743 this.el.removeClass('open');
25745 Roo.get(document).un("mouseup", this.onMouseUp);
25747 this.fireEvent("hide", this);
25750 onMouseUp : function()
25764 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25767 * @class Roo.bootstrap.menu.Item
25768 * @extends Roo.bootstrap.Component
25769 * Bootstrap MenuItem class
25770 * @cfg {Boolean} submenu (true | false) default false
25771 * @cfg {String} html text of the item
25772 * @cfg {String} href the link
25773 * @cfg {Boolean} disable (true | false) default false
25774 * @cfg {Boolean} preventDefault (true | false) default true
25775 * @cfg {String} icon Font awesome icon
25776 * @cfg {String} pos Submenu align to (left | right) default right
25780 * Create a new Item
25781 * @param {Object} config The config object
25785 Roo.bootstrap.menu.Item = function(config){
25786 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25790 * Fires when the mouse is hovering over this menu
25791 * @param {Roo.bootstrap.menu.Item} this
25792 * @param {Roo.EventObject} e
25797 * Fires when the mouse exits this menu
25798 * @param {Roo.bootstrap.menu.Item} this
25799 * @param {Roo.EventObject} e
25805 * The raw click event for the entire grid.
25806 * @param {Roo.EventObject} e
25812 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25817 preventDefault: true,
25822 getAutoCreate : function()
25827 cls : 'roo-menu-item-text',
25835 cls : 'fa ' + this.icon
25844 href : this.href || '#',
25851 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25855 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25857 if(this.pos == 'left'){
25858 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25865 initEvents : function()
25867 this.el.on('mouseover', this.onMouseOver, this);
25868 this.el.on('mouseout', this.onMouseOut, this);
25870 this.el.select('a', true).first().on('click', this.onClick, this);
25874 onClick : function(e)
25876 if(this.preventDefault){
25877 e.preventDefault();
25880 this.fireEvent("click", this, e);
25883 onMouseOver : function(e)
25885 if(this.submenu && this.pos == 'left'){
25886 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25889 this.fireEvent("mouseover", this, e);
25892 onMouseOut : function(e)
25894 this.fireEvent("mouseout", this, e);
25906 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25909 * @class Roo.bootstrap.menu.Separator
25910 * @extends Roo.bootstrap.Component
25911 * Bootstrap Separator class
25914 * Create a new Separator
25915 * @param {Object} config The config object
25919 Roo.bootstrap.menu.Separator = function(config){
25920 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25923 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25925 getAutoCreate : function(){
25946 * @class Roo.bootstrap.Tooltip
25947 * Bootstrap Tooltip class
25948 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25949 * to determine which dom element triggers the tooltip.
25951 * It needs to add support for additional attributes like tooltip-position
25954 * Create a new Toolti
25955 * @param {Object} config The config object
25958 Roo.bootstrap.Tooltip = function(config){
25959 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25961 this.alignment = Roo.bootstrap.Tooltip.alignment;
25963 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25964 this.alignment = config.alignment;
25969 Roo.apply(Roo.bootstrap.Tooltip, {
25971 * @function init initialize tooltip monitoring.
25975 currentTip : false,
25976 currentRegion : false,
25982 Roo.get(document).on('mouseover', this.enter ,this);
25983 Roo.get(document).on('mouseout', this.leave, this);
25986 this.currentTip = new Roo.bootstrap.Tooltip();
25989 enter : function(ev)
25991 var dom = ev.getTarget();
25993 //Roo.log(['enter',dom]);
25994 var el = Roo.fly(dom);
25995 if (this.currentEl) {
25997 //Roo.log(this.currentEl);
25998 //Roo.log(this.currentEl.contains(dom));
25999 if (this.currentEl == el) {
26002 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26008 if (this.currentTip.el) {
26009 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26013 if(!el || el.dom == document){
26019 // you can not look for children, as if el is the body.. then everythign is the child..
26020 if (!el.attr('tooltip')) { //
26021 if (!el.select("[tooltip]").elements.length) {
26024 // is the mouse over this child...?
26025 bindEl = el.select("[tooltip]").first();
26026 var xy = ev.getXY();
26027 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26028 //Roo.log("not in region.");
26031 //Roo.log("child element over..");
26034 this.currentEl = bindEl;
26035 this.currentTip.bind(bindEl);
26036 this.currentRegion = Roo.lib.Region.getRegion(dom);
26037 this.currentTip.enter();
26040 leave : function(ev)
26042 var dom = ev.getTarget();
26043 //Roo.log(['leave',dom]);
26044 if (!this.currentEl) {
26049 if (dom != this.currentEl.dom) {
26052 var xy = ev.getXY();
26053 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26056 // only activate leave if mouse cursor is outside... bounding box..
26061 if (this.currentTip) {
26062 this.currentTip.leave();
26064 //Roo.log('clear currentEl');
26065 this.currentEl = false;
26070 'left' : ['r-l', [-2,0], 'right'],
26071 'right' : ['l-r', [2,0], 'left'],
26072 'bottom' : ['t-b', [0,2], 'top'],
26073 'top' : [ 'b-t', [0,-2], 'bottom']
26079 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26084 delay : null, // can be { show : 300 , hide: 500}
26088 hoverState : null, //???
26090 placement : 'bottom',
26094 getAutoCreate : function(){
26101 cls : 'tooltip-arrow'
26104 cls : 'tooltip-inner'
26111 bind : function(el)
26117 enter : function () {
26119 if (this.timeout != null) {
26120 clearTimeout(this.timeout);
26123 this.hoverState = 'in';
26124 //Roo.log("enter - show");
26125 if (!this.delay || !this.delay.show) {
26130 this.timeout = setTimeout(function () {
26131 if (_t.hoverState == 'in') {
26134 }, this.delay.show);
26138 clearTimeout(this.timeout);
26140 this.hoverState = 'out';
26141 if (!this.delay || !this.delay.hide) {
26147 this.timeout = setTimeout(function () {
26148 //Roo.log("leave - timeout");
26150 if (_t.hoverState == 'out') {
26152 Roo.bootstrap.Tooltip.currentEl = false;
26157 show : function (msg)
26160 this.render(document.body);
26163 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26165 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26167 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26169 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26171 var placement = typeof this.placement == 'function' ?
26172 this.placement.call(this, this.el, on_el) :
26175 var autoToken = /\s?auto?\s?/i;
26176 var autoPlace = autoToken.test(placement);
26178 placement = placement.replace(autoToken, '') || 'top';
26182 //this.el.setXY([0,0]);
26184 //this.el.dom.style.display='block';
26186 //this.el.appendTo(on_el);
26188 var p = this.getPosition();
26189 var box = this.el.getBox();
26195 var align = this.alignment[placement];
26197 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26199 if(placement == 'top' || placement == 'bottom'){
26201 placement = 'right';
26204 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26205 placement = 'left';
26208 var scroll = Roo.select('body', true).first().getScroll();
26210 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26216 this.el.alignTo(this.bindEl, align[0],align[1]);
26217 //var arrow = this.el.select('.arrow',true).first();
26218 //arrow.set(align[2],
26220 this.el.addClass(placement);
26222 this.el.addClass('in fade');
26224 this.hoverState = null;
26226 if (this.el.hasClass('fade')) {
26237 //this.el.setXY([0,0]);
26238 this.el.removeClass('in');
26254 * @class Roo.bootstrap.LocationPicker
26255 * @extends Roo.bootstrap.Component
26256 * Bootstrap LocationPicker class
26257 * @cfg {Number} latitude Position when init default 0
26258 * @cfg {Number} longitude Position when init default 0
26259 * @cfg {Number} zoom default 15
26260 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26261 * @cfg {Boolean} mapTypeControl default false
26262 * @cfg {Boolean} disableDoubleClickZoom default false
26263 * @cfg {Boolean} scrollwheel default true
26264 * @cfg {Boolean} streetViewControl default false
26265 * @cfg {Number} radius default 0
26266 * @cfg {String} locationName
26267 * @cfg {Boolean} draggable default true
26268 * @cfg {Boolean} enableAutocomplete default false
26269 * @cfg {Boolean} enableReverseGeocode default true
26270 * @cfg {String} markerTitle
26273 * Create a new LocationPicker
26274 * @param {Object} config The config object
26278 Roo.bootstrap.LocationPicker = function(config){
26280 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26285 * Fires when the picker initialized.
26286 * @param {Roo.bootstrap.LocationPicker} this
26287 * @param {Google Location} location
26291 * @event positionchanged
26292 * Fires when the picker position changed.
26293 * @param {Roo.bootstrap.LocationPicker} this
26294 * @param {Google Location} location
26296 positionchanged : true,
26299 * Fires when the map resize.
26300 * @param {Roo.bootstrap.LocationPicker} this
26305 * Fires when the map show.
26306 * @param {Roo.bootstrap.LocationPicker} this
26311 * Fires when the map hide.
26312 * @param {Roo.bootstrap.LocationPicker} this
26317 * Fires when click the map.
26318 * @param {Roo.bootstrap.LocationPicker} this
26319 * @param {Map event} e
26323 * @event mapRightClick
26324 * Fires when right click the map.
26325 * @param {Roo.bootstrap.LocationPicker} this
26326 * @param {Map event} e
26328 mapRightClick : true,
26330 * @event markerClick
26331 * Fires when click the marker.
26332 * @param {Roo.bootstrap.LocationPicker} this
26333 * @param {Map event} e
26335 markerClick : true,
26337 * @event markerRightClick
26338 * Fires when right click the marker.
26339 * @param {Roo.bootstrap.LocationPicker} this
26340 * @param {Map event} e
26342 markerRightClick : true,
26344 * @event OverlayViewDraw
26345 * Fires when OverlayView Draw
26346 * @param {Roo.bootstrap.LocationPicker} this
26348 OverlayViewDraw : true,
26350 * @event OverlayViewOnAdd
26351 * Fires when OverlayView Draw
26352 * @param {Roo.bootstrap.LocationPicker} this
26354 OverlayViewOnAdd : true,
26356 * @event OverlayViewOnRemove
26357 * Fires when OverlayView Draw
26358 * @param {Roo.bootstrap.LocationPicker} this
26360 OverlayViewOnRemove : true,
26362 * @event OverlayViewShow
26363 * Fires when OverlayView Draw
26364 * @param {Roo.bootstrap.LocationPicker} this
26365 * @param {Pixel} cpx
26367 OverlayViewShow : true,
26369 * @event OverlayViewHide
26370 * Fires when OverlayView Draw
26371 * @param {Roo.bootstrap.LocationPicker} this
26373 OverlayViewHide : true,
26375 * @event loadexception
26376 * Fires when load google lib failed.
26377 * @param {Roo.bootstrap.LocationPicker} this
26379 loadexception : true
26384 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26386 gMapContext: false,
26392 mapTypeControl: false,
26393 disableDoubleClickZoom: false,
26395 streetViewControl: false,
26399 enableAutocomplete: false,
26400 enableReverseGeocode: true,
26403 getAutoCreate: function()
26408 cls: 'roo-location-picker'
26414 initEvents: function(ct, position)
26416 if(!this.el.getWidth() || this.isApplied()){
26420 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26425 initial: function()
26427 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26428 this.fireEvent('loadexception', this);
26432 if(!this.mapTypeId){
26433 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26436 this.gMapContext = this.GMapContext();
26438 this.initOverlayView();
26440 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26444 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26445 _this.setPosition(_this.gMapContext.marker.position);
26448 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26449 _this.fireEvent('mapClick', this, event);
26453 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26454 _this.fireEvent('mapRightClick', this, event);
26458 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26459 _this.fireEvent('markerClick', this, event);
26463 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26464 _this.fireEvent('markerRightClick', this, event);
26468 this.setPosition(this.gMapContext.location);
26470 this.fireEvent('initial', this, this.gMapContext.location);
26473 initOverlayView: function()
26477 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26481 _this.fireEvent('OverlayViewDraw', _this);
26486 _this.fireEvent('OverlayViewOnAdd', _this);
26489 onRemove: function()
26491 _this.fireEvent('OverlayViewOnRemove', _this);
26494 show: function(cpx)
26496 _this.fireEvent('OverlayViewShow', _this, cpx);
26501 _this.fireEvent('OverlayViewHide', _this);
26507 fromLatLngToContainerPixel: function(event)
26509 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26512 isApplied: function()
26514 return this.getGmapContext() == false ? false : true;
26517 getGmapContext: function()
26519 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26522 GMapContext: function()
26524 var position = new google.maps.LatLng(this.latitude, this.longitude);
26526 var _map = new google.maps.Map(this.el.dom, {
26529 mapTypeId: this.mapTypeId,
26530 mapTypeControl: this.mapTypeControl,
26531 disableDoubleClickZoom: this.disableDoubleClickZoom,
26532 scrollwheel: this.scrollwheel,
26533 streetViewControl: this.streetViewControl,
26534 locationName: this.locationName,
26535 draggable: this.draggable,
26536 enableAutocomplete: this.enableAutocomplete,
26537 enableReverseGeocode: this.enableReverseGeocode
26540 var _marker = new google.maps.Marker({
26541 position: position,
26543 title: this.markerTitle,
26544 draggable: this.draggable
26551 location: position,
26552 radius: this.radius,
26553 locationName: this.locationName,
26554 addressComponents: {
26555 formatted_address: null,
26556 addressLine1: null,
26557 addressLine2: null,
26559 streetNumber: null,
26563 stateOrProvince: null
26566 domContainer: this.el.dom,
26567 geodecoder: new google.maps.Geocoder()
26571 drawCircle: function(center, radius, options)
26573 if (this.gMapContext.circle != null) {
26574 this.gMapContext.circle.setMap(null);
26578 options = Roo.apply({}, options, {
26579 strokeColor: "#0000FF",
26580 strokeOpacity: .35,
26582 fillColor: "#0000FF",
26586 options.map = this.gMapContext.map;
26587 options.radius = radius;
26588 options.center = center;
26589 this.gMapContext.circle = new google.maps.Circle(options);
26590 return this.gMapContext.circle;
26596 setPosition: function(location)
26598 this.gMapContext.location = location;
26599 this.gMapContext.marker.setPosition(location);
26600 this.gMapContext.map.panTo(location);
26601 this.drawCircle(location, this.gMapContext.radius, {});
26605 if (this.gMapContext.settings.enableReverseGeocode) {
26606 this.gMapContext.geodecoder.geocode({
26607 latLng: this.gMapContext.location
26608 }, function(results, status) {
26610 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26611 _this.gMapContext.locationName = results[0].formatted_address;
26612 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26614 _this.fireEvent('positionchanged', this, location);
26621 this.fireEvent('positionchanged', this, location);
26626 google.maps.event.trigger(this.gMapContext.map, "resize");
26628 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26630 this.fireEvent('resize', this);
26633 setPositionByLatLng: function(latitude, longitude)
26635 this.setPosition(new google.maps.LatLng(latitude, longitude));
26638 getCurrentPosition: function()
26641 latitude: this.gMapContext.location.lat(),
26642 longitude: this.gMapContext.location.lng()
26646 getAddressName: function()
26648 return this.gMapContext.locationName;
26651 getAddressComponents: function()
26653 return this.gMapContext.addressComponents;
26656 address_component_from_google_geocode: function(address_components)
26660 for (var i = 0; i < address_components.length; i++) {
26661 var component = address_components[i];
26662 if (component.types.indexOf("postal_code") >= 0) {
26663 result.postalCode = component.short_name;
26664 } else if (component.types.indexOf("street_number") >= 0) {
26665 result.streetNumber = component.short_name;
26666 } else if (component.types.indexOf("route") >= 0) {
26667 result.streetName = component.short_name;
26668 } else if (component.types.indexOf("neighborhood") >= 0) {
26669 result.city = component.short_name;
26670 } else if (component.types.indexOf("locality") >= 0) {
26671 result.city = component.short_name;
26672 } else if (component.types.indexOf("sublocality") >= 0) {
26673 result.district = component.short_name;
26674 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26675 result.stateOrProvince = component.short_name;
26676 } else if (component.types.indexOf("country") >= 0) {
26677 result.country = component.short_name;
26681 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26682 result.addressLine2 = "";
26686 setZoomLevel: function(zoom)
26688 this.gMapContext.map.setZoom(zoom);
26701 this.fireEvent('show', this);
26712 this.fireEvent('hide', this);
26717 Roo.apply(Roo.bootstrap.LocationPicker, {
26719 OverlayView : function(map, options)
26721 options = options || {};
26735 * @class Roo.bootstrap.Alert
26736 * @extends Roo.bootstrap.Component
26737 * Bootstrap Alert class
26738 * @cfg {String} title The title of alert
26739 * @cfg {String} html The content of alert
26740 * @cfg {String} weight ( success | info | warning | danger )
26741 * @cfg {String} faicon font-awesomeicon
26744 * Create a new alert
26745 * @param {Object} config The config object
26749 Roo.bootstrap.Alert = function(config){
26750 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26754 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26761 getAutoCreate : function()
26770 cls : 'roo-alert-icon'
26775 cls : 'roo-alert-title',
26780 cls : 'roo-alert-text',
26787 cfg.cn[0].cls += ' fa ' + this.faicon;
26791 cfg.cls += ' alert-' + this.weight;
26797 initEvents: function()
26799 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26802 setTitle : function(str)
26804 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26807 setText : function(str)
26809 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26812 setWeight : function(weight)
26815 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26818 this.weight = weight;
26820 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26823 setIcon : function(icon)
26826 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26829 this.faicon = icon;
26831 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26852 * @class Roo.bootstrap.UploadCropbox
26853 * @extends Roo.bootstrap.Component
26854 * Bootstrap UploadCropbox class
26855 * @cfg {String} emptyText show when image has been loaded
26856 * @cfg {String} rotateNotify show when image too small to rotate
26857 * @cfg {Number} errorTimeout default 3000
26858 * @cfg {Number} minWidth default 300
26859 * @cfg {Number} minHeight default 300
26860 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26861 * @cfg {Boolean} isDocument (true|false) default false
26862 * @cfg {String} url action url
26863 * @cfg {String} paramName default 'imageUpload'
26864 * @cfg {String} method default POST
26865 * @cfg {Boolean} loadMask (true|false) default true
26866 * @cfg {Boolean} loadingText default 'Loading...'
26869 * Create a new UploadCropbox
26870 * @param {Object} config The config object
26873 Roo.bootstrap.UploadCropbox = function(config){
26874 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26878 * @event beforeselectfile
26879 * Fire before select file
26880 * @param {Roo.bootstrap.UploadCropbox} this
26882 "beforeselectfile" : true,
26885 * Fire after initEvent
26886 * @param {Roo.bootstrap.UploadCropbox} this
26891 * Fire after initEvent
26892 * @param {Roo.bootstrap.UploadCropbox} this
26893 * @param {String} data
26898 * Fire when preparing the file data
26899 * @param {Roo.bootstrap.UploadCropbox} this
26900 * @param {Object} file
26905 * Fire when get exception
26906 * @param {Roo.bootstrap.UploadCropbox} this
26907 * @param {XMLHttpRequest} xhr
26909 "exception" : true,
26911 * @event beforeloadcanvas
26912 * Fire before load the canvas
26913 * @param {Roo.bootstrap.UploadCropbox} this
26914 * @param {String} src
26916 "beforeloadcanvas" : true,
26919 * Fire when trash image
26920 * @param {Roo.bootstrap.UploadCropbox} this
26925 * Fire when download the image
26926 * @param {Roo.bootstrap.UploadCropbox} this
26930 * @event footerbuttonclick
26931 * Fire when footerbuttonclick
26932 * @param {Roo.bootstrap.UploadCropbox} this
26933 * @param {String} type
26935 "footerbuttonclick" : true,
26939 * @param {Roo.bootstrap.UploadCropbox} this
26944 * Fire when rotate the image
26945 * @param {Roo.bootstrap.UploadCropbox} this
26946 * @param {String} pos
26951 * Fire when inspect the file
26952 * @param {Roo.bootstrap.UploadCropbox} this
26953 * @param {Object} file
26958 * Fire when xhr upload the file
26959 * @param {Roo.bootstrap.UploadCropbox} this
26960 * @param {Object} data
26965 * Fire when arrange the file data
26966 * @param {Roo.bootstrap.UploadCropbox} this
26967 * @param {Object} formData
26972 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26975 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26977 emptyText : 'Click to upload image',
26978 rotateNotify : 'Image is too small to rotate',
26979 errorTimeout : 3000,
26993 cropType : 'image/jpeg',
26995 canvasLoaded : false,
26996 isDocument : false,
26998 paramName : 'imageUpload',
27000 loadingText : 'Loading...',
27003 getAutoCreate : function()
27007 cls : 'roo-upload-cropbox',
27011 cls : 'roo-upload-cropbox-selector',
27016 cls : 'roo-upload-cropbox-body',
27017 style : 'cursor:pointer',
27021 cls : 'roo-upload-cropbox-preview'
27025 cls : 'roo-upload-cropbox-thumb'
27029 cls : 'roo-upload-cropbox-empty-notify',
27030 html : this.emptyText
27034 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27035 html : this.rotateNotify
27041 cls : 'roo-upload-cropbox-footer',
27044 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27054 onRender : function(ct, position)
27056 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27058 if (this.buttons.length) {
27060 Roo.each(this.buttons, function(bb) {
27062 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27064 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27070 this.maskEl = this.el;
27074 initEvents : function()
27076 this.urlAPI = (window.createObjectURL && window) ||
27077 (window.URL && URL.revokeObjectURL && URL) ||
27078 (window.webkitURL && webkitURL);
27080 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27081 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27083 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27084 this.selectorEl.hide();
27086 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27087 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27089 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27090 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27091 this.thumbEl.hide();
27093 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27094 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27096 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27097 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27098 this.errorEl.hide();
27100 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27101 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27102 this.footerEl.hide();
27104 this.setThumbBoxSize();
27110 this.fireEvent('initial', this);
27117 window.addEventListener("resize", function() { _this.resize(); } );
27119 this.bodyEl.on('click', this.beforeSelectFile, this);
27122 this.bodyEl.on('touchstart', this.onTouchStart, this);
27123 this.bodyEl.on('touchmove', this.onTouchMove, this);
27124 this.bodyEl.on('touchend', this.onTouchEnd, this);
27128 this.bodyEl.on('mousedown', this.onMouseDown, this);
27129 this.bodyEl.on('mousemove', this.onMouseMove, this);
27130 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27131 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27132 Roo.get(document).on('mouseup', this.onMouseUp, this);
27135 this.selectorEl.on('change', this.onFileSelected, this);
27141 this.baseScale = 1;
27143 this.baseRotate = 1;
27144 this.dragable = false;
27145 this.pinching = false;
27148 this.cropData = false;
27149 this.notifyEl.dom.innerHTML = this.emptyText;
27151 this.selectorEl.dom.value = '';
27155 resize : function()
27157 if(this.fireEvent('resize', this) != false){
27158 this.setThumbBoxPosition();
27159 this.setCanvasPosition();
27163 onFooterButtonClick : function(e, el, o, type)
27166 case 'rotate-left' :
27167 this.onRotateLeft(e);
27169 case 'rotate-right' :
27170 this.onRotateRight(e);
27173 this.beforeSelectFile(e);
27188 this.fireEvent('footerbuttonclick', this, type);
27191 beforeSelectFile : function(e)
27193 e.preventDefault();
27195 if(this.fireEvent('beforeselectfile', this) != false){
27196 this.selectorEl.dom.click();
27200 onFileSelected : function(e)
27202 e.preventDefault();
27204 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27208 var file = this.selectorEl.dom.files[0];
27210 if(this.fireEvent('inspect', this, file) != false){
27211 this.prepare(file);
27216 trash : function(e)
27218 this.fireEvent('trash', this);
27221 download : function(e)
27223 this.fireEvent('download', this);
27226 loadCanvas : function(src)
27228 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27232 this.imageEl = document.createElement('img');
27236 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27238 this.imageEl.src = src;
27242 onLoadCanvas : function()
27244 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27245 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27247 this.bodyEl.un('click', this.beforeSelectFile, this);
27249 this.notifyEl.hide();
27250 this.thumbEl.show();
27251 this.footerEl.show();
27253 this.baseRotateLevel();
27255 if(this.isDocument){
27256 this.setThumbBoxSize();
27259 this.setThumbBoxPosition();
27261 this.baseScaleLevel();
27267 this.canvasLoaded = true;
27270 this.maskEl.unmask();
27275 setCanvasPosition : function()
27277 if(!this.canvasEl){
27281 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27282 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27284 this.previewEl.setLeft(pw);
27285 this.previewEl.setTop(ph);
27289 onMouseDown : function(e)
27293 this.dragable = true;
27294 this.pinching = false;
27296 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27297 this.dragable = false;
27301 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27302 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27306 onMouseMove : function(e)
27310 if(!this.canvasLoaded){
27314 if (!this.dragable){
27318 var minX = Math.ceil(this.thumbEl.getLeft(true));
27319 var minY = Math.ceil(this.thumbEl.getTop(true));
27321 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27322 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27324 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27325 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27327 x = x - this.mouseX;
27328 y = y - this.mouseY;
27330 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27331 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27333 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27334 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27336 this.previewEl.setLeft(bgX);
27337 this.previewEl.setTop(bgY);
27339 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27340 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27343 onMouseUp : function(e)
27347 this.dragable = false;
27350 onMouseWheel : function(e)
27354 this.startScale = this.scale;
27356 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27358 if(!this.zoomable()){
27359 this.scale = this.startScale;
27368 zoomable : function()
27370 var minScale = this.thumbEl.getWidth() / this.minWidth;
27372 if(this.minWidth < this.minHeight){
27373 minScale = this.thumbEl.getHeight() / this.minHeight;
27376 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27377 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27381 (this.rotate == 0 || this.rotate == 180) &&
27383 width > this.imageEl.OriginWidth ||
27384 height > this.imageEl.OriginHeight ||
27385 (width < this.minWidth && height < this.minHeight)
27393 (this.rotate == 90 || this.rotate == 270) &&
27395 width > this.imageEl.OriginWidth ||
27396 height > this.imageEl.OriginHeight ||
27397 (width < this.minHeight && height < this.minWidth)
27404 !this.isDocument &&
27405 (this.rotate == 0 || this.rotate == 180) &&
27407 width < this.minWidth ||
27408 width > this.imageEl.OriginWidth ||
27409 height < this.minHeight ||
27410 height > this.imageEl.OriginHeight
27417 !this.isDocument &&
27418 (this.rotate == 90 || this.rotate == 270) &&
27420 width < this.minHeight ||
27421 width > this.imageEl.OriginWidth ||
27422 height < this.minWidth ||
27423 height > this.imageEl.OriginHeight
27433 onRotateLeft : function(e)
27435 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27437 var minScale = this.thumbEl.getWidth() / this.minWidth;
27439 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27440 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27442 this.startScale = this.scale;
27444 while (this.getScaleLevel() < minScale){
27446 this.scale = this.scale + 1;
27448 if(!this.zoomable()){
27453 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27454 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27459 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27466 this.scale = this.startScale;
27468 this.onRotateFail();
27473 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27475 if(this.isDocument){
27476 this.setThumbBoxSize();
27477 this.setThumbBoxPosition();
27478 this.setCanvasPosition();
27483 this.fireEvent('rotate', this, 'left');
27487 onRotateRight : function(e)
27489 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27491 var minScale = this.thumbEl.getWidth() / this.minWidth;
27493 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27494 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27496 this.startScale = this.scale;
27498 while (this.getScaleLevel() < minScale){
27500 this.scale = this.scale + 1;
27502 if(!this.zoomable()){
27507 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27508 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27513 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27520 this.scale = this.startScale;
27522 this.onRotateFail();
27527 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27529 if(this.isDocument){
27530 this.setThumbBoxSize();
27531 this.setThumbBoxPosition();
27532 this.setCanvasPosition();
27537 this.fireEvent('rotate', this, 'right');
27540 onRotateFail : function()
27542 this.errorEl.show(true);
27546 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27551 this.previewEl.dom.innerHTML = '';
27553 var canvasEl = document.createElement("canvas");
27555 var contextEl = canvasEl.getContext("2d");
27557 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27558 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27559 var center = this.imageEl.OriginWidth / 2;
27561 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27562 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27563 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27564 center = this.imageEl.OriginHeight / 2;
27567 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27569 contextEl.translate(center, center);
27570 contextEl.rotate(this.rotate * Math.PI / 180);
27572 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27574 this.canvasEl = document.createElement("canvas");
27576 this.contextEl = this.canvasEl.getContext("2d");
27578 switch (this.rotate) {
27581 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27582 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27584 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27589 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27590 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27592 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27593 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);
27597 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27602 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27603 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27605 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27606 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);
27610 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);
27615 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27616 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27618 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27619 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27623 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);
27630 this.previewEl.appendChild(this.canvasEl);
27632 this.setCanvasPosition();
27637 if(!this.canvasLoaded){
27641 var imageCanvas = document.createElement("canvas");
27643 var imageContext = imageCanvas.getContext("2d");
27645 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27646 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27648 var center = imageCanvas.width / 2;
27650 imageContext.translate(center, center);
27652 imageContext.rotate(this.rotate * Math.PI / 180);
27654 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27656 var canvas = document.createElement("canvas");
27658 var context = canvas.getContext("2d");
27660 canvas.width = this.minWidth;
27661 canvas.height = this.minHeight;
27663 switch (this.rotate) {
27666 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27667 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27669 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27670 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27672 var targetWidth = this.minWidth - 2 * x;
27673 var targetHeight = this.minHeight - 2 * y;
27677 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27678 scale = targetWidth / width;
27681 if(x > 0 && y == 0){
27682 scale = targetHeight / height;
27685 if(x > 0 && y > 0){
27686 scale = targetWidth / width;
27688 if(width < height){
27689 scale = targetHeight / height;
27693 context.scale(scale, scale);
27695 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27696 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27698 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27699 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27701 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27706 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27707 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27709 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27710 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27712 var targetWidth = this.minWidth - 2 * x;
27713 var targetHeight = this.minHeight - 2 * y;
27717 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27718 scale = targetWidth / width;
27721 if(x > 0 && y == 0){
27722 scale = targetHeight / height;
27725 if(x > 0 && y > 0){
27726 scale = targetWidth / width;
27728 if(width < height){
27729 scale = targetHeight / height;
27733 context.scale(scale, scale);
27735 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27736 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27738 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27739 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27741 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27743 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27748 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27749 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27751 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27752 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27754 var targetWidth = this.minWidth - 2 * x;
27755 var targetHeight = this.minHeight - 2 * y;
27759 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27760 scale = targetWidth / width;
27763 if(x > 0 && y == 0){
27764 scale = targetHeight / height;
27767 if(x > 0 && y > 0){
27768 scale = targetWidth / width;
27770 if(width < height){
27771 scale = targetHeight / height;
27775 context.scale(scale, scale);
27777 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27778 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27780 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27781 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27783 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27784 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27786 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27791 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27792 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27794 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27795 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27797 var targetWidth = this.minWidth - 2 * x;
27798 var targetHeight = this.minHeight - 2 * y;
27802 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27803 scale = targetWidth / width;
27806 if(x > 0 && y == 0){
27807 scale = targetHeight / height;
27810 if(x > 0 && y > 0){
27811 scale = targetWidth / width;
27813 if(width < height){
27814 scale = targetHeight / height;
27818 context.scale(scale, scale);
27820 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27821 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27823 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27824 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27826 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27828 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27835 this.cropData = canvas.toDataURL(this.cropType);
27837 if(this.fireEvent('crop', this, this.cropData) !== false){
27838 this.process(this.file, this.cropData);
27845 setThumbBoxSize : function()
27849 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27850 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27851 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27853 this.minWidth = width;
27854 this.minHeight = height;
27856 if(this.rotate == 90 || this.rotate == 270){
27857 this.minWidth = height;
27858 this.minHeight = width;
27863 width = Math.ceil(this.minWidth * height / this.minHeight);
27865 if(this.minWidth > this.minHeight){
27867 height = Math.ceil(this.minHeight * width / this.minWidth);
27870 this.thumbEl.setStyle({
27871 width : width + 'px',
27872 height : height + 'px'
27879 setThumbBoxPosition : function()
27881 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27882 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27884 this.thumbEl.setLeft(x);
27885 this.thumbEl.setTop(y);
27889 baseRotateLevel : function()
27891 this.baseRotate = 1;
27894 typeof(this.exif) != 'undefined' &&
27895 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27896 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27898 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27901 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27905 baseScaleLevel : function()
27909 if(this.isDocument){
27911 if(this.baseRotate == 6 || this.baseRotate == 8){
27913 height = this.thumbEl.getHeight();
27914 this.baseScale = height / this.imageEl.OriginWidth;
27916 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27917 width = this.thumbEl.getWidth();
27918 this.baseScale = width / this.imageEl.OriginHeight;
27924 height = this.thumbEl.getHeight();
27925 this.baseScale = height / this.imageEl.OriginHeight;
27927 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27928 width = this.thumbEl.getWidth();
27929 this.baseScale = width / this.imageEl.OriginWidth;
27935 if(this.baseRotate == 6 || this.baseRotate == 8){
27937 width = this.thumbEl.getHeight();
27938 this.baseScale = width / this.imageEl.OriginHeight;
27940 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27941 height = this.thumbEl.getWidth();
27942 this.baseScale = height / this.imageEl.OriginHeight;
27945 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27946 height = this.thumbEl.getWidth();
27947 this.baseScale = height / this.imageEl.OriginHeight;
27949 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27950 width = this.thumbEl.getHeight();
27951 this.baseScale = width / this.imageEl.OriginWidth;
27958 width = this.thumbEl.getWidth();
27959 this.baseScale = width / this.imageEl.OriginWidth;
27961 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27962 height = this.thumbEl.getHeight();
27963 this.baseScale = height / this.imageEl.OriginHeight;
27966 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27968 height = this.thumbEl.getHeight();
27969 this.baseScale = height / this.imageEl.OriginHeight;
27971 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27972 width = this.thumbEl.getWidth();
27973 this.baseScale = width / this.imageEl.OriginWidth;
27981 getScaleLevel : function()
27983 return this.baseScale * Math.pow(1.1, this.scale);
27986 onTouchStart : function(e)
27988 if(!this.canvasLoaded){
27989 this.beforeSelectFile(e);
27993 var touches = e.browserEvent.touches;
27999 if(touches.length == 1){
28000 this.onMouseDown(e);
28004 if(touches.length != 2){
28010 for(var i = 0, finger; finger = touches[i]; i++){
28011 coords.push(finger.pageX, finger.pageY);
28014 var x = Math.pow(coords[0] - coords[2], 2);
28015 var y = Math.pow(coords[1] - coords[3], 2);
28017 this.startDistance = Math.sqrt(x + y);
28019 this.startScale = this.scale;
28021 this.pinching = true;
28022 this.dragable = false;
28026 onTouchMove : function(e)
28028 if(!this.pinching && !this.dragable){
28032 var touches = e.browserEvent.touches;
28039 this.onMouseMove(e);
28045 for(var i = 0, finger; finger = touches[i]; i++){
28046 coords.push(finger.pageX, finger.pageY);
28049 var x = Math.pow(coords[0] - coords[2], 2);
28050 var y = Math.pow(coords[1] - coords[3], 2);
28052 this.endDistance = Math.sqrt(x + y);
28054 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28056 if(!this.zoomable()){
28057 this.scale = this.startScale;
28065 onTouchEnd : function(e)
28067 this.pinching = false;
28068 this.dragable = false;
28072 process : function(file, crop)
28075 this.maskEl.mask(this.loadingText);
28078 this.xhr = new XMLHttpRequest();
28080 file.xhr = this.xhr;
28082 this.xhr.open(this.method, this.url, true);
28085 "Accept": "application/json",
28086 "Cache-Control": "no-cache",
28087 "X-Requested-With": "XMLHttpRequest"
28090 for (var headerName in headers) {
28091 var headerValue = headers[headerName];
28093 this.xhr.setRequestHeader(headerName, headerValue);
28099 this.xhr.onload = function()
28101 _this.xhrOnLoad(_this.xhr);
28104 this.xhr.onerror = function()
28106 _this.xhrOnError(_this.xhr);
28109 var formData = new FormData();
28111 formData.append('returnHTML', 'NO');
28114 formData.append('crop', crop);
28117 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28118 formData.append(this.paramName, file, file.name);
28121 if(typeof(file.filename) != 'undefined'){
28122 formData.append('filename', file.filename);
28125 if(typeof(file.mimetype) != 'undefined'){
28126 formData.append('mimetype', file.mimetype);
28129 if(this.fireEvent('arrange', this, formData) != false){
28130 this.xhr.send(formData);
28134 xhrOnLoad : function(xhr)
28137 this.maskEl.unmask();
28140 if (xhr.readyState !== 4) {
28141 this.fireEvent('exception', this, xhr);
28145 var response = Roo.decode(xhr.responseText);
28147 if(!response.success){
28148 this.fireEvent('exception', this, xhr);
28152 var response = Roo.decode(xhr.responseText);
28154 this.fireEvent('upload', this, response);
28158 xhrOnError : function()
28161 this.maskEl.unmask();
28164 Roo.log('xhr on error');
28166 var response = Roo.decode(xhr.responseText);
28172 prepare : function(file)
28175 this.maskEl.mask(this.loadingText);
28181 if(typeof(file) === 'string'){
28182 this.loadCanvas(file);
28186 if(!file || !this.urlAPI){
28191 this.cropType = file.type;
28195 if(this.fireEvent('prepare', this, this.file) != false){
28197 var reader = new FileReader();
28199 reader.onload = function (e) {
28200 if (e.target.error) {
28201 Roo.log(e.target.error);
28205 var buffer = e.target.result,
28206 dataView = new DataView(buffer),
28208 maxOffset = dataView.byteLength - 4,
28212 if (dataView.getUint16(0) === 0xffd8) {
28213 while (offset < maxOffset) {
28214 markerBytes = dataView.getUint16(offset);
28216 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28217 markerLength = dataView.getUint16(offset + 2) + 2;
28218 if (offset + markerLength > dataView.byteLength) {
28219 Roo.log('Invalid meta data: Invalid segment size.');
28223 if(markerBytes == 0xffe1){
28224 _this.parseExifData(
28231 offset += markerLength;
28241 var url = _this.urlAPI.createObjectURL(_this.file);
28243 _this.loadCanvas(url);
28248 reader.readAsArrayBuffer(this.file);
28254 parseExifData : function(dataView, offset, length)
28256 var tiffOffset = offset + 10,
28260 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28261 // No Exif data, might be XMP data instead
28265 // Check for the ASCII code for "Exif" (0x45786966):
28266 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28267 // No Exif data, might be XMP data instead
28270 if (tiffOffset + 8 > dataView.byteLength) {
28271 Roo.log('Invalid Exif data: Invalid segment size.');
28274 // Check for the two null bytes:
28275 if (dataView.getUint16(offset + 8) !== 0x0000) {
28276 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28279 // Check the byte alignment:
28280 switch (dataView.getUint16(tiffOffset)) {
28282 littleEndian = true;
28285 littleEndian = false;
28288 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28291 // Check for the TIFF tag marker (0x002A):
28292 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28293 Roo.log('Invalid Exif data: Missing TIFF marker.');
28296 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28297 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28299 this.parseExifTags(
28302 tiffOffset + dirOffset,
28307 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28312 if (dirOffset + 6 > dataView.byteLength) {
28313 Roo.log('Invalid Exif data: Invalid directory offset.');
28316 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28317 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28318 if (dirEndOffset + 4 > dataView.byteLength) {
28319 Roo.log('Invalid Exif data: Invalid directory size.');
28322 for (i = 0; i < tagsNumber; i += 1) {
28326 dirOffset + 2 + 12 * i, // tag offset
28330 // Return the offset to the next directory:
28331 return dataView.getUint32(dirEndOffset, littleEndian);
28334 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28336 var tag = dataView.getUint16(offset, littleEndian);
28338 this.exif[tag] = this.getExifValue(
28342 dataView.getUint16(offset + 2, littleEndian), // tag type
28343 dataView.getUint32(offset + 4, littleEndian), // tag length
28348 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28350 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28359 Roo.log('Invalid Exif data: Invalid tag type.');
28363 tagSize = tagType.size * length;
28364 // Determine if the value is contained in the dataOffset bytes,
28365 // or if the value at the dataOffset is a pointer to the actual data:
28366 dataOffset = tagSize > 4 ?
28367 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28368 if (dataOffset + tagSize > dataView.byteLength) {
28369 Roo.log('Invalid Exif data: Invalid data offset.');
28372 if (length === 1) {
28373 return tagType.getValue(dataView, dataOffset, littleEndian);
28376 for (i = 0; i < length; i += 1) {
28377 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28380 if (tagType.ascii) {
28382 // Concatenate the chars:
28383 for (i = 0; i < values.length; i += 1) {
28385 // Ignore the terminating NULL byte(s):
28386 if (c === '\u0000') {
28398 Roo.apply(Roo.bootstrap.UploadCropbox, {
28400 'Orientation': 0x0112
28404 1: 0, //'top-left',
28406 3: 180, //'bottom-right',
28407 // 4: 'bottom-left',
28409 6: 90, //'right-top',
28410 // 7: 'right-bottom',
28411 8: 270 //'left-bottom'
28415 // byte, 8-bit unsigned int:
28417 getValue: function (dataView, dataOffset) {
28418 return dataView.getUint8(dataOffset);
28422 // ascii, 8-bit byte:
28424 getValue: function (dataView, dataOffset) {
28425 return String.fromCharCode(dataView.getUint8(dataOffset));
28430 // short, 16 bit int:
28432 getValue: function (dataView, dataOffset, littleEndian) {
28433 return dataView.getUint16(dataOffset, littleEndian);
28437 // long, 32 bit int:
28439 getValue: function (dataView, dataOffset, littleEndian) {
28440 return dataView.getUint32(dataOffset, littleEndian);
28444 // rational = two long values, first is numerator, second is denominator:
28446 getValue: function (dataView, dataOffset, littleEndian) {
28447 return dataView.getUint32(dataOffset, littleEndian) /
28448 dataView.getUint32(dataOffset + 4, littleEndian);
28452 // slong, 32 bit signed int:
28454 getValue: function (dataView, dataOffset, littleEndian) {
28455 return dataView.getInt32(dataOffset, littleEndian);
28459 // srational, two slongs, first is numerator, second is denominator:
28461 getValue: function (dataView, dataOffset, littleEndian) {
28462 return dataView.getInt32(dataOffset, littleEndian) /
28463 dataView.getInt32(dataOffset + 4, littleEndian);
28473 cls : 'btn-group roo-upload-cropbox-rotate-left',
28474 action : 'rotate-left',
28478 cls : 'btn btn-default',
28479 html : '<i class="fa fa-undo"></i>'
28485 cls : 'btn-group roo-upload-cropbox-picture',
28486 action : 'picture',
28490 cls : 'btn btn-default',
28491 html : '<i class="fa fa-picture-o"></i>'
28497 cls : 'btn-group roo-upload-cropbox-rotate-right',
28498 action : 'rotate-right',
28502 cls : 'btn btn-default',
28503 html : '<i class="fa fa-repeat"></i>'
28511 cls : 'btn-group roo-upload-cropbox-rotate-left',
28512 action : 'rotate-left',
28516 cls : 'btn btn-default',
28517 html : '<i class="fa fa-undo"></i>'
28523 cls : 'btn-group roo-upload-cropbox-download',
28524 action : 'download',
28528 cls : 'btn btn-default',
28529 html : '<i class="fa fa-download"></i>'
28535 cls : 'btn-group roo-upload-cropbox-crop',
28540 cls : 'btn btn-default',
28541 html : '<i class="fa fa-crop"></i>'
28547 cls : 'btn-group roo-upload-cropbox-trash',
28552 cls : 'btn btn-default',
28553 html : '<i class="fa fa-trash"></i>'
28559 cls : 'btn-group roo-upload-cropbox-rotate-right',
28560 action : 'rotate-right',
28564 cls : 'btn btn-default',
28565 html : '<i class="fa fa-repeat"></i>'
28573 cls : 'btn-group roo-upload-cropbox-rotate-left',
28574 action : 'rotate-left',
28578 cls : 'btn btn-default',
28579 html : '<i class="fa fa-undo"></i>'
28585 cls : 'btn-group roo-upload-cropbox-rotate-right',
28586 action : 'rotate-right',
28590 cls : 'btn btn-default',
28591 html : '<i class="fa fa-repeat"></i>'
28604 * @class Roo.bootstrap.DocumentManager
28605 * @extends Roo.bootstrap.Component
28606 * Bootstrap DocumentManager class
28607 * @cfg {String} paramName default 'imageUpload'
28608 * @cfg {String} toolTipName default 'filename'
28609 * @cfg {String} method default POST
28610 * @cfg {String} url action url
28611 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28612 * @cfg {Boolean} multiple multiple upload default true
28613 * @cfg {Number} thumbSize default 300
28614 * @cfg {String} fieldLabel
28615 * @cfg {Number} labelWidth default 4
28616 * @cfg {String} labelAlign (left|top) default left
28617 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28618 * @cfg {Number} labellg set the width of label (1-12)
28619 * @cfg {Number} labelmd set the width of label (1-12)
28620 * @cfg {Number} labelsm set the width of label (1-12)
28621 * @cfg {Number} labelxs set the width of label (1-12)
28624 * Create a new DocumentManager
28625 * @param {Object} config The config object
28628 Roo.bootstrap.DocumentManager = function(config){
28629 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28632 this.delegates = [];
28637 * Fire when initial the DocumentManager
28638 * @param {Roo.bootstrap.DocumentManager} this
28643 * inspect selected file
28644 * @param {Roo.bootstrap.DocumentManager} this
28645 * @param {File} file
28650 * Fire when xhr load exception
28651 * @param {Roo.bootstrap.DocumentManager} this
28652 * @param {XMLHttpRequest} xhr
28654 "exception" : true,
28656 * @event afterupload
28657 * Fire when xhr load exception
28658 * @param {Roo.bootstrap.DocumentManager} this
28659 * @param {XMLHttpRequest} xhr
28661 "afterupload" : true,
28664 * prepare the form data
28665 * @param {Roo.bootstrap.DocumentManager} this
28666 * @param {Object} formData
28671 * Fire when remove the file
28672 * @param {Roo.bootstrap.DocumentManager} this
28673 * @param {Object} file
28678 * Fire after refresh the file
28679 * @param {Roo.bootstrap.DocumentManager} this
28684 * Fire after click the image
28685 * @param {Roo.bootstrap.DocumentManager} this
28686 * @param {Object} file
28691 * Fire when upload a image and editable set to true
28692 * @param {Roo.bootstrap.DocumentManager} this
28693 * @param {Object} file
28697 * @event beforeselectfile
28698 * Fire before select file
28699 * @param {Roo.bootstrap.DocumentManager} this
28701 "beforeselectfile" : true,
28704 * Fire before process file
28705 * @param {Roo.bootstrap.DocumentManager} this
28706 * @param {Object} file
28710 * @event previewrendered
28711 * Fire when preview rendered
28712 * @param {Roo.bootstrap.DocumentManager} this
28713 * @param {Object} file
28715 "previewrendered" : true,
28718 "previewResize" : true
28723 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28732 paramName : 'imageUpload',
28733 toolTipName : 'filename',
28736 labelAlign : 'left',
28746 getAutoCreate : function()
28748 var managerWidget = {
28750 cls : 'roo-document-manager',
28754 cls : 'roo-document-manager-selector',
28759 cls : 'roo-document-manager-uploader',
28763 cls : 'roo-document-manager-upload-btn',
28764 html : '<i class="fa fa-plus"></i>'
28775 cls : 'column col-md-12',
28780 if(this.fieldLabel.length){
28785 cls : 'column col-md-12',
28786 html : this.fieldLabel
28790 cls : 'column col-md-12',
28795 if(this.labelAlign == 'left'){
28800 html : this.fieldLabel
28809 if(this.labelWidth > 12){
28810 content[0].style = "width: " + this.labelWidth + 'px';
28813 if(this.labelWidth < 13 && this.labelmd == 0){
28814 this.labelmd = this.labelWidth;
28817 if(this.labellg > 0){
28818 content[0].cls += ' col-lg-' + this.labellg;
28819 content[1].cls += ' col-lg-' + (12 - this.labellg);
28822 if(this.labelmd > 0){
28823 content[0].cls += ' col-md-' + this.labelmd;
28824 content[1].cls += ' col-md-' + (12 - this.labelmd);
28827 if(this.labelsm > 0){
28828 content[0].cls += ' col-sm-' + this.labelsm;
28829 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28832 if(this.labelxs > 0){
28833 content[0].cls += ' col-xs-' + this.labelxs;
28834 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28842 cls : 'row clearfix',
28850 initEvents : function()
28852 this.managerEl = this.el.select('.roo-document-manager', true).first();
28853 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28855 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28856 this.selectorEl.hide();
28859 this.selectorEl.attr('multiple', 'multiple');
28862 this.selectorEl.on('change', this.onFileSelected, this);
28864 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28865 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28867 this.uploader.on('click', this.onUploaderClick, this);
28869 this.renderProgressDialog();
28873 window.addEventListener("resize", function() { _this.refresh(); } );
28875 this.fireEvent('initial', this);
28878 renderProgressDialog : function()
28882 this.progressDialog = new Roo.bootstrap.Modal({
28883 cls : 'roo-document-manager-progress-dialog',
28884 allow_close : false,
28894 btnclick : function() {
28895 _this.uploadCancel();
28901 this.progressDialog.render(Roo.get(document.body));
28903 this.progress = new Roo.bootstrap.Progress({
28904 cls : 'roo-document-manager-progress',
28909 this.progress.render(this.progressDialog.getChildContainer());
28911 this.progressBar = new Roo.bootstrap.ProgressBar({
28912 cls : 'roo-document-manager-progress-bar',
28915 aria_valuemax : 12,
28919 this.progressBar.render(this.progress.getChildContainer());
28922 onUploaderClick : function(e)
28924 e.preventDefault();
28926 if(this.fireEvent('beforeselectfile', this) != false){
28927 this.selectorEl.dom.click();
28932 onFileSelected : function(e)
28934 e.preventDefault();
28936 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28940 Roo.each(this.selectorEl.dom.files, function(file){
28941 if(this.fireEvent('inspect', this, file) != false){
28942 this.files.push(file);
28952 this.selectorEl.dom.value = '';
28954 if(!this.files || !this.files.length){
28958 if(this.boxes > 0 && this.files.length > this.boxes){
28959 this.files = this.files.slice(0, this.boxes);
28962 this.uploader.show();
28964 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28965 this.uploader.hide();
28974 Roo.each(this.files, function(file){
28976 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28977 var f = this.renderPreview(file);
28982 if(file.type.indexOf('image') != -1){
28983 this.delegates.push(
28985 _this.process(file);
28986 }).createDelegate(this)
28994 _this.process(file);
28995 }).createDelegate(this)
29000 this.files = files;
29002 this.delegates = this.delegates.concat(docs);
29004 if(!this.delegates.length){
29009 this.progressBar.aria_valuemax = this.delegates.length;
29016 arrange : function()
29018 if(!this.delegates.length){
29019 this.progressDialog.hide();
29024 var delegate = this.delegates.shift();
29026 this.progressDialog.show();
29028 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29030 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29035 refresh : function()
29037 this.uploader.show();
29039 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29040 this.uploader.hide();
29043 Roo.isTouch ? this.closable(false) : this.closable(true);
29045 this.fireEvent('refresh', this);
29048 onRemove : function(e, el, o)
29050 e.preventDefault();
29052 this.fireEvent('remove', this, o);
29056 remove : function(o)
29060 Roo.each(this.files, function(file){
29061 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29070 this.files = files;
29077 Roo.each(this.files, function(file){
29082 file.target.remove();
29091 onClick : function(e, el, o)
29093 e.preventDefault();
29095 this.fireEvent('click', this, o);
29099 closable : function(closable)
29101 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29103 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29115 xhrOnLoad : function(xhr)
29117 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29121 if (xhr.readyState !== 4) {
29123 this.fireEvent('exception', this, xhr);
29127 var response = Roo.decode(xhr.responseText);
29129 if(!response.success){
29131 this.fireEvent('exception', this, xhr);
29135 var file = this.renderPreview(response.data);
29137 this.files.push(file);
29141 this.fireEvent('afterupload', this, xhr);
29145 xhrOnError : function(xhr)
29147 Roo.log('xhr on error');
29149 var response = Roo.decode(xhr.responseText);
29156 process : function(file)
29158 if(this.fireEvent('process', this, file) !== false){
29159 if(this.editable && file.type.indexOf('image') != -1){
29160 this.fireEvent('edit', this, file);
29164 this.uploadStart(file, false);
29171 uploadStart : function(file, crop)
29173 this.xhr = new XMLHttpRequest();
29175 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29180 file.xhr = this.xhr;
29182 this.managerEl.createChild({
29184 cls : 'roo-document-manager-loading',
29188 tooltip : file.name,
29189 cls : 'roo-document-manager-thumb',
29190 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29196 this.xhr.open(this.method, this.url, true);
29199 "Accept": "application/json",
29200 "Cache-Control": "no-cache",
29201 "X-Requested-With": "XMLHttpRequest"
29204 for (var headerName in headers) {
29205 var headerValue = headers[headerName];
29207 this.xhr.setRequestHeader(headerName, headerValue);
29213 this.xhr.onload = function()
29215 _this.xhrOnLoad(_this.xhr);
29218 this.xhr.onerror = function()
29220 _this.xhrOnError(_this.xhr);
29223 var formData = new FormData();
29225 formData.append('returnHTML', 'NO');
29228 formData.append('crop', crop);
29231 formData.append(this.paramName, file, file.name);
29238 if(this.fireEvent('prepare', this, formData, options) != false){
29240 if(options.manually){
29244 this.xhr.send(formData);
29248 this.uploadCancel();
29251 uploadCancel : function()
29257 this.delegates = [];
29259 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29266 renderPreview : function(file)
29268 if(typeof(file.target) != 'undefined' && file.target){
29272 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29274 var previewEl = this.managerEl.createChild({
29276 cls : 'roo-document-manager-preview',
29280 tooltip : file[this.toolTipName],
29281 cls : 'roo-document-manager-thumb',
29282 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29287 html : '<i class="fa fa-times-circle"></i>'
29292 var close = previewEl.select('button.close', true).first();
29294 close.on('click', this.onRemove, this, file);
29296 file.target = previewEl;
29298 var image = previewEl.select('img', true).first();
29302 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29304 image.on('click', this.onClick, this, file);
29306 this.fireEvent('previewrendered', this, file);
29312 onPreviewLoad : function(file, image)
29314 if(typeof(file.target) == 'undefined' || !file.target){
29318 var width = image.dom.naturalWidth || image.dom.width;
29319 var height = image.dom.naturalHeight || image.dom.height;
29321 if(!this.previewResize) {
29325 if(width > height){
29326 file.target.addClass('wide');
29330 file.target.addClass('tall');
29335 uploadFromSource : function(file, crop)
29337 this.xhr = new XMLHttpRequest();
29339 this.managerEl.createChild({
29341 cls : 'roo-document-manager-loading',
29345 tooltip : file.name,
29346 cls : 'roo-document-manager-thumb',
29347 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29353 this.xhr.open(this.method, this.url, true);
29356 "Accept": "application/json",
29357 "Cache-Control": "no-cache",
29358 "X-Requested-With": "XMLHttpRequest"
29361 for (var headerName in headers) {
29362 var headerValue = headers[headerName];
29364 this.xhr.setRequestHeader(headerName, headerValue);
29370 this.xhr.onload = function()
29372 _this.xhrOnLoad(_this.xhr);
29375 this.xhr.onerror = function()
29377 _this.xhrOnError(_this.xhr);
29380 var formData = new FormData();
29382 formData.append('returnHTML', 'NO');
29384 formData.append('crop', crop);
29386 if(typeof(file.filename) != 'undefined'){
29387 formData.append('filename', file.filename);
29390 if(typeof(file.mimetype) != 'undefined'){
29391 formData.append('mimetype', file.mimetype);
29396 if(this.fireEvent('prepare', this, formData) != false){
29397 this.xhr.send(formData);
29407 * @class Roo.bootstrap.DocumentViewer
29408 * @extends Roo.bootstrap.Component
29409 * Bootstrap DocumentViewer class
29410 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29411 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29414 * Create a new DocumentViewer
29415 * @param {Object} config The config object
29418 Roo.bootstrap.DocumentViewer = function(config){
29419 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29424 * Fire after initEvent
29425 * @param {Roo.bootstrap.DocumentViewer} this
29431 * @param {Roo.bootstrap.DocumentViewer} this
29436 * Fire after download button
29437 * @param {Roo.bootstrap.DocumentViewer} this
29442 * Fire after trash button
29443 * @param {Roo.bootstrap.DocumentViewer} this
29450 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29452 showDownload : true,
29456 getAutoCreate : function()
29460 cls : 'roo-document-viewer',
29464 cls : 'roo-document-viewer-body',
29468 cls : 'roo-document-viewer-thumb',
29472 cls : 'roo-document-viewer-image'
29480 cls : 'roo-document-viewer-footer',
29483 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29487 cls : 'btn-group roo-document-viewer-download',
29491 cls : 'btn btn-default',
29492 html : '<i class="fa fa-download"></i>'
29498 cls : 'btn-group roo-document-viewer-trash',
29502 cls : 'btn btn-default',
29503 html : '<i class="fa fa-trash"></i>'
29516 initEvents : function()
29518 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29519 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29521 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29522 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29524 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29525 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29527 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29528 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29530 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29531 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29533 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29534 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29536 this.bodyEl.on('click', this.onClick, this);
29537 this.downloadBtn.on('click', this.onDownload, this);
29538 this.trashBtn.on('click', this.onTrash, this);
29540 this.downloadBtn.hide();
29541 this.trashBtn.hide();
29543 if(this.showDownload){
29544 this.downloadBtn.show();
29547 if(this.showTrash){
29548 this.trashBtn.show();
29551 if(!this.showDownload && !this.showTrash) {
29552 this.footerEl.hide();
29557 initial : function()
29559 this.fireEvent('initial', this);
29563 onClick : function(e)
29565 e.preventDefault();
29567 this.fireEvent('click', this);
29570 onDownload : function(e)
29572 e.preventDefault();
29574 this.fireEvent('download', this);
29577 onTrash : function(e)
29579 e.preventDefault();
29581 this.fireEvent('trash', this);
29593 * @class Roo.bootstrap.NavProgressBar
29594 * @extends Roo.bootstrap.Component
29595 * Bootstrap NavProgressBar class
29598 * Create a new nav progress bar
29599 * @param {Object} config The config object
29602 Roo.bootstrap.NavProgressBar = function(config){
29603 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29605 this.bullets = this.bullets || [];
29607 // Roo.bootstrap.NavProgressBar.register(this);
29611 * Fires when the active item changes
29612 * @param {Roo.bootstrap.NavProgressBar} this
29613 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29614 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29621 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29626 getAutoCreate : function()
29628 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29632 cls : 'roo-navigation-bar-group',
29636 cls : 'roo-navigation-top-bar'
29640 cls : 'roo-navigation-bullets-bar',
29644 cls : 'roo-navigation-bar'
29651 cls : 'roo-navigation-bottom-bar'
29661 initEvents: function()
29666 onRender : function(ct, position)
29668 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29670 if(this.bullets.length){
29671 Roo.each(this.bullets, function(b){
29680 addItem : function(cfg)
29682 var item = new Roo.bootstrap.NavProgressItem(cfg);
29684 item.parentId = this.id;
29685 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29688 var top = new Roo.bootstrap.Element({
29690 cls : 'roo-navigation-bar-text'
29693 var bottom = new Roo.bootstrap.Element({
29695 cls : 'roo-navigation-bar-text'
29698 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29699 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29701 var topText = new Roo.bootstrap.Element({
29703 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29706 var bottomText = new Roo.bootstrap.Element({
29708 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29711 topText.onRender(top.el, null);
29712 bottomText.onRender(bottom.el, null);
29715 item.bottomEl = bottom;
29718 this.barItems.push(item);
29723 getActive : function()
29725 var active = false;
29727 Roo.each(this.barItems, function(v){
29729 if (!v.isActive()) {
29741 setActiveItem : function(item)
29745 Roo.each(this.barItems, function(v){
29746 if (v.rid == item.rid) {
29750 if (v.isActive()) {
29751 v.setActive(false);
29756 item.setActive(true);
29758 this.fireEvent('changed', this, item, prev);
29761 getBarItem: function(rid)
29765 Roo.each(this.barItems, function(e) {
29766 if (e.rid != rid) {
29777 indexOfItem : function(item)
29781 Roo.each(this.barItems, function(v, i){
29783 if (v.rid != item.rid) {
29794 setActiveNext : function()
29796 var i = this.indexOfItem(this.getActive());
29798 if (i > this.barItems.length) {
29802 this.setActiveItem(this.barItems[i+1]);
29805 setActivePrev : function()
29807 var i = this.indexOfItem(this.getActive());
29813 this.setActiveItem(this.barItems[i-1]);
29816 format : function()
29818 if(!this.barItems.length){
29822 var width = 100 / this.barItems.length;
29824 Roo.each(this.barItems, function(i){
29825 i.el.setStyle('width', width + '%');
29826 i.topEl.el.setStyle('width', width + '%');
29827 i.bottomEl.el.setStyle('width', width + '%');
29836 * Nav Progress Item
29841 * @class Roo.bootstrap.NavProgressItem
29842 * @extends Roo.bootstrap.Component
29843 * Bootstrap NavProgressItem class
29844 * @cfg {String} rid the reference id
29845 * @cfg {Boolean} active (true|false) Is item active default false
29846 * @cfg {Boolean} disabled (true|false) Is item active default false
29847 * @cfg {String} html
29848 * @cfg {String} position (top|bottom) text position default bottom
29849 * @cfg {String} icon show icon instead of number
29852 * Create a new NavProgressItem
29853 * @param {Object} config The config object
29855 Roo.bootstrap.NavProgressItem = function(config){
29856 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29861 * The raw click event for the entire grid.
29862 * @param {Roo.bootstrap.NavProgressItem} this
29863 * @param {Roo.EventObject} e
29870 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29876 position : 'bottom',
29879 getAutoCreate : function()
29881 var iconCls = 'roo-navigation-bar-item-icon';
29883 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29887 cls: 'roo-navigation-bar-item',
29897 cfg.cls += ' active';
29900 cfg.cls += ' disabled';
29906 disable : function()
29908 this.setDisabled(true);
29911 enable : function()
29913 this.setDisabled(false);
29916 initEvents: function()
29918 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29920 this.iconEl.on('click', this.onClick, this);
29923 onClick : function(e)
29925 e.preventDefault();
29931 if(this.fireEvent('click', this, e) === false){
29935 this.parent().setActiveItem(this);
29938 isActive: function ()
29940 return this.active;
29943 setActive : function(state)
29945 if(this.active == state){
29949 this.active = state;
29952 this.el.addClass('active');
29956 this.el.removeClass('active');
29961 setDisabled : function(state)
29963 if(this.disabled == state){
29967 this.disabled = state;
29970 this.el.addClass('disabled');
29974 this.el.removeClass('disabled');
29977 tooltipEl : function()
29979 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29992 * @class Roo.bootstrap.FieldLabel
29993 * @extends Roo.bootstrap.Component
29994 * Bootstrap FieldLabel class
29995 * @cfg {String} html contents of the element
29996 * @cfg {String} tag tag of the element default label
29997 * @cfg {String} cls class of the element
29998 * @cfg {String} target label target
29999 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30000 * @cfg {String} invalidClass default "text-warning"
30001 * @cfg {String} validClass default "text-success"
30002 * @cfg {String} iconTooltip default "This field is required"
30003 * @cfg {String} indicatorpos (left|right) default left
30006 * Create a new FieldLabel
30007 * @param {Object} config The config object
30010 Roo.bootstrap.FieldLabel = function(config){
30011 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30016 * Fires after the field has been marked as invalid.
30017 * @param {Roo.form.FieldLabel} this
30018 * @param {String} msg The validation message
30023 * Fires after the field has been validated with no errors.
30024 * @param {Roo.form.FieldLabel} this
30030 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30037 invalidClass : 'has-warning',
30038 validClass : 'has-success',
30039 iconTooltip : 'This field is required',
30040 indicatorpos : 'left',
30042 getAutoCreate : function(){
30045 if (!this.allowBlank) {
30051 cls : 'roo-bootstrap-field-label ' + this.cls,
30056 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30057 tooltip : this.iconTooltip
30066 if(this.indicatorpos == 'right'){
30069 cls : 'roo-bootstrap-field-label ' + this.cls,
30078 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30079 tooltip : this.iconTooltip
30088 initEvents: function()
30090 Roo.bootstrap.Element.superclass.initEvents.call(this);
30092 this.indicator = this.indicatorEl();
30094 if(this.indicator){
30095 this.indicator.removeClass('visible');
30096 this.indicator.addClass('invisible');
30099 Roo.bootstrap.FieldLabel.register(this);
30102 indicatorEl : function()
30104 var indicator = this.el.select('i.roo-required-indicator',true).first();
30115 * Mark this field as valid
30117 markValid : function()
30119 if(this.indicator){
30120 this.indicator.removeClass('visible');
30121 this.indicator.addClass('invisible');
30124 this.el.removeClass(this.invalidClass);
30126 this.el.addClass(this.validClass);
30128 this.fireEvent('valid', this);
30132 * Mark this field as invalid
30133 * @param {String} msg The validation message
30135 markInvalid : function(msg)
30137 if(this.indicator){
30138 this.indicator.removeClass('invisible');
30139 this.indicator.addClass('visible');
30142 this.el.removeClass(this.validClass);
30144 this.el.addClass(this.invalidClass);
30146 this.fireEvent('invalid', this, msg);
30152 Roo.apply(Roo.bootstrap.FieldLabel, {
30157 * register a FieldLabel Group
30158 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30160 register : function(label)
30162 if(this.groups.hasOwnProperty(label.target)){
30166 this.groups[label.target] = label;
30170 * fetch a FieldLabel Group based on the target
30171 * @param {string} target
30172 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30174 get: function(target) {
30175 if (typeof(this.groups[target]) == 'undefined') {
30179 return this.groups[target] ;
30188 * page DateSplitField.
30194 * @class Roo.bootstrap.DateSplitField
30195 * @extends Roo.bootstrap.Component
30196 * Bootstrap DateSplitField class
30197 * @cfg {string} fieldLabel - the label associated
30198 * @cfg {Number} labelWidth set the width of label (0-12)
30199 * @cfg {String} labelAlign (top|left)
30200 * @cfg {Boolean} dayAllowBlank (true|false) default false
30201 * @cfg {Boolean} monthAllowBlank (true|false) default false
30202 * @cfg {Boolean} yearAllowBlank (true|false) default false
30203 * @cfg {string} dayPlaceholder
30204 * @cfg {string} monthPlaceholder
30205 * @cfg {string} yearPlaceholder
30206 * @cfg {string} dayFormat default 'd'
30207 * @cfg {string} monthFormat default 'm'
30208 * @cfg {string} yearFormat default 'Y'
30209 * @cfg {Number} labellg set the width of label (1-12)
30210 * @cfg {Number} labelmd set the width of label (1-12)
30211 * @cfg {Number} labelsm set the width of label (1-12)
30212 * @cfg {Number} labelxs set the width of label (1-12)
30216 * Create a new DateSplitField
30217 * @param {Object} config The config object
30220 Roo.bootstrap.DateSplitField = function(config){
30221 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30227 * getting the data of years
30228 * @param {Roo.bootstrap.DateSplitField} this
30229 * @param {Object} years
30234 * getting the data of days
30235 * @param {Roo.bootstrap.DateSplitField} this
30236 * @param {Object} days
30241 * Fires after the field has been marked as invalid.
30242 * @param {Roo.form.Field} this
30243 * @param {String} msg The validation message
30248 * Fires after the field has been validated with no errors.
30249 * @param {Roo.form.Field} this
30255 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30258 labelAlign : 'top',
30260 dayAllowBlank : false,
30261 monthAllowBlank : false,
30262 yearAllowBlank : false,
30263 dayPlaceholder : '',
30264 monthPlaceholder : '',
30265 yearPlaceholder : '',
30269 isFormField : true,
30275 getAutoCreate : function()
30279 cls : 'row roo-date-split-field-group',
30284 cls : 'form-hidden-field roo-date-split-field-group-value',
30290 var labelCls = 'col-md-12';
30291 var contentCls = 'col-md-4';
30293 if(this.fieldLabel){
30297 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30301 html : this.fieldLabel
30306 if(this.labelAlign == 'left'){
30308 if(this.labelWidth > 12){
30309 label.style = "width: " + this.labelWidth + 'px';
30312 if(this.labelWidth < 13 && this.labelmd == 0){
30313 this.labelmd = this.labelWidth;
30316 if(this.labellg > 0){
30317 labelCls = ' col-lg-' + this.labellg;
30318 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30321 if(this.labelmd > 0){
30322 labelCls = ' col-md-' + this.labelmd;
30323 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30326 if(this.labelsm > 0){
30327 labelCls = ' col-sm-' + this.labelsm;
30328 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30331 if(this.labelxs > 0){
30332 labelCls = ' col-xs-' + this.labelxs;
30333 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30337 label.cls += ' ' + labelCls;
30339 cfg.cn.push(label);
30342 Roo.each(['day', 'month', 'year'], function(t){
30345 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30352 inputEl: function ()
30354 return this.el.select('.roo-date-split-field-group-value', true).first();
30357 onRender : function(ct, position)
30361 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30363 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30365 this.dayField = new Roo.bootstrap.ComboBox({
30366 allowBlank : this.dayAllowBlank,
30367 alwaysQuery : true,
30368 displayField : 'value',
30371 forceSelection : true,
30373 placeholder : this.dayPlaceholder,
30374 selectOnFocus : true,
30375 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30376 triggerAction : 'all',
30378 valueField : 'value',
30379 store : new Roo.data.SimpleStore({
30380 data : (function() {
30382 _this.fireEvent('days', _this, days);
30385 fields : [ 'value' ]
30388 select : function (_self, record, index)
30390 _this.setValue(_this.getValue());
30395 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30397 this.monthField = new Roo.bootstrap.MonthField({
30398 after : '<i class=\"fa fa-calendar\"></i>',
30399 allowBlank : this.monthAllowBlank,
30400 placeholder : this.monthPlaceholder,
30403 render : function (_self)
30405 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30406 e.preventDefault();
30410 select : function (_self, oldvalue, newvalue)
30412 _this.setValue(_this.getValue());
30417 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30419 this.yearField = new Roo.bootstrap.ComboBox({
30420 allowBlank : this.yearAllowBlank,
30421 alwaysQuery : true,
30422 displayField : 'value',
30425 forceSelection : true,
30427 placeholder : this.yearPlaceholder,
30428 selectOnFocus : true,
30429 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30430 triggerAction : 'all',
30432 valueField : 'value',
30433 store : new Roo.data.SimpleStore({
30434 data : (function() {
30436 _this.fireEvent('years', _this, years);
30439 fields : [ 'value' ]
30442 select : function (_self, record, index)
30444 _this.setValue(_this.getValue());
30449 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30452 setValue : function(v, format)
30454 this.inputEl.dom.value = v;
30456 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30458 var d = Date.parseDate(v, f);
30465 this.setDay(d.format(this.dayFormat));
30466 this.setMonth(d.format(this.monthFormat));
30467 this.setYear(d.format(this.yearFormat));
30474 setDay : function(v)
30476 this.dayField.setValue(v);
30477 this.inputEl.dom.value = this.getValue();
30482 setMonth : function(v)
30484 this.monthField.setValue(v, true);
30485 this.inputEl.dom.value = this.getValue();
30490 setYear : function(v)
30492 this.yearField.setValue(v);
30493 this.inputEl.dom.value = this.getValue();
30498 getDay : function()
30500 return this.dayField.getValue();
30503 getMonth : function()
30505 return this.monthField.getValue();
30508 getYear : function()
30510 return this.yearField.getValue();
30513 getValue : function()
30515 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30517 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30527 this.inputEl.dom.value = '';
30532 validate : function()
30534 var d = this.dayField.validate();
30535 var m = this.monthField.validate();
30536 var y = this.yearField.validate();
30541 (!this.dayAllowBlank && !d) ||
30542 (!this.monthAllowBlank && !m) ||
30543 (!this.yearAllowBlank && !y)
30548 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30557 this.markInvalid();
30562 markValid : function()
30565 var label = this.el.select('label', true).first();
30566 var icon = this.el.select('i.fa-star', true).first();
30572 this.fireEvent('valid', this);
30576 * Mark this field as invalid
30577 * @param {String} msg The validation message
30579 markInvalid : function(msg)
30582 var label = this.el.select('label', true).first();
30583 var icon = this.el.select('i.fa-star', true).first();
30585 if(label && !icon){
30586 this.el.select('.roo-date-split-field-label', true).createChild({
30588 cls : 'text-danger fa fa-lg fa-star',
30589 tooltip : 'This field is required',
30590 style : 'margin-right:5px;'
30594 this.fireEvent('invalid', this, msg);
30597 clearInvalid : function()
30599 var label = this.el.select('label', true).first();
30600 var icon = this.el.select('i.fa-star', true).first();
30606 this.fireEvent('valid', this);
30609 getName: function()
30619 * http://masonry.desandro.com
30621 * The idea is to render all the bricks based on vertical width...
30623 * The original code extends 'outlayer' - we might need to use that....
30629 * @class Roo.bootstrap.LayoutMasonry
30630 * @extends Roo.bootstrap.Component
30631 * Bootstrap Layout Masonry class
30634 * Create a new Element
30635 * @param {Object} config The config object
30638 Roo.bootstrap.LayoutMasonry = function(config){
30640 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30644 Roo.bootstrap.LayoutMasonry.register(this);
30650 * Fire after layout the items
30651 * @param {Roo.bootstrap.LayoutMasonry} this
30652 * @param {Roo.EventObject} e
30659 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30662 * @cfg {Boolean} isLayoutInstant = no animation?
30664 isLayoutInstant : false, // needed?
30667 * @cfg {Number} boxWidth width of the columns
30672 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30677 * @cfg {Number} padWidth padding below box..
30682 * @cfg {Number} gutter gutter width..
30687 * @cfg {Number} maxCols maximum number of columns
30693 * @cfg {Boolean} isAutoInitial defalut true
30695 isAutoInitial : true,
30700 * @cfg {Boolean} isHorizontal defalut false
30702 isHorizontal : false,
30704 currentSize : null,
30710 bricks: null, //CompositeElement
30714 _isLayoutInited : false,
30716 // isAlternative : false, // only use for vertical layout...
30719 * @cfg {Number} alternativePadWidth padding below box..
30721 alternativePadWidth : 50,
30723 selectedBrick : [],
30725 getAutoCreate : function(){
30727 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30731 cls: 'blog-masonary-wrapper ' + this.cls,
30733 cls : 'mas-boxes masonary'
30740 getChildContainer: function( )
30742 if (this.boxesEl) {
30743 return this.boxesEl;
30746 this.boxesEl = this.el.select('.mas-boxes').first();
30748 return this.boxesEl;
30752 initEvents : function()
30756 if(this.isAutoInitial){
30757 Roo.log('hook children rendered');
30758 this.on('childrenrendered', function() {
30759 Roo.log('children rendered');
30765 initial : function()
30767 this.selectedBrick = [];
30769 this.currentSize = this.el.getBox(true);
30771 Roo.EventManager.onWindowResize(this.resize, this);
30773 if(!this.isAutoInitial){
30781 //this.layout.defer(500,this);
30785 resize : function()
30787 var cs = this.el.getBox(true);
30790 this.currentSize.width == cs.width &&
30791 this.currentSize.x == cs.x &&
30792 this.currentSize.height == cs.height &&
30793 this.currentSize.y == cs.y
30795 Roo.log("no change in with or X or Y");
30799 this.currentSize = cs;
30805 layout : function()
30807 this._resetLayout();
30809 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30811 this.layoutItems( isInstant );
30813 this._isLayoutInited = true;
30815 this.fireEvent('layout', this);
30819 _resetLayout : function()
30821 if(this.isHorizontal){
30822 this.horizontalMeasureColumns();
30826 this.verticalMeasureColumns();
30830 verticalMeasureColumns : function()
30832 this.getContainerWidth();
30834 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30835 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30839 var boxWidth = this.boxWidth + this.padWidth;
30841 if(this.containerWidth < this.boxWidth){
30842 boxWidth = this.containerWidth
30845 var containerWidth = this.containerWidth;
30847 var cols = Math.floor(containerWidth / boxWidth);
30849 this.cols = Math.max( cols, 1 );
30851 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30853 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30855 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30857 this.colWidth = boxWidth + avail - this.padWidth;
30859 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30860 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30863 horizontalMeasureColumns : function()
30865 this.getContainerWidth();
30867 var boxWidth = this.boxWidth;
30869 if(this.containerWidth < boxWidth){
30870 boxWidth = this.containerWidth;
30873 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30875 this.el.setHeight(boxWidth);
30879 getContainerWidth : function()
30881 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30884 layoutItems : function( isInstant )
30886 Roo.log(this.bricks);
30888 var items = Roo.apply([], this.bricks);
30890 if(this.isHorizontal){
30891 this._horizontalLayoutItems( items , isInstant );
30895 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30896 // this._verticalAlternativeLayoutItems( items , isInstant );
30900 this._verticalLayoutItems( items , isInstant );
30904 _verticalLayoutItems : function ( items , isInstant)
30906 if ( !items || !items.length ) {
30911 ['xs', 'xs', 'xs', 'tall'],
30912 ['xs', 'xs', 'tall'],
30913 ['xs', 'xs', 'sm'],
30914 ['xs', 'xs', 'xs'],
30920 ['sm', 'xs', 'xs'],
30924 ['tall', 'xs', 'xs', 'xs'],
30925 ['tall', 'xs', 'xs'],
30937 Roo.each(items, function(item, k){
30939 switch (item.size) {
30940 // these layouts take up a full box,
30951 boxes.push([item]);
30974 var filterPattern = function(box, length)
30982 var pattern = box.slice(0, length);
30986 Roo.each(pattern, function(i){
30987 format.push(i.size);
30990 Roo.each(standard, function(s){
30992 if(String(s) != String(format)){
31001 if(!match && length == 1){
31006 filterPattern(box, length - 1);
31010 queue.push(pattern);
31012 box = box.slice(length, box.length);
31014 filterPattern(box, 4);
31020 Roo.each(boxes, function(box, k){
31026 if(box.length == 1){
31031 filterPattern(box, 4);
31035 this._processVerticalLayoutQueue( queue, isInstant );
31039 // _verticalAlternativeLayoutItems : function( items , isInstant )
31041 // if ( !items || !items.length ) {
31045 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31049 _horizontalLayoutItems : function ( items , isInstant)
31051 if ( !items || !items.length || items.length < 3) {
31057 var eItems = items.slice(0, 3);
31059 items = items.slice(3, items.length);
31062 ['xs', 'xs', 'xs', 'wide'],
31063 ['xs', 'xs', 'wide'],
31064 ['xs', 'xs', 'sm'],
31065 ['xs', 'xs', 'xs'],
31071 ['sm', 'xs', 'xs'],
31075 ['wide', 'xs', 'xs', 'xs'],
31076 ['wide', 'xs', 'xs'],
31089 Roo.each(items, function(item, k){
31091 switch (item.size) {
31102 boxes.push([item]);
31126 var filterPattern = function(box, length)
31134 var pattern = box.slice(0, length);
31138 Roo.each(pattern, function(i){
31139 format.push(i.size);
31142 Roo.each(standard, function(s){
31144 if(String(s) != String(format)){
31153 if(!match && length == 1){
31158 filterPattern(box, length - 1);
31162 queue.push(pattern);
31164 box = box.slice(length, box.length);
31166 filterPattern(box, 4);
31172 Roo.each(boxes, function(box, k){
31178 if(box.length == 1){
31183 filterPattern(box, 4);
31190 var pos = this.el.getBox(true);
31194 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31196 var hit_end = false;
31198 Roo.each(queue, function(box){
31202 Roo.each(box, function(b){
31204 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31214 Roo.each(box, function(b){
31216 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31219 mx = Math.max(mx, b.x);
31223 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31227 Roo.each(box, function(b){
31229 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31243 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31246 /** Sets position of item in DOM
31247 * @param {Element} item
31248 * @param {Number} x - horizontal position
31249 * @param {Number} y - vertical position
31250 * @param {Boolean} isInstant - disables transitions
31252 _processVerticalLayoutQueue : function( queue, isInstant )
31254 var pos = this.el.getBox(true);
31259 for (var i = 0; i < this.cols; i++){
31263 Roo.each(queue, function(box, k){
31265 var col = k % this.cols;
31267 Roo.each(box, function(b,kk){
31269 b.el.position('absolute');
31271 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31272 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31274 if(b.size == 'md-left' || b.size == 'md-right'){
31275 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31276 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31279 b.el.setWidth(width);
31280 b.el.setHeight(height);
31282 b.el.select('iframe',true).setSize(width,height);
31286 for (var i = 0; i < this.cols; i++){
31288 if(maxY[i] < maxY[col]){
31293 col = Math.min(col, i);
31297 x = pos.x + col * (this.colWidth + this.padWidth);
31301 var positions = [];
31303 switch (box.length){
31305 positions = this.getVerticalOneBoxColPositions(x, y, box);
31308 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31311 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31314 positions = this.getVerticalFourBoxColPositions(x, y, box);
31320 Roo.each(box, function(b,kk){
31322 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31324 var sz = b.el.getSize();
31326 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31334 for (var i = 0; i < this.cols; i++){
31335 mY = Math.max(mY, maxY[i]);
31338 this.el.setHeight(mY - pos.y);
31342 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31344 // var pos = this.el.getBox(true);
31347 // var maxX = pos.right;
31349 // var maxHeight = 0;
31351 // Roo.each(items, function(item, k){
31355 // item.el.position('absolute');
31357 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31359 // item.el.setWidth(width);
31361 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31363 // item.el.setHeight(height);
31366 // item.el.setXY([x, y], isInstant ? false : true);
31368 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31371 // y = y + height + this.alternativePadWidth;
31373 // maxHeight = maxHeight + height + this.alternativePadWidth;
31377 // this.el.setHeight(maxHeight);
31381 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31383 var pos = this.el.getBox(true);
31388 var maxX = pos.right;
31390 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31392 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31394 Roo.each(queue, function(box, k){
31396 Roo.each(box, function(b, kk){
31398 b.el.position('absolute');
31400 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31401 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31403 if(b.size == 'md-left' || b.size == 'md-right'){
31404 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31405 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31408 b.el.setWidth(width);
31409 b.el.setHeight(height);
31417 var positions = [];
31419 switch (box.length){
31421 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31424 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31427 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31430 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31436 Roo.each(box, function(b,kk){
31438 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31440 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31448 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31450 Roo.each(eItems, function(b,k){
31452 b.size = (k == 0) ? 'sm' : 'xs';
31453 b.x = (k == 0) ? 2 : 1;
31454 b.y = (k == 0) ? 2 : 1;
31456 b.el.position('absolute');
31458 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31460 b.el.setWidth(width);
31462 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31464 b.el.setHeight(height);
31468 var positions = [];
31471 x : maxX - this.unitWidth * 2 - this.gutter,
31476 x : maxX - this.unitWidth,
31477 y : minY + (this.unitWidth + this.gutter) * 2
31481 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31485 Roo.each(eItems, function(b,k){
31487 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31493 getVerticalOneBoxColPositions : function(x, y, box)
31497 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31499 if(box[0].size == 'md-left'){
31503 if(box[0].size == 'md-right'){
31508 x : x + (this.unitWidth + this.gutter) * rand,
31515 getVerticalTwoBoxColPositions : function(x, y, box)
31519 if(box[0].size == 'xs'){
31523 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31527 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31541 x : x + (this.unitWidth + this.gutter) * 2,
31542 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31549 getVerticalThreeBoxColPositions : function(x, y, box)
31553 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31561 x : x + (this.unitWidth + this.gutter) * 1,
31566 x : x + (this.unitWidth + this.gutter) * 2,
31574 if(box[0].size == 'xs' && box[1].size == 'xs'){
31583 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31587 x : x + (this.unitWidth + this.gutter) * 1,
31601 x : x + (this.unitWidth + this.gutter) * 2,
31606 x : x + (this.unitWidth + this.gutter) * 2,
31607 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31614 getVerticalFourBoxColPositions : function(x, y, box)
31618 if(box[0].size == 'xs'){
31627 y : y + (this.unitHeight + this.gutter) * 1
31632 y : y + (this.unitHeight + this.gutter) * 2
31636 x : x + (this.unitWidth + this.gutter) * 1,
31650 x : x + (this.unitWidth + this.gutter) * 2,
31655 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31656 y : y + (this.unitHeight + this.gutter) * 1
31660 x : x + (this.unitWidth + this.gutter) * 2,
31661 y : y + (this.unitWidth + this.gutter) * 2
31668 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31672 if(box[0].size == 'md-left'){
31674 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31681 if(box[0].size == 'md-right'){
31683 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31684 y : minY + (this.unitWidth + this.gutter) * 1
31690 var rand = Math.floor(Math.random() * (4 - box[0].y));
31693 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31694 y : minY + (this.unitWidth + this.gutter) * rand
31701 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31705 if(box[0].size == 'xs'){
31708 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31713 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31714 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31722 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31727 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31728 y : minY + (this.unitWidth + this.gutter) * 2
31735 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31739 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31742 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31747 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31748 y : minY + (this.unitWidth + this.gutter) * 1
31752 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31753 y : minY + (this.unitWidth + this.gutter) * 2
31760 if(box[0].size == 'xs' && box[1].size == 'xs'){
31763 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31768 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31773 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31774 y : minY + (this.unitWidth + this.gutter) * 1
31782 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31787 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31788 y : minY + (this.unitWidth + this.gutter) * 2
31792 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31793 y : minY + (this.unitWidth + this.gutter) * 2
31800 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31804 if(box[0].size == 'xs'){
31807 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31812 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31817 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),
31822 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31823 y : minY + (this.unitWidth + this.gutter) * 1
31831 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31836 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31837 y : minY + (this.unitWidth + this.gutter) * 2
31841 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31842 y : minY + (this.unitWidth + this.gutter) * 2
31846 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),
31847 y : minY + (this.unitWidth + this.gutter) * 2
31855 * remove a Masonry Brick
31856 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31858 removeBrick : function(brick_id)
31864 for (var i = 0; i<this.bricks.length; i++) {
31865 if (this.bricks[i].id == brick_id) {
31866 this.bricks.splice(i,1);
31867 this.el.dom.removeChild(Roo.get(brick_id).dom);
31874 * adds a Masonry Brick
31875 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31877 addBrick : function(cfg)
31879 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31880 //this.register(cn);
31881 cn.parentId = this.id;
31882 cn.onRender(this.el, null);
31887 * register a Masonry Brick
31888 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31891 register : function(brick)
31893 this.bricks.push(brick);
31894 brick.masonryId = this.id;
31898 * clear all the Masonry Brick
31900 clearAll : function()
31903 //this.getChildContainer().dom.innerHTML = "";
31904 this.el.dom.innerHTML = '';
31907 getSelected : function()
31909 if (!this.selectedBrick) {
31913 return this.selectedBrick;
31917 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31921 * register a Masonry Layout
31922 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31925 register : function(layout)
31927 this.groups[layout.id] = layout;
31930 * fetch a Masonry Layout based on the masonry layout ID
31931 * @param {string} the masonry layout to add
31932 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31935 get: function(layout_id) {
31936 if (typeof(this.groups[layout_id]) == 'undefined') {
31939 return this.groups[layout_id] ;
31951 * http://masonry.desandro.com
31953 * The idea is to render all the bricks based on vertical width...
31955 * The original code extends 'outlayer' - we might need to use that....
31961 * @class Roo.bootstrap.LayoutMasonryAuto
31962 * @extends Roo.bootstrap.Component
31963 * Bootstrap Layout Masonry class
31966 * Create a new Element
31967 * @param {Object} config The config object
31970 Roo.bootstrap.LayoutMasonryAuto = function(config){
31971 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31974 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31977 * @cfg {Boolean} isFitWidth - resize the width..
31979 isFitWidth : false, // options..
31981 * @cfg {Boolean} isOriginLeft = left align?
31983 isOriginLeft : true,
31985 * @cfg {Boolean} isOriginTop = top align?
31987 isOriginTop : false,
31989 * @cfg {Boolean} isLayoutInstant = no animation?
31991 isLayoutInstant : false, // needed?
31993 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31995 isResizingContainer : true,
31997 * @cfg {Number} columnWidth width of the columns
32003 * @cfg {Number} maxCols maximum number of columns
32008 * @cfg {Number} padHeight padding below box..
32014 * @cfg {Boolean} isAutoInitial defalut true
32017 isAutoInitial : true,
32023 initialColumnWidth : 0,
32024 currentSize : null,
32026 colYs : null, // array.
32033 bricks: null, //CompositeElement
32034 cols : 0, // array?
32035 // element : null, // wrapped now this.el
32036 _isLayoutInited : null,
32039 getAutoCreate : function(){
32043 cls: 'blog-masonary-wrapper ' + this.cls,
32045 cls : 'mas-boxes masonary'
32052 getChildContainer: function( )
32054 if (this.boxesEl) {
32055 return this.boxesEl;
32058 this.boxesEl = this.el.select('.mas-boxes').first();
32060 return this.boxesEl;
32064 initEvents : function()
32068 if(this.isAutoInitial){
32069 Roo.log('hook children rendered');
32070 this.on('childrenrendered', function() {
32071 Roo.log('children rendered');
32078 initial : function()
32080 this.reloadItems();
32082 this.currentSize = this.el.getBox(true);
32084 /// was window resize... - let's see if this works..
32085 Roo.EventManager.onWindowResize(this.resize, this);
32087 if(!this.isAutoInitial){
32092 this.layout.defer(500,this);
32095 reloadItems: function()
32097 this.bricks = this.el.select('.masonry-brick', true);
32099 this.bricks.each(function(b) {
32100 //Roo.log(b.getSize());
32101 if (!b.attr('originalwidth')) {
32102 b.attr('originalwidth', b.getSize().width);
32107 Roo.log(this.bricks.elements.length);
32110 resize : function()
32113 var cs = this.el.getBox(true);
32115 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32116 Roo.log("no change in with or X");
32119 this.currentSize = cs;
32123 layout : function()
32126 this._resetLayout();
32127 //this._manageStamps();
32129 // don't animate first layout
32130 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32131 this.layoutItems( isInstant );
32133 // flag for initalized
32134 this._isLayoutInited = true;
32137 layoutItems : function( isInstant )
32139 //var items = this._getItemsForLayout( this.items );
32140 // original code supports filtering layout items.. we just ignore it..
32142 this._layoutItems( this.bricks , isInstant );
32144 this._postLayout();
32146 _layoutItems : function ( items , isInstant)
32148 //this.fireEvent( 'layout', this, items );
32151 if ( !items || !items.elements.length ) {
32152 // no items, emit event with empty array
32157 items.each(function(item) {
32158 Roo.log("layout item");
32160 // get x/y object from method
32161 var position = this._getItemLayoutPosition( item );
32163 position.item = item;
32164 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32165 queue.push( position );
32168 this._processLayoutQueue( queue );
32170 /** Sets position of item in DOM
32171 * @param {Element} item
32172 * @param {Number} x - horizontal position
32173 * @param {Number} y - vertical position
32174 * @param {Boolean} isInstant - disables transitions
32176 _processLayoutQueue : function( queue )
32178 for ( var i=0, len = queue.length; i < len; i++ ) {
32179 var obj = queue[i];
32180 obj.item.position('absolute');
32181 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32187 * Any logic you want to do after each layout,
32188 * i.e. size the container
32190 _postLayout : function()
32192 this.resizeContainer();
32195 resizeContainer : function()
32197 if ( !this.isResizingContainer ) {
32200 var size = this._getContainerSize();
32202 this.el.setSize(size.width,size.height);
32203 this.boxesEl.setSize(size.width,size.height);
32209 _resetLayout : function()
32211 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32212 this.colWidth = this.el.getWidth();
32213 //this.gutter = this.el.getWidth();
32215 this.measureColumns();
32221 this.colYs.push( 0 );
32227 measureColumns : function()
32229 this.getContainerWidth();
32230 // if columnWidth is 0, default to outerWidth of first item
32231 if ( !this.columnWidth ) {
32232 var firstItem = this.bricks.first();
32233 Roo.log(firstItem);
32234 this.columnWidth = this.containerWidth;
32235 if (firstItem && firstItem.attr('originalwidth') ) {
32236 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32238 // columnWidth fall back to item of first element
32239 Roo.log("set column width?");
32240 this.initialColumnWidth = this.columnWidth ;
32242 // if first elem has no width, default to size of container
32247 if (this.initialColumnWidth) {
32248 this.columnWidth = this.initialColumnWidth;
32253 // column width is fixed at the top - however if container width get's smaller we should
32256 // this bit calcs how man columns..
32258 var columnWidth = this.columnWidth += this.gutter;
32260 // calculate columns
32261 var containerWidth = this.containerWidth + this.gutter;
32263 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32264 // fix rounding errors, typically with gutters
32265 var excess = columnWidth - containerWidth % columnWidth;
32268 // if overshoot is less than a pixel, round up, otherwise floor it
32269 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32270 cols = Math[ mathMethod ]( cols );
32271 this.cols = Math.max( cols, 1 );
32272 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32274 // padding positioning..
32275 var totalColWidth = this.cols * this.columnWidth;
32276 var padavail = this.containerWidth - totalColWidth;
32277 // so for 2 columns - we need 3 'pads'
32279 var padNeeded = (1+this.cols) * this.padWidth;
32281 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32283 this.columnWidth += padExtra
32284 //this.padWidth = Math.floor(padavail / ( this.cols));
32286 // adjust colum width so that padding is fixed??
32288 // we have 3 columns ... total = width * 3
32289 // we have X left over... that should be used by
32291 //if (this.expandC) {
32299 getContainerWidth : function()
32301 /* // container is parent if fit width
32302 var container = this.isFitWidth ? this.element.parentNode : this.element;
32303 // check that this.size and size are there
32304 // IE8 triggers resize on body size change, so they might not be
32306 var size = getSize( container ); //FIXME
32307 this.containerWidth = size && size.innerWidth; //FIXME
32310 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32314 _getItemLayoutPosition : function( item ) // what is item?
32316 // we resize the item to our columnWidth..
32318 item.setWidth(this.columnWidth);
32319 item.autoBoxAdjust = false;
32321 var sz = item.getSize();
32323 // how many columns does this brick span
32324 var remainder = this.containerWidth % this.columnWidth;
32326 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32327 // round if off by 1 pixel, otherwise use ceil
32328 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32329 colSpan = Math.min( colSpan, this.cols );
32331 // normally this should be '1' as we dont' currently allow multi width columns..
32333 var colGroup = this._getColGroup( colSpan );
32334 // get the minimum Y value from the columns
32335 var minimumY = Math.min.apply( Math, colGroup );
32336 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32338 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32340 // position the brick
32342 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32343 y: this.currentSize.y + minimumY + this.padHeight
32347 // apply setHeight to necessary columns
32348 var setHeight = minimumY + sz.height + this.padHeight;
32349 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32351 var setSpan = this.cols + 1 - colGroup.length;
32352 for ( var i = 0; i < setSpan; i++ ) {
32353 this.colYs[ shortColIndex + i ] = setHeight ;
32360 * @param {Number} colSpan - number of columns the element spans
32361 * @returns {Array} colGroup
32363 _getColGroup : function( colSpan )
32365 if ( colSpan < 2 ) {
32366 // if brick spans only one column, use all the column Ys
32371 // how many different places could this brick fit horizontally
32372 var groupCount = this.cols + 1 - colSpan;
32373 // for each group potential horizontal position
32374 for ( var i = 0; i < groupCount; i++ ) {
32375 // make an array of colY values for that one group
32376 var groupColYs = this.colYs.slice( i, i + colSpan );
32377 // and get the max value of the array
32378 colGroup[i] = Math.max.apply( Math, groupColYs );
32383 _manageStamp : function( stamp )
32385 var stampSize = stamp.getSize();
32386 var offset = stamp.getBox();
32387 // get the columns that this stamp affects
32388 var firstX = this.isOriginLeft ? offset.x : offset.right;
32389 var lastX = firstX + stampSize.width;
32390 var firstCol = Math.floor( firstX / this.columnWidth );
32391 firstCol = Math.max( 0, firstCol );
32393 var lastCol = Math.floor( lastX / this.columnWidth );
32394 // lastCol should not go over if multiple of columnWidth #425
32395 lastCol -= lastX % this.columnWidth ? 0 : 1;
32396 lastCol = Math.min( this.cols - 1, lastCol );
32398 // set colYs to bottom of the stamp
32399 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32402 for ( var i = firstCol; i <= lastCol; i++ ) {
32403 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32408 _getContainerSize : function()
32410 this.maxY = Math.max.apply( Math, this.colYs );
32415 if ( this.isFitWidth ) {
32416 size.width = this._getContainerFitWidth();
32422 _getContainerFitWidth : function()
32424 var unusedCols = 0;
32425 // count unused columns
32428 if ( this.colYs[i] !== 0 ) {
32433 // fit container to columns that have been used
32434 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32437 needsResizeLayout : function()
32439 var previousWidth = this.containerWidth;
32440 this.getContainerWidth();
32441 return previousWidth !== this.containerWidth;
32456 * @class Roo.bootstrap.MasonryBrick
32457 * @extends Roo.bootstrap.Component
32458 * Bootstrap MasonryBrick class
32461 * Create a new MasonryBrick
32462 * @param {Object} config The config object
32465 Roo.bootstrap.MasonryBrick = function(config){
32467 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32469 Roo.bootstrap.MasonryBrick.register(this);
32475 * When a MasonryBrick is clcik
32476 * @param {Roo.bootstrap.MasonryBrick} this
32477 * @param {Roo.EventObject} e
32483 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32486 * @cfg {String} title
32490 * @cfg {String} html
32494 * @cfg {String} bgimage
32498 * @cfg {String} videourl
32502 * @cfg {String} cls
32506 * @cfg {String} href
32510 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32515 * @cfg {String} placetitle (center|bottom)
32520 * @cfg {Boolean} isFitContainer defalut true
32522 isFitContainer : true,
32525 * @cfg {Boolean} preventDefault defalut false
32527 preventDefault : false,
32530 * @cfg {Boolean} inverse defalut false
32532 maskInverse : false,
32534 getAutoCreate : function()
32536 if(!this.isFitContainer){
32537 return this.getSplitAutoCreate();
32540 var cls = 'masonry-brick masonry-brick-full';
32542 if(this.href.length){
32543 cls += ' masonry-brick-link';
32546 if(this.bgimage.length){
32547 cls += ' masonry-brick-image';
32550 if(this.maskInverse){
32551 cls += ' mask-inverse';
32554 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32555 cls += ' enable-mask';
32559 cls += ' masonry-' + this.size + '-brick';
32562 if(this.placetitle.length){
32564 switch (this.placetitle) {
32566 cls += ' masonry-center-title';
32569 cls += ' masonry-bottom-title';
32576 if(!this.html.length && !this.bgimage.length){
32577 cls += ' masonry-center-title';
32580 if(!this.html.length && this.bgimage.length){
32581 cls += ' masonry-bottom-title';
32586 cls += ' ' + this.cls;
32590 tag: (this.href.length) ? 'a' : 'div',
32595 cls: 'masonry-brick-mask'
32599 cls: 'masonry-brick-paragraph',
32605 if(this.href.length){
32606 cfg.href = this.href;
32609 var cn = cfg.cn[1].cn;
32611 if(this.title.length){
32614 cls: 'masonry-brick-title',
32619 if(this.html.length){
32622 cls: 'masonry-brick-text',
32627 if (!this.title.length && !this.html.length) {
32628 cfg.cn[1].cls += ' hide';
32631 if(this.bgimage.length){
32634 cls: 'masonry-brick-image-view',
32639 if(this.videourl.length){
32640 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32641 // youtube support only?
32644 cls: 'masonry-brick-image-view',
32647 allowfullscreen : true
32655 getSplitAutoCreate : function()
32657 var cls = 'masonry-brick masonry-brick-split';
32659 if(this.href.length){
32660 cls += ' masonry-brick-link';
32663 if(this.bgimage.length){
32664 cls += ' masonry-brick-image';
32668 cls += ' masonry-' + this.size + '-brick';
32671 switch (this.placetitle) {
32673 cls += ' masonry-center-title';
32676 cls += ' masonry-bottom-title';
32679 if(!this.bgimage.length){
32680 cls += ' masonry-center-title';
32683 if(this.bgimage.length){
32684 cls += ' masonry-bottom-title';
32690 cls += ' ' + this.cls;
32694 tag: (this.href.length) ? 'a' : 'div',
32699 cls: 'masonry-brick-split-head',
32703 cls: 'masonry-brick-paragraph',
32710 cls: 'masonry-brick-split-body',
32716 if(this.href.length){
32717 cfg.href = this.href;
32720 if(this.title.length){
32721 cfg.cn[0].cn[0].cn.push({
32723 cls: 'masonry-brick-title',
32728 if(this.html.length){
32729 cfg.cn[1].cn.push({
32731 cls: 'masonry-brick-text',
32736 if(this.bgimage.length){
32737 cfg.cn[0].cn.push({
32739 cls: 'masonry-brick-image-view',
32744 if(this.videourl.length){
32745 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32746 // youtube support only?
32747 cfg.cn[0].cn.cn.push({
32749 cls: 'masonry-brick-image-view',
32752 allowfullscreen : true
32759 initEvents: function()
32761 switch (this.size) {
32794 this.el.on('touchstart', this.onTouchStart, this);
32795 this.el.on('touchmove', this.onTouchMove, this);
32796 this.el.on('touchend', this.onTouchEnd, this);
32797 this.el.on('contextmenu', this.onContextMenu, this);
32799 this.el.on('mouseenter' ,this.enter, this);
32800 this.el.on('mouseleave', this.leave, this);
32801 this.el.on('click', this.onClick, this);
32804 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32805 this.parent().bricks.push(this);
32810 onClick: function(e, el)
32812 var time = this.endTimer - this.startTimer;
32813 // Roo.log(e.preventDefault());
32816 e.preventDefault();
32821 if(!this.preventDefault){
32825 e.preventDefault();
32827 if (this.activeClass != '') {
32828 this.selectBrick();
32831 this.fireEvent('click', this, e);
32834 enter: function(e, el)
32836 e.preventDefault();
32838 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32842 if(this.bgimage.length && this.html.length){
32843 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32847 leave: function(e, el)
32849 e.preventDefault();
32851 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32855 if(this.bgimage.length && this.html.length){
32856 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32860 onTouchStart: function(e, el)
32862 // e.preventDefault();
32864 this.touchmoved = false;
32866 if(!this.isFitContainer){
32870 if(!this.bgimage.length || !this.html.length){
32874 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32876 this.timer = new Date().getTime();
32880 onTouchMove: function(e, el)
32882 this.touchmoved = true;
32885 onContextMenu : function(e,el)
32887 e.preventDefault();
32888 e.stopPropagation();
32892 onTouchEnd: function(e, el)
32894 // e.preventDefault();
32896 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32903 if(!this.bgimage.length || !this.html.length){
32905 if(this.href.length){
32906 window.location.href = this.href;
32912 if(!this.isFitContainer){
32916 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32918 window.location.href = this.href;
32921 //selection on single brick only
32922 selectBrick : function() {
32924 if (!this.parentId) {
32928 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32929 var index = m.selectedBrick.indexOf(this.id);
32932 m.selectedBrick.splice(index,1);
32933 this.el.removeClass(this.activeClass);
32937 for(var i = 0; i < m.selectedBrick.length; i++) {
32938 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32939 b.el.removeClass(b.activeClass);
32942 m.selectedBrick = [];
32944 m.selectedBrick.push(this.id);
32945 this.el.addClass(this.activeClass);
32949 isSelected : function(){
32950 return this.el.hasClass(this.activeClass);
32955 Roo.apply(Roo.bootstrap.MasonryBrick, {
32958 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32960 * register a Masonry Brick
32961 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32964 register : function(brick)
32966 //this.groups[brick.id] = brick;
32967 this.groups.add(brick.id, brick);
32970 * fetch a masonry brick based on the masonry brick ID
32971 * @param {string} the masonry brick to add
32972 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32975 get: function(brick_id)
32977 // if (typeof(this.groups[brick_id]) == 'undefined') {
32980 // return this.groups[brick_id] ;
32982 if(this.groups.key(brick_id)) {
32983 return this.groups.key(brick_id);
33001 * @class Roo.bootstrap.Brick
33002 * @extends Roo.bootstrap.Component
33003 * Bootstrap Brick class
33006 * Create a new Brick
33007 * @param {Object} config The config object
33010 Roo.bootstrap.Brick = function(config){
33011 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33017 * When a Brick is click
33018 * @param {Roo.bootstrap.Brick} this
33019 * @param {Roo.EventObject} e
33025 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33028 * @cfg {String} title
33032 * @cfg {String} html
33036 * @cfg {String} bgimage
33040 * @cfg {String} cls
33044 * @cfg {String} href
33048 * @cfg {String} video
33052 * @cfg {Boolean} square
33056 getAutoCreate : function()
33058 var cls = 'roo-brick';
33060 if(this.href.length){
33061 cls += ' roo-brick-link';
33064 if(this.bgimage.length){
33065 cls += ' roo-brick-image';
33068 if(!this.html.length && !this.bgimage.length){
33069 cls += ' roo-brick-center-title';
33072 if(!this.html.length && this.bgimage.length){
33073 cls += ' roo-brick-bottom-title';
33077 cls += ' ' + this.cls;
33081 tag: (this.href.length) ? 'a' : 'div',
33086 cls: 'roo-brick-paragraph',
33092 if(this.href.length){
33093 cfg.href = this.href;
33096 var cn = cfg.cn[0].cn;
33098 if(this.title.length){
33101 cls: 'roo-brick-title',
33106 if(this.html.length){
33109 cls: 'roo-brick-text',
33116 if(this.bgimage.length){
33119 cls: 'roo-brick-image-view',
33127 initEvents: function()
33129 if(this.title.length || this.html.length){
33130 this.el.on('mouseenter' ,this.enter, this);
33131 this.el.on('mouseleave', this.leave, this);
33134 Roo.EventManager.onWindowResize(this.resize, this);
33136 if(this.bgimage.length){
33137 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33138 this.imageEl.on('load', this.onImageLoad, this);
33145 onImageLoad : function()
33150 resize : function()
33152 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33154 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33156 if(this.bgimage.length){
33157 var image = this.el.select('.roo-brick-image-view', true).first();
33159 image.setWidth(paragraph.getWidth());
33162 image.setHeight(paragraph.getWidth());
33165 this.el.setHeight(image.getHeight());
33166 paragraph.setHeight(image.getHeight());
33172 enter: function(e, el)
33174 e.preventDefault();
33176 if(this.bgimage.length){
33177 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33178 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33182 leave: function(e, el)
33184 e.preventDefault();
33186 if(this.bgimage.length){
33187 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33188 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33203 * @class Roo.bootstrap.NumberField
33204 * @extends Roo.bootstrap.Input
33205 * Bootstrap NumberField class
33211 * Create a new NumberField
33212 * @param {Object} config The config object
33215 Roo.bootstrap.NumberField = function(config){
33216 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33219 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33222 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33224 allowDecimals : true,
33226 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33228 decimalSeparator : ".",
33230 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33232 decimalPrecision : 2,
33234 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33236 allowNegative : true,
33239 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33243 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33245 minValue : Number.NEGATIVE_INFINITY,
33247 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33249 maxValue : Number.MAX_VALUE,
33251 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33253 minText : "The minimum value for this field is {0}",
33255 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33257 maxText : "The maximum value for this field is {0}",
33259 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33260 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33262 nanText : "{0} is not a valid number",
33264 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33266 thousandsDelimiter : false,
33268 * @cfg {String} valueAlign alignment of value
33270 valueAlign : "left",
33272 getAutoCreate : function()
33274 var hiddenInput = {
33278 cls: 'hidden-number-input'
33282 hiddenInput.name = this.name;
33287 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33289 this.name = hiddenInput.name;
33291 if(cfg.cn.length > 0) {
33292 cfg.cn.push(hiddenInput);
33299 initEvents : function()
33301 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33303 var allowed = "0123456789";
33305 if(this.allowDecimals){
33306 allowed += this.decimalSeparator;
33309 if(this.allowNegative){
33313 if(this.thousandsDelimiter) {
33317 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33319 var keyPress = function(e){
33321 var k = e.getKey();
33323 var c = e.getCharCode();
33326 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33327 allowed.indexOf(String.fromCharCode(c)) === -1
33333 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33337 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33342 this.el.on("keypress", keyPress, this);
33345 validateValue : function(value)
33348 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33352 var num = this.parseValue(value);
33355 this.markInvalid(String.format(this.nanText, value));
33359 if(num < this.minValue){
33360 this.markInvalid(String.format(this.minText, this.minValue));
33364 if(num > this.maxValue){
33365 this.markInvalid(String.format(this.maxText, this.maxValue));
33372 getValue : function()
33374 var v = this.hiddenEl().getValue();
33376 return this.fixPrecision(this.parseValue(v));
33379 parseValue : function(value)
33381 if(this.thousandsDelimiter) {
33383 r = new RegExp(",", "g");
33384 value = value.replace(r, "");
33387 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33388 return isNaN(value) ? '' : value;
33391 fixPrecision : function(value)
33393 if(this.thousandsDelimiter) {
33395 r = new RegExp(",", "g");
33396 value = value.replace(r, "");
33399 var nan = isNaN(value);
33401 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33402 return nan ? '' : value;
33404 return parseFloat(value).toFixed(this.decimalPrecision);
33407 setValue : function(v)
33409 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33415 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33417 this.inputEl().dom.value = (v == '') ? '' :
33418 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33420 if(!this.allowZero && v === '0') {
33421 this.hiddenEl().dom.value = '';
33422 this.inputEl().dom.value = '';
33429 decimalPrecisionFcn : function(v)
33431 return Math.floor(v);
33434 beforeBlur : function()
33436 var v = this.parseValue(this.getRawValue());
33438 if(v || v === 0 || v === ''){
33443 hiddenEl : function()
33445 return this.el.select('input.hidden-number-input',true).first();
33457 * @class Roo.bootstrap.DocumentSlider
33458 * @extends Roo.bootstrap.Component
33459 * Bootstrap DocumentSlider class
33462 * Create a new DocumentViewer
33463 * @param {Object} config The config object
33466 Roo.bootstrap.DocumentSlider = function(config){
33467 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33474 * Fire after initEvent
33475 * @param {Roo.bootstrap.DocumentSlider} this
33480 * Fire after update
33481 * @param {Roo.bootstrap.DocumentSlider} this
33487 * @param {Roo.bootstrap.DocumentSlider} this
33493 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33499 getAutoCreate : function()
33503 cls : 'roo-document-slider',
33507 cls : 'roo-document-slider-header',
33511 cls : 'roo-document-slider-header-title'
33517 cls : 'roo-document-slider-body',
33521 cls : 'roo-document-slider-prev',
33525 cls : 'fa fa-chevron-left'
33531 cls : 'roo-document-slider-thumb',
33535 cls : 'roo-document-slider-image'
33541 cls : 'roo-document-slider-next',
33545 cls : 'fa fa-chevron-right'
33557 initEvents : function()
33559 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33560 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33562 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33563 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33565 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33566 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33568 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33569 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33571 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33572 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33574 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33575 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33577 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33578 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33580 this.thumbEl.on('click', this.onClick, this);
33582 this.prevIndicator.on('click', this.prev, this);
33584 this.nextIndicator.on('click', this.next, this);
33588 initial : function()
33590 if(this.files.length){
33591 this.indicator = 1;
33595 this.fireEvent('initial', this);
33598 update : function()
33600 this.imageEl.attr('src', this.files[this.indicator - 1]);
33602 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33604 this.prevIndicator.show();
33606 if(this.indicator == 1){
33607 this.prevIndicator.hide();
33610 this.nextIndicator.show();
33612 if(this.indicator == this.files.length){
33613 this.nextIndicator.hide();
33616 this.thumbEl.scrollTo('top');
33618 this.fireEvent('update', this);
33621 onClick : function(e)
33623 e.preventDefault();
33625 this.fireEvent('click', this);
33630 e.preventDefault();
33632 this.indicator = Math.max(1, this.indicator - 1);
33639 e.preventDefault();
33641 this.indicator = Math.min(this.files.length, this.indicator + 1);
33655 * @class Roo.bootstrap.RadioSet
33656 * @extends Roo.bootstrap.Input
33657 * Bootstrap RadioSet class
33658 * @cfg {String} indicatorpos (left|right) default left
33659 * @cfg {Boolean} inline (true|false) inline the element (default true)
33660 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33662 * Create a new RadioSet
33663 * @param {Object} config The config object
33666 Roo.bootstrap.RadioSet = function(config){
33668 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33672 Roo.bootstrap.RadioSet.register(this);
33677 * Fires when the element is checked or unchecked.
33678 * @param {Roo.bootstrap.RadioSet} this This radio
33679 * @param {Roo.bootstrap.Radio} item The checked item
33684 * Fires when the element is click.
33685 * @param {Roo.bootstrap.RadioSet} this This radio set
33686 * @param {Roo.bootstrap.Radio} item The checked item
33687 * @param {Roo.EventObject} e The event object
33694 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33702 indicatorpos : 'left',
33704 getAutoCreate : function()
33708 cls : 'roo-radio-set-label',
33712 html : this.fieldLabel
33717 if(this.indicatorpos == 'left'){
33720 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33721 tooltip : 'This field is required'
33726 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33727 tooltip : 'This field is required'
33733 cls : 'roo-radio-set-items'
33736 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33738 if (align === 'left' && this.fieldLabel.length) {
33741 cls : "roo-radio-set-right",
33747 if(this.labelWidth > 12){
33748 label.style = "width: " + this.labelWidth + 'px';
33751 if(this.labelWidth < 13 && this.labelmd == 0){
33752 this.labelmd = this.labelWidth;
33755 if(this.labellg > 0){
33756 label.cls += ' col-lg-' + this.labellg;
33757 items.cls += ' col-lg-' + (12 - this.labellg);
33760 if(this.labelmd > 0){
33761 label.cls += ' col-md-' + this.labelmd;
33762 items.cls += ' col-md-' + (12 - this.labelmd);
33765 if(this.labelsm > 0){
33766 label.cls += ' col-sm-' + this.labelsm;
33767 items.cls += ' col-sm-' + (12 - this.labelsm);
33770 if(this.labelxs > 0){
33771 label.cls += ' col-xs-' + this.labelxs;
33772 items.cls += ' col-xs-' + (12 - this.labelxs);
33778 cls : 'roo-radio-set',
33782 cls : 'roo-radio-set-input',
33785 value : this.value ? this.value : ''
33792 if(this.weight.length){
33793 cfg.cls += ' roo-radio-' + this.weight;
33797 cfg.cls += ' roo-radio-set-inline';
33801 ['xs','sm','md','lg'].map(function(size){
33802 if (settings[size]) {
33803 cfg.cls += ' col-' + size + '-' + settings[size];
33811 initEvents : function()
33813 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33814 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33816 if(!this.fieldLabel.length){
33817 this.labelEl.hide();
33820 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33821 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33823 this.indicator = this.indicatorEl();
33825 if(this.indicator){
33826 this.indicator.addClass('invisible');
33829 this.originalValue = this.getValue();
33833 inputEl: function ()
33835 return this.el.select('.roo-radio-set-input', true).first();
33838 getChildContainer : function()
33840 return this.itemsEl;
33843 register : function(item)
33845 this.radioes.push(item);
33849 validate : function()
33851 if(this.getVisibilityEl().hasClass('hidden')){
33857 Roo.each(this.radioes, function(i){
33866 if(this.allowBlank) {
33870 if(this.disabled || valid){
33875 this.markInvalid();
33880 markValid : function()
33882 if(this.labelEl.isVisible(true)){
33883 this.indicatorEl().removeClass('visible');
33884 this.indicatorEl().addClass('invisible');
33887 this.el.removeClass([this.invalidClass, this.validClass]);
33888 this.el.addClass(this.validClass);
33890 this.fireEvent('valid', this);
33893 markInvalid : function(msg)
33895 if(this.allowBlank || this.disabled){
33899 if(this.labelEl.isVisible(true)){
33900 this.indicatorEl().removeClass('invisible');
33901 this.indicatorEl().addClass('visible');
33904 this.el.removeClass([this.invalidClass, this.validClass]);
33905 this.el.addClass(this.invalidClass);
33907 this.fireEvent('invalid', this, msg);
33911 setValue : function(v, suppressEvent)
33913 if(this.value === v){
33920 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33923 Roo.each(this.radioes, function(i){
33925 i.el.removeClass('checked');
33928 Roo.each(this.radioes, function(i){
33930 if(i.value === v || i.value.toString() === v.toString()){
33932 i.el.addClass('checked');
33934 if(suppressEvent !== true){
33935 this.fireEvent('check', this, i);
33946 clearInvalid : function(){
33948 if(!this.el || this.preventMark){
33952 this.el.removeClass([this.invalidClass]);
33954 this.fireEvent('valid', this);
33959 Roo.apply(Roo.bootstrap.RadioSet, {
33963 register : function(set)
33965 this.groups[set.name] = set;
33968 get: function(name)
33970 if (typeof(this.groups[name]) == 'undefined') {
33974 return this.groups[name] ;
33980 * Ext JS Library 1.1.1
33981 * Copyright(c) 2006-2007, Ext JS, LLC.
33983 * Originally Released Under LGPL - original licence link has changed is not relivant.
33986 * <script type="text/javascript">
33991 * @class Roo.bootstrap.SplitBar
33992 * @extends Roo.util.Observable
33993 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33997 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33998 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33999 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34000 split.minSize = 100;
34001 split.maxSize = 600;
34002 split.animate = true;
34003 split.on('moved', splitterMoved);
34006 * Create a new SplitBar
34007 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34008 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34009 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34010 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34011 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34012 position of the SplitBar).
34014 Roo.bootstrap.SplitBar = function(cfg){
34019 // dragElement : elm
34020 // resizingElement: el,
34022 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34023 // placement : Roo.bootstrap.SplitBar.LEFT ,
34024 // existingProxy ???
34027 this.el = Roo.get(cfg.dragElement, true);
34028 this.el.dom.unselectable = "on";
34030 this.resizingEl = Roo.get(cfg.resizingElement, true);
34034 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34035 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34038 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34041 * The minimum size of the resizing element. (Defaults to 0)
34047 * The maximum size of the resizing element. (Defaults to 2000)
34050 this.maxSize = 2000;
34053 * Whether to animate the transition to the new size
34056 this.animate = false;
34059 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34062 this.useShim = false;
34067 if(!cfg.existingProxy){
34069 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34071 this.proxy = Roo.get(cfg.existingProxy).dom;
34074 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34077 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34080 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34083 this.dragSpecs = {};
34086 * @private The adapter to use to positon and resize elements
34088 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34089 this.adapter.init(this);
34091 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34093 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34094 this.el.addClass("roo-splitbar-h");
34097 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34098 this.el.addClass("roo-splitbar-v");
34104 * Fires when the splitter is moved (alias for {@link #event-moved})
34105 * @param {Roo.bootstrap.SplitBar} this
34106 * @param {Number} newSize the new width or height
34111 * Fires when the splitter is moved
34112 * @param {Roo.bootstrap.SplitBar} this
34113 * @param {Number} newSize the new width or height
34117 * @event beforeresize
34118 * Fires before the splitter is dragged
34119 * @param {Roo.bootstrap.SplitBar} this
34121 "beforeresize" : true,
34123 "beforeapply" : true
34126 Roo.util.Observable.call(this);
34129 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34130 onStartProxyDrag : function(x, y){
34131 this.fireEvent("beforeresize", this);
34133 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34135 o.enableDisplayMode("block");
34136 // all splitbars share the same overlay
34137 Roo.bootstrap.SplitBar.prototype.overlay = o;
34139 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34140 this.overlay.show();
34141 Roo.get(this.proxy).setDisplayed("block");
34142 var size = this.adapter.getElementSize(this);
34143 this.activeMinSize = this.getMinimumSize();;
34144 this.activeMaxSize = this.getMaximumSize();;
34145 var c1 = size - this.activeMinSize;
34146 var c2 = Math.max(this.activeMaxSize - size, 0);
34147 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34148 this.dd.resetConstraints();
34149 this.dd.setXConstraint(
34150 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34151 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34153 this.dd.setYConstraint(0, 0);
34155 this.dd.resetConstraints();
34156 this.dd.setXConstraint(0, 0);
34157 this.dd.setYConstraint(
34158 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34159 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34162 this.dragSpecs.startSize = size;
34163 this.dragSpecs.startPoint = [x, y];
34164 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34168 * @private Called after the drag operation by the DDProxy
34170 onEndProxyDrag : function(e){
34171 Roo.get(this.proxy).setDisplayed(false);
34172 var endPoint = Roo.lib.Event.getXY(e);
34174 this.overlay.hide();
34177 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34178 newSize = this.dragSpecs.startSize +
34179 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34180 endPoint[0] - this.dragSpecs.startPoint[0] :
34181 this.dragSpecs.startPoint[0] - endPoint[0]
34184 newSize = this.dragSpecs.startSize +
34185 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34186 endPoint[1] - this.dragSpecs.startPoint[1] :
34187 this.dragSpecs.startPoint[1] - endPoint[1]
34190 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34191 if(newSize != this.dragSpecs.startSize){
34192 if(this.fireEvent('beforeapply', this, newSize) !== false){
34193 this.adapter.setElementSize(this, newSize);
34194 this.fireEvent("moved", this, newSize);
34195 this.fireEvent("resize", this, newSize);
34201 * Get the adapter this SplitBar uses
34202 * @return The adapter object
34204 getAdapter : function(){
34205 return this.adapter;
34209 * Set the adapter this SplitBar uses
34210 * @param {Object} adapter A SplitBar adapter object
34212 setAdapter : function(adapter){
34213 this.adapter = adapter;
34214 this.adapter.init(this);
34218 * Gets the minimum size for the resizing element
34219 * @return {Number} The minimum size
34221 getMinimumSize : function(){
34222 return this.minSize;
34226 * Sets the minimum size for the resizing element
34227 * @param {Number} minSize The minimum size
34229 setMinimumSize : function(minSize){
34230 this.minSize = minSize;
34234 * Gets the maximum size for the resizing element
34235 * @return {Number} The maximum size
34237 getMaximumSize : function(){
34238 return this.maxSize;
34242 * Sets the maximum size for the resizing element
34243 * @param {Number} maxSize The maximum size
34245 setMaximumSize : function(maxSize){
34246 this.maxSize = maxSize;
34250 * Sets the initialize size for the resizing element
34251 * @param {Number} size The initial size
34253 setCurrentSize : function(size){
34254 var oldAnimate = this.animate;
34255 this.animate = false;
34256 this.adapter.setElementSize(this, size);
34257 this.animate = oldAnimate;
34261 * Destroy this splitbar.
34262 * @param {Boolean} removeEl True to remove the element
34264 destroy : function(removeEl){
34266 this.shim.remove();
34269 this.proxy.parentNode.removeChild(this.proxy);
34277 * @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.
34279 Roo.bootstrap.SplitBar.createProxy = function(dir){
34280 var proxy = new Roo.Element(document.createElement("div"));
34281 proxy.unselectable();
34282 var cls = 'roo-splitbar-proxy';
34283 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34284 document.body.appendChild(proxy.dom);
34289 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34290 * Default Adapter. It assumes the splitter and resizing element are not positioned
34291 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34293 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34296 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34297 // do nothing for now
34298 init : function(s){
34302 * Called before drag operations to get the current size of the resizing element.
34303 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34305 getElementSize : function(s){
34306 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34307 return s.resizingEl.getWidth();
34309 return s.resizingEl.getHeight();
34314 * Called after drag operations to set the size of the resizing element.
34315 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34316 * @param {Number} newSize The new size to set
34317 * @param {Function} onComplete A function to be invoked when resizing is complete
34319 setElementSize : function(s, newSize, onComplete){
34320 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34322 s.resizingEl.setWidth(newSize);
34324 onComplete(s, newSize);
34327 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34332 s.resizingEl.setHeight(newSize);
34334 onComplete(s, newSize);
34337 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34344 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34345 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34346 * Adapter that moves the splitter element to align with the resized sizing element.
34347 * Used with an absolute positioned SplitBar.
34348 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34349 * document.body, make sure you assign an id to the body element.
34351 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34352 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34353 this.container = Roo.get(container);
34356 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34357 init : function(s){
34358 this.basic.init(s);
34361 getElementSize : function(s){
34362 return this.basic.getElementSize(s);
34365 setElementSize : function(s, newSize, onComplete){
34366 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34369 moveSplitter : function(s){
34370 var yes = Roo.bootstrap.SplitBar;
34371 switch(s.placement){
34373 s.el.setX(s.resizingEl.getRight());
34376 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34379 s.el.setY(s.resizingEl.getBottom());
34382 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34389 * Orientation constant - Create a vertical SplitBar
34393 Roo.bootstrap.SplitBar.VERTICAL = 1;
34396 * Orientation constant - Create a horizontal SplitBar
34400 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34403 * Placement constant - The resizing element is to the left of the splitter element
34407 Roo.bootstrap.SplitBar.LEFT = 1;
34410 * Placement constant - The resizing element is to the right of the splitter element
34414 Roo.bootstrap.SplitBar.RIGHT = 2;
34417 * Placement constant - The resizing element is positioned above the splitter element
34421 Roo.bootstrap.SplitBar.TOP = 3;
34424 * Placement constant - The resizing element is positioned under splitter element
34428 Roo.bootstrap.SplitBar.BOTTOM = 4;
34429 Roo.namespace("Roo.bootstrap.layout");/*
34431 * Ext JS Library 1.1.1
34432 * Copyright(c) 2006-2007, Ext JS, LLC.
34434 * Originally Released Under LGPL - original licence link has changed is not relivant.
34437 * <script type="text/javascript">
34441 * @class Roo.bootstrap.layout.Manager
34442 * @extends Roo.bootstrap.Component
34443 * Base class for layout managers.
34445 Roo.bootstrap.layout.Manager = function(config)
34447 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34453 /** false to disable window resize monitoring @type Boolean */
34454 this.monitorWindowResize = true;
34459 * Fires when a layout is performed.
34460 * @param {Roo.LayoutManager} this
34464 * @event regionresized
34465 * Fires when the user resizes a region.
34466 * @param {Roo.LayoutRegion} region The resized region
34467 * @param {Number} newSize The new size (width for east/west, height for north/south)
34469 "regionresized" : true,
34471 * @event regioncollapsed
34472 * Fires when a region is collapsed.
34473 * @param {Roo.LayoutRegion} region The collapsed region
34475 "regioncollapsed" : true,
34477 * @event regionexpanded
34478 * Fires when a region is expanded.
34479 * @param {Roo.LayoutRegion} region The expanded region
34481 "regionexpanded" : true
34483 this.updating = false;
34486 this.el = Roo.get(config.el);
34492 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34497 monitorWindowResize : true,
34503 onRender : function(ct, position)
34506 this.el = Roo.get(ct);
34509 //this.fireEvent('render',this);
34513 initEvents: function()
34517 // ie scrollbar fix
34518 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34519 document.body.scroll = "no";
34520 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34521 this.el.position('relative');
34523 this.id = this.el.id;
34524 this.el.addClass("roo-layout-container");
34525 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34526 if(this.el.dom != document.body ) {
34527 this.el.on('resize', this.layout,this);
34528 this.el.on('show', this.layout,this);
34534 * Returns true if this layout is currently being updated
34535 * @return {Boolean}
34537 isUpdating : function(){
34538 return this.updating;
34542 * Suspend the LayoutManager from doing auto-layouts while
34543 * making multiple add or remove calls
34545 beginUpdate : function(){
34546 this.updating = true;
34550 * Restore auto-layouts and optionally disable the manager from performing a layout
34551 * @param {Boolean} noLayout true to disable a layout update
34553 endUpdate : function(noLayout){
34554 this.updating = false;
34560 layout: function(){
34564 onRegionResized : function(region, newSize){
34565 this.fireEvent("regionresized", region, newSize);
34569 onRegionCollapsed : function(region){
34570 this.fireEvent("regioncollapsed", region);
34573 onRegionExpanded : function(region){
34574 this.fireEvent("regionexpanded", region);
34578 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34579 * performs box-model adjustments.
34580 * @return {Object} The size as an object {width: (the width), height: (the height)}
34582 getViewSize : function()
34585 if(this.el.dom != document.body){
34586 size = this.el.getSize();
34588 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34590 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34591 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34596 * Returns the Element this layout is bound to.
34597 * @return {Roo.Element}
34599 getEl : function(){
34604 * Returns the specified region.
34605 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34606 * @return {Roo.LayoutRegion}
34608 getRegion : function(target){
34609 return this.regions[target.toLowerCase()];
34612 onWindowResize : function(){
34613 if(this.monitorWindowResize){
34620 * Ext JS Library 1.1.1
34621 * Copyright(c) 2006-2007, Ext JS, LLC.
34623 * Originally Released Under LGPL - original licence link has changed is not relivant.
34626 * <script type="text/javascript">
34629 * @class Roo.bootstrap.layout.Border
34630 * @extends Roo.bootstrap.layout.Manager
34631 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34632 * please see: examples/bootstrap/nested.html<br><br>
34634 <b>The container the layout is rendered into can be either the body element or any other element.
34635 If it is not the body element, the container needs to either be an absolute positioned element,
34636 or you will need to add "position:relative" to the css of the container. You will also need to specify
34637 the container size if it is not the body element.</b>
34640 * Create a new Border
34641 * @param {Object} config Configuration options
34643 Roo.bootstrap.layout.Border = function(config){
34644 config = config || {};
34645 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34649 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34650 if(config[region]){
34651 config[region].region = region;
34652 this.addRegion(config[region]);
34658 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34660 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34662 * Creates and adds a new region if it doesn't already exist.
34663 * @param {String} target The target region key (north, south, east, west or center).
34664 * @param {Object} config The regions config object
34665 * @return {BorderLayoutRegion} The new region
34667 addRegion : function(config)
34669 if(!this.regions[config.region]){
34670 var r = this.factory(config);
34671 this.bindRegion(r);
34673 return this.regions[config.region];
34677 bindRegion : function(r){
34678 this.regions[r.config.region] = r;
34680 r.on("visibilitychange", this.layout, this);
34681 r.on("paneladded", this.layout, this);
34682 r.on("panelremoved", this.layout, this);
34683 r.on("invalidated", this.layout, this);
34684 r.on("resized", this.onRegionResized, this);
34685 r.on("collapsed", this.onRegionCollapsed, this);
34686 r.on("expanded", this.onRegionExpanded, this);
34690 * Performs a layout update.
34692 layout : function()
34694 if(this.updating) {
34698 // render all the rebions if they have not been done alreayd?
34699 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34700 if(this.regions[region] && !this.regions[region].bodyEl){
34701 this.regions[region].onRender(this.el)
34705 var size = this.getViewSize();
34706 var w = size.width;
34707 var h = size.height;
34712 //var x = 0, y = 0;
34714 var rs = this.regions;
34715 var north = rs["north"];
34716 var south = rs["south"];
34717 var west = rs["west"];
34718 var east = rs["east"];
34719 var center = rs["center"];
34720 //if(this.hideOnLayout){ // not supported anymore
34721 //c.el.setStyle("display", "none");
34723 if(north && north.isVisible()){
34724 var b = north.getBox();
34725 var m = north.getMargins();
34726 b.width = w - (m.left+m.right);
34729 centerY = b.height + b.y + m.bottom;
34730 centerH -= centerY;
34731 north.updateBox(this.safeBox(b));
34733 if(south && south.isVisible()){
34734 var b = south.getBox();
34735 var m = south.getMargins();
34736 b.width = w - (m.left+m.right);
34738 var totalHeight = (b.height + m.top + m.bottom);
34739 b.y = h - totalHeight + m.top;
34740 centerH -= totalHeight;
34741 south.updateBox(this.safeBox(b));
34743 if(west && west.isVisible()){
34744 var b = west.getBox();
34745 var m = west.getMargins();
34746 b.height = centerH - (m.top+m.bottom);
34748 b.y = centerY + m.top;
34749 var totalWidth = (b.width + m.left + m.right);
34750 centerX += totalWidth;
34751 centerW -= totalWidth;
34752 west.updateBox(this.safeBox(b));
34754 if(east && east.isVisible()){
34755 var b = east.getBox();
34756 var m = east.getMargins();
34757 b.height = centerH - (m.top+m.bottom);
34758 var totalWidth = (b.width + m.left + m.right);
34759 b.x = w - totalWidth + m.left;
34760 b.y = centerY + m.top;
34761 centerW -= totalWidth;
34762 east.updateBox(this.safeBox(b));
34765 var m = center.getMargins();
34767 x: centerX + m.left,
34768 y: centerY + m.top,
34769 width: centerW - (m.left+m.right),
34770 height: centerH - (m.top+m.bottom)
34772 //if(this.hideOnLayout){
34773 //center.el.setStyle("display", "block");
34775 center.updateBox(this.safeBox(centerBox));
34778 this.fireEvent("layout", this);
34782 safeBox : function(box){
34783 box.width = Math.max(0, box.width);
34784 box.height = Math.max(0, box.height);
34789 * Adds a ContentPanel (or subclass) to this layout.
34790 * @param {String} target The target region key (north, south, east, west or center).
34791 * @param {Roo.ContentPanel} panel The panel to add
34792 * @return {Roo.ContentPanel} The added panel
34794 add : function(target, panel){
34796 target = target.toLowerCase();
34797 return this.regions[target].add(panel);
34801 * Remove a ContentPanel (or subclass) to this layout.
34802 * @param {String} target The target region key (north, south, east, west or center).
34803 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34804 * @return {Roo.ContentPanel} The removed panel
34806 remove : function(target, panel){
34807 target = target.toLowerCase();
34808 return this.regions[target].remove(panel);
34812 * Searches all regions for a panel with the specified id
34813 * @param {String} panelId
34814 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34816 findPanel : function(panelId){
34817 var rs = this.regions;
34818 for(var target in rs){
34819 if(typeof rs[target] != "function"){
34820 var p = rs[target].getPanel(panelId);
34830 * Searches all regions for a panel with the specified id and activates (shows) it.
34831 * @param {String/ContentPanel} panelId The panels id or the panel itself
34832 * @return {Roo.ContentPanel} The shown panel or null
34834 showPanel : function(panelId) {
34835 var rs = this.regions;
34836 for(var target in rs){
34837 var r = rs[target];
34838 if(typeof r != "function"){
34839 if(r.hasPanel(panelId)){
34840 return r.showPanel(panelId);
34848 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34849 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34852 restoreState : function(provider){
34854 provider = Roo.state.Manager;
34856 var sm = new Roo.LayoutStateManager();
34857 sm.init(this, provider);
34863 * Adds a xtype elements to the layout.
34867 xtype : 'ContentPanel',
34874 xtype : 'NestedLayoutPanel',
34880 items : [ ... list of content panels or nested layout panels.. ]
34884 * @param {Object} cfg Xtype definition of item to add.
34886 addxtype : function(cfg)
34888 // basically accepts a pannel...
34889 // can accept a layout region..!?!?
34890 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34893 // theory? children can only be panels??
34895 //if (!cfg.xtype.match(/Panel$/)) {
34900 if (typeof(cfg.region) == 'undefined') {
34901 Roo.log("Failed to add Panel, region was not set");
34905 var region = cfg.region;
34911 xitems = cfg.items;
34918 case 'Content': // ContentPanel (el, cfg)
34919 case 'Scroll': // ContentPanel (el, cfg)
34921 cfg.autoCreate = true;
34922 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34924 // var el = this.el.createChild();
34925 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34928 this.add(region, ret);
34932 case 'TreePanel': // our new panel!
34933 cfg.el = this.el.createChild();
34934 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34935 this.add(region, ret);
34940 // create a new Layout (which is a Border Layout...
34942 var clayout = cfg.layout;
34943 clayout.el = this.el.createChild();
34944 clayout.items = clayout.items || [];
34948 // replace this exitems with the clayout ones..
34949 xitems = clayout.items;
34951 // force background off if it's in center...
34952 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34953 cfg.background = false;
34955 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34958 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34959 //console.log('adding nested layout panel ' + cfg.toSource());
34960 this.add(region, ret);
34961 nb = {}; /// find first...
34966 // needs grid and region
34968 //var el = this.getRegion(region).el.createChild();
34970 *var el = this.el.createChild();
34971 // create the grid first...
34972 cfg.grid.container = el;
34973 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34976 if (region == 'center' && this.active ) {
34977 cfg.background = false;
34980 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34982 this.add(region, ret);
34984 if (cfg.background) {
34985 // render grid on panel activation (if panel background)
34986 ret.on('activate', function(gp) {
34987 if (!gp.grid.rendered) {
34988 // gp.grid.render(el);
34992 // cfg.grid.render(el);
34998 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34999 // it was the old xcomponent building that caused this before.
35000 // espeically if border is the top element in the tree.
35010 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35012 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35013 this.add(region, ret);
35017 throw "Can not add '" + cfg.xtype + "' to Border";
35023 this.beginUpdate();
35027 Roo.each(xitems, function(i) {
35028 region = nb && i.region ? i.region : false;
35030 var add = ret.addxtype(i);
35033 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35034 if (!i.background) {
35035 abn[region] = nb[region] ;
35042 // make the last non-background panel active..
35043 //if (nb) { Roo.log(abn); }
35046 for(var r in abn) {
35047 region = this.getRegion(r);
35049 // tried using nb[r], but it does not work..
35051 region.showPanel(abn[r]);
35062 factory : function(cfg)
35065 var validRegions = Roo.bootstrap.layout.Border.regions;
35067 var target = cfg.region;
35070 var r = Roo.bootstrap.layout;
35074 return new r.North(cfg);
35076 return new r.South(cfg);
35078 return new r.East(cfg);
35080 return new r.West(cfg);
35082 return new r.Center(cfg);
35084 throw 'Layout region "'+target+'" not supported.';
35091 * Ext JS Library 1.1.1
35092 * Copyright(c) 2006-2007, Ext JS, LLC.
35094 * Originally Released Under LGPL - original licence link has changed is not relivant.
35097 * <script type="text/javascript">
35101 * @class Roo.bootstrap.layout.Basic
35102 * @extends Roo.util.Observable
35103 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35104 * and does not have a titlebar, tabs or any other features. All it does is size and position
35105 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35106 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35107 * @cfg {string} region the region that it inhabits..
35108 * @cfg {bool} skipConfig skip config?
35112 Roo.bootstrap.layout.Basic = function(config){
35114 this.mgr = config.mgr;
35116 this.position = config.region;
35118 var skipConfig = config.skipConfig;
35122 * @scope Roo.BasicLayoutRegion
35126 * @event beforeremove
35127 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35128 * @param {Roo.LayoutRegion} this
35129 * @param {Roo.ContentPanel} panel The panel
35130 * @param {Object} e The cancel event object
35132 "beforeremove" : true,
35134 * @event invalidated
35135 * Fires when the layout for this region is changed.
35136 * @param {Roo.LayoutRegion} this
35138 "invalidated" : true,
35140 * @event visibilitychange
35141 * Fires when this region is shown or hidden
35142 * @param {Roo.LayoutRegion} this
35143 * @param {Boolean} visibility true or false
35145 "visibilitychange" : true,
35147 * @event paneladded
35148 * Fires when a panel is added.
35149 * @param {Roo.LayoutRegion} this
35150 * @param {Roo.ContentPanel} panel The panel
35152 "paneladded" : true,
35154 * @event panelremoved
35155 * Fires when a panel is removed.
35156 * @param {Roo.LayoutRegion} this
35157 * @param {Roo.ContentPanel} panel The panel
35159 "panelremoved" : true,
35161 * @event beforecollapse
35162 * Fires when this region before collapse.
35163 * @param {Roo.LayoutRegion} this
35165 "beforecollapse" : true,
35168 * Fires when this region is collapsed.
35169 * @param {Roo.LayoutRegion} this
35171 "collapsed" : true,
35174 * Fires when this region is expanded.
35175 * @param {Roo.LayoutRegion} this
35180 * Fires when this region is slid into view.
35181 * @param {Roo.LayoutRegion} this
35183 "slideshow" : true,
35186 * Fires when this region slides out of view.
35187 * @param {Roo.LayoutRegion} this
35189 "slidehide" : true,
35191 * @event panelactivated
35192 * Fires when a panel is activated.
35193 * @param {Roo.LayoutRegion} this
35194 * @param {Roo.ContentPanel} panel The activated panel
35196 "panelactivated" : true,
35199 * Fires when the user resizes this region.
35200 * @param {Roo.LayoutRegion} this
35201 * @param {Number} newSize The new size (width for east/west, height for north/south)
35205 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35206 this.panels = new Roo.util.MixedCollection();
35207 this.panels.getKey = this.getPanelId.createDelegate(this);
35209 this.activePanel = null;
35210 // ensure listeners are added...
35212 if (config.listeners || config.events) {
35213 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35214 listeners : config.listeners || {},
35215 events : config.events || {}
35219 if(skipConfig !== true){
35220 this.applyConfig(config);
35224 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35226 getPanelId : function(p){
35230 applyConfig : function(config){
35231 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35232 this.config = config;
35237 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35238 * the width, for horizontal (north, south) the height.
35239 * @param {Number} newSize The new width or height
35241 resizeTo : function(newSize){
35242 var el = this.el ? this.el :
35243 (this.activePanel ? this.activePanel.getEl() : null);
35245 switch(this.position){
35248 el.setWidth(newSize);
35249 this.fireEvent("resized", this, newSize);
35253 el.setHeight(newSize);
35254 this.fireEvent("resized", this, newSize);
35260 getBox : function(){
35261 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35264 getMargins : function(){
35265 return this.margins;
35268 updateBox : function(box){
35270 var el = this.activePanel.getEl();
35271 el.dom.style.left = box.x + "px";
35272 el.dom.style.top = box.y + "px";
35273 this.activePanel.setSize(box.width, box.height);
35277 * Returns the container element for this region.
35278 * @return {Roo.Element}
35280 getEl : function(){
35281 return this.activePanel;
35285 * Returns true if this region is currently visible.
35286 * @return {Boolean}
35288 isVisible : function(){
35289 return this.activePanel ? true : false;
35292 setActivePanel : function(panel){
35293 panel = this.getPanel(panel);
35294 if(this.activePanel && this.activePanel != panel){
35295 this.activePanel.setActiveState(false);
35296 this.activePanel.getEl().setLeftTop(-10000,-10000);
35298 this.activePanel = panel;
35299 panel.setActiveState(true);
35301 panel.setSize(this.box.width, this.box.height);
35303 this.fireEvent("panelactivated", this, panel);
35304 this.fireEvent("invalidated");
35308 * Show the specified panel.
35309 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35310 * @return {Roo.ContentPanel} The shown panel or null
35312 showPanel : function(panel){
35313 panel = this.getPanel(panel);
35315 this.setActivePanel(panel);
35321 * Get the active panel for this region.
35322 * @return {Roo.ContentPanel} The active panel or null
35324 getActivePanel : function(){
35325 return this.activePanel;
35329 * Add the passed ContentPanel(s)
35330 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35331 * @return {Roo.ContentPanel} The panel added (if only one was added)
35333 add : function(panel){
35334 if(arguments.length > 1){
35335 for(var i = 0, len = arguments.length; i < len; i++) {
35336 this.add(arguments[i]);
35340 if(this.hasPanel(panel)){
35341 this.showPanel(panel);
35344 var el = panel.getEl();
35345 if(el.dom.parentNode != this.mgr.el.dom){
35346 this.mgr.el.dom.appendChild(el.dom);
35348 if(panel.setRegion){
35349 panel.setRegion(this);
35351 this.panels.add(panel);
35352 el.setStyle("position", "absolute");
35353 if(!panel.background){
35354 this.setActivePanel(panel);
35355 if(this.config.initialSize && this.panels.getCount()==1){
35356 this.resizeTo(this.config.initialSize);
35359 this.fireEvent("paneladded", this, panel);
35364 * Returns true if the panel is in this region.
35365 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366 * @return {Boolean}
35368 hasPanel : function(panel){
35369 if(typeof panel == "object"){ // must be panel obj
35370 panel = panel.getId();
35372 return this.getPanel(panel) ? true : false;
35376 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35377 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35378 * @param {Boolean} preservePanel Overrides the config preservePanel option
35379 * @return {Roo.ContentPanel} The panel that was removed
35381 remove : function(panel, preservePanel){
35382 panel = this.getPanel(panel);
35387 this.fireEvent("beforeremove", this, panel, e);
35388 if(e.cancel === true){
35391 var panelId = panel.getId();
35392 this.panels.removeKey(panelId);
35397 * Returns the panel specified or null if it's not in this region.
35398 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35399 * @return {Roo.ContentPanel}
35401 getPanel : function(id){
35402 if(typeof id == "object"){ // must be panel obj
35405 return this.panels.get(id);
35409 * Returns this regions position (north/south/east/west/center).
35412 getPosition: function(){
35413 return this.position;
35417 * Ext JS Library 1.1.1
35418 * Copyright(c) 2006-2007, Ext JS, LLC.
35420 * Originally Released Under LGPL - original licence link has changed is not relivant.
35423 * <script type="text/javascript">
35427 * @class Roo.bootstrap.layout.Region
35428 * @extends Roo.bootstrap.layout.Basic
35429 * This class represents a region in a layout manager.
35431 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35432 * @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})
35433 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35434 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35435 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35436 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35437 * @cfg {String} title The title for the region (overrides panel titles)
35438 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35439 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35440 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35441 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35442 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35443 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35444 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35445 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35446 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35447 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35449 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35450 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35451 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35452 * @cfg {Number} width For East/West panels
35453 * @cfg {Number} height For North/South panels
35454 * @cfg {Boolean} split To show the splitter
35455 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35457 * @cfg {string} cls Extra CSS classes to add to region
35459 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35460 * @cfg {string} region the region that it inhabits..
35463 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35464 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35466 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35467 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35468 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35470 Roo.bootstrap.layout.Region = function(config)
35472 this.applyConfig(config);
35474 var mgr = config.mgr;
35475 var pos = config.region;
35476 config.skipConfig = true;
35477 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35480 this.onRender(mgr.el);
35483 this.visible = true;
35484 this.collapsed = false;
35485 this.unrendered_panels = [];
35488 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35490 position: '', // set by wrapper (eg. north/south etc..)
35491 unrendered_panels : null, // unrendered panels.
35492 createBody : function(){
35493 /** This region's body element
35494 * @type Roo.Element */
35495 this.bodyEl = this.el.createChild({
35497 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35501 onRender: function(ctr, pos)
35503 var dh = Roo.DomHelper;
35504 /** This region's container element
35505 * @type Roo.Element */
35506 this.el = dh.append(ctr.dom, {
35508 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35510 /** This region's title element
35511 * @type Roo.Element */
35513 this.titleEl = dh.append(this.el.dom,
35516 unselectable: "on",
35517 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35519 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35520 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35523 this.titleEl.enableDisplayMode();
35524 /** This region's title text element
35525 * @type HTMLElement */
35526 this.titleTextEl = this.titleEl.dom.firstChild;
35527 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35529 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35530 this.closeBtn.enableDisplayMode();
35531 this.closeBtn.on("click", this.closeClicked, this);
35532 this.closeBtn.hide();
35534 this.createBody(this.config);
35535 if(this.config.hideWhenEmpty){
35537 this.on("paneladded", this.validateVisibility, this);
35538 this.on("panelremoved", this.validateVisibility, this);
35540 if(this.autoScroll){
35541 this.bodyEl.setStyle("overflow", "auto");
35543 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35545 //if(c.titlebar !== false){
35546 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35547 this.titleEl.hide();
35549 this.titleEl.show();
35550 if(this.config.title){
35551 this.titleTextEl.innerHTML = this.config.title;
35555 if(this.config.collapsed){
35556 this.collapse(true);
35558 if(this.config.hidden){
35562 if (this.unrendered_panels && this.unrendered_panels.length) {
35563 for (var i =0;i< this.unrendered_panels.length; i++) {
35564 this.add(this.unrendered_panels[i]);
35566 this.unrendered_panels = null;
35572 applyConfig : function(c)
35575 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35576 var dh = Roo.DomHelper;
35577 if(c.titlebar !== false){
35578 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35579 this.collapseBtn.on("click", this.collapse, this);
35580 this.collapseBtn.enableDisplayMode();
35582 if(c.showPin === true || this.showPin){
35583 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35584 this.stickBtn.enableDisplayMode();
35585 this.stickBtn.on("click", this.expand, this);
35586 this.stickBtn.hide();
35591 /** This region's collapsed element
35592 * @type Roo.Element */
35595 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35596 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35599 if(c.floatable !== false){
35600 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35601 this.collapsedEl.on("click", this.collapseClick, this);
35604 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35605 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35606 id: "message", unselectable: "on", style:{"float":"left"}});
35607 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35609 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35610 this.expandBtn.on("click", this.expand, this);
35614 if(this.collapseBtn){
35615 this.collapseBtn.setVisible(c.collapsible == true);
35618 this.cmargins = c.cmargins || this.cmargins ||
35619 (this.position == "west" || this.position == "east" ?
35620 {top: 0, left: 2, right:2, bottom: 0} :
35621 {top: 2, left: 0, right:0, bottom: 2});
35623 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35626 this.bottomTabs = c.tabPosition != "top";
35628 this.autoScroll = c.autoScroll || false;
35633 this.duration = c.duration || .30;
35634 this.slideDuration = c.slideDuration || .45;
35639 * Returns true if this region is currently visible.
35640 * @return {Boolean}
35642 isVisible : function(){
35643 return this.visible;
35647 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35648 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35650 //setCollapsedTitle : function(title){
35651 // title = title || " ";
35652 // if(this.collapsedTitleTextEl){
35653 // this.collapsedTitleTextEl.innerHTML = title;
35657 getBox : function(){
35659 // if(!this.collapsed){
35660 b = this.el.getBox(false, true);
35662 // b = this.collapsedEl.getBox(false, true);
35667 getMargins : function(){
35668 return this.margins;
35669 //return this.collapsed ? this.cmargins : this.margins;
35672 highlight : function(){
35673 this.el.addClass("x-layout-panel-dragover");
35676 unhighlight : function(){
35677 this.el.removeClass("x-layout-panel-dragover");
35680 updateBox : function(box)
35682 if (!this.bodyEl) {
35683 return; // not rendered yet..
35687 if(!this.collapsed){
35688 this.el.dom.style.left = box.x + "px";
35689 this.el.dom.style.top = box.y + "px";
35690 this.updateBody(box.width, box.height);
35692 this.collapsedEl.dom.style.left = box.x + "px";
35693 this.collapsedEl.dom.style.top = box.y + "px";
35694 this.collapsedEl.setSize(box.width, box.height);
35697 this.tabs.autoSizeTabs();
35701 updateBody : function(w, h)
35704 this.el.setWidth(w);
35705 w -= this.el.getBorderWidth("rl");
35706 if(this.config.adjustments){
35707 w += this.config.adjustments[0];
35710 if(h !== null && h > 0){
35711 this.el.setHeight(h);
35712 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35713 h -= this.el.getBorderWidth("tb");
35714 if(this.config.adjustments){
35715 h += this.config.adjustments[1];
35717 this.bodyEl.setHeight(h);
35719 h = this.tabs.syncHeight(h);
35722 if(this.panelSize){
35723 w = w !== null ? w : this.panelSize.width;
35724 h = h !== null ? h : this.panelSize.height;
35726 if(this.activePanel){
35727 var el = this.activePanel.getEl();
35728 w = w !== null ? w : el.getWidth();
35729 h = h !== null ? h : el.getHeight();
35730 this.panelSize = {width: w, height: h};
35731 this.activePanel.setSize(w, h);
35733 if(Roo.isIE && this.tabs){
35734 this.tabs.el.repaint();
35739 * Returns the container element for this region.
35740 * @return {Roo.Element}
35742 getEl : function(){
35747 * Hides this region.
35750 //if(!this.collapsed){
35751 this.el.dom.style.left = "-2000px";
35754 // this.collapsedEl.dom.style.left = "-2000px";
35755 // this.collapsedEl.hide();
35757 this.visible = false;
35758 this.fireEvent("visibilitychange", this, false);
35762 * Shows this region if it was previously hidden.
35765 //if(!this.collapsed){
35768 // this.collapsedEl.show();
35770 this.visible = true;
35771 this.fireEvent("visibilitychange", this, true);
35774 closeClicked : function(){
35775 if(this.activePanel){
35776 this.remove(this.activePanel);
35780 collapseClick : function(e){
35782 e.stopPropagation();
35785 e.stopPropagation();
35791 * Collapses this region.
35792 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35795 collapse : function(skipAnim, skipCheck = false){
35796 if(this.collapsed) {
35800 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35802 this.collapsed = true;
35804 this.split.el.hide();
35806 if(this.config.animate && skipAnim !== true){
35807 this.fireEvent("invalidated", this);
35808 this.animateCollapse();
35810 this.el.setLocation(-20000,-20000);
35812 this.collapsedEl.show();
35813 this.fireEvent("collapsed", this);
35814 this.fireEvent("invalidated", this);
35820 animateCollapse : function(){
35825 * Expands this region if it was previously collapsed.
35826 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35827 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35830 expand : function(e, skipAnim){
35832 e.stopPropagation();
35834 if(!this.collapsed || this.el.hasActiveFx()) {
35838 this.afterSlideIn();
35841 this.collapsed = false;
35842 if(this.config.animate && skipAnim !== true){
35843 this.animateExpand();
35847 this.split.el.show();
35849 this.collapsedEl.setLocation(-2000,-2000);
35850 this.collapsedEl.hide();
35851 this.fireEvent("invalidated", this);
35852 this.fireEvent("expanded", this);
35856 animateExpand : function(){
35860 initTabs : function()
35862 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35864 var ts = new Roo.bootstrap.panel.Tabs({
35865 el: this.bodyEl.dom,
35866 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35867 disableTooltips: this.config.disableTabTips,
35868 toolbar : this.config.toolbar
35871 if(this.config.hideTabs){
35872 ts.stripWrap.setDisplayed(false);
35875 ts.resizeTabs = this.config.resizeTabs === true;
35876 ts.minTabWidth = this.config.minTabWidth || 40;
35877 ts.maxTabWidth = this.config.maxTabWidth || 250;
35878 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35879 ts.monitorResize = false;
35880 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35881 ts.bodyEl.addClass('roo-layout-tabs-body');
35882 this.panels.each(this.initPanelAsTab, this);
35885 initPanelAsTab : function(panel){
35886 var ti = this.tabs.addTab(
35890 this.config.closeOnTab && panel.isClosable(),
35893 if(panel.tabTip !== undefined){
35894 ti.setTooltip(panel.tabTip);
35896 ti.on("activate", function(){
35897 this.setActivePanel(panel);
35900 if(this.config.closeOnTab){
35901 ti.on("beforeclose", function(t, e){
35903 this.remove(panel);
35907 panel.tabItem = ti;
35912 updatePanelTitle : function(panel, title)
35914 if(this.activePanel == panel){
35915 this.updateTitle(title);
35918 var ti = this.tabs.getTab(panel.getEl().id);
35920 if(panel.tabTip !== undefined){
35921 ti.setTooltip(panel.tabTip);
35926 updateTitle : function(title){
35927 if(this.titleTextEl && !this.config.title){
35928 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35932 setActivePanel : function(panel)
35934 panel = this.getPanel(panel);
35935 if(this.activePanel && this.activePanel != panel){
35936 if(this.activePanel.setActiveState(false) === false){
35940 this.activePanel = panel;
35941 panel.setActiveState(true);
35942 if(this.panelSize){
35943 panel.setSize(this.panelSize.width, this.panelSize.height);
35946 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35948 this.updateTitle(panel.getTitle());
35950 this.fireEvent("invalidated", this);
35952 this.fireEvent("panelactivated", this, panel);
35956 * Shows the specified panel.
35957 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35958 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35960 showPanel : function(panel)
35962 panel = this.getPanel(panel);
35965 var tab = this.tabs.getTab(panel.getEl().id);
35966 if(tab.isHidden()){
35967 this.tabs.unhideTab(tab.id);
35971 this.setActivePanel(panel);
35978 * Get the active panel for this region.
35979 * @return {Roo.ContentPanel} The active panel or null
35981 getActivePanel : function(){
35982 return this.activePanel;
35985 validateVisibility : function(){
35986 if(this.panels.getCount() < 1){
35987 this.updateTitle(" ");
35988 this.closeBtn.hide();
35991 if(!this.isVisible()){
35998 * Adds the passed ContentPanel(s) to this region.
35999 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36000 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36002 add : function(panel)
36004 if(arguments.length > 1){
36005 for(var i = 0, len = arguments.length; i < len; i++) {
36006 this.add(arguments[i]);
36011 // if we have not been rendered yet, then we can not really do much of this..
36012 if (!this.bodyEl) {
36013 this.unrendered_panels.push(panel);
36020 if(this.hasPanel(panel)){
36021 this.showPanel(panel);
36024 panel.setRegion(this);
36025 this.panels.add(panel);
36026 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36027 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36028 // and hide them... ???
36029 this.bodyEl.dom.appendChild(panel.getEl().dom);
36030 if(panel.background !== true){
36031 this.setActivePanel(panel);
36033 this.fireEvent("paneladded", this, panel);
36040 this.initPanelAsTab(panel);
36044 if(panel.background !== true){
36045 this.tabs.activate(panel.getEl().id);
36047 this.fireEvent("paneladded", this, panel);
36052 * Hides the tab for the specified panel.
36053 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36055 hidePanel : function(panel){
36056 if(this.tabs && (panel = this.getPanel(panel))){
36057 this.tabs.hideTab(panel.getEl().id);
36062 * Unhides the tab for a previously hidden panel.
36063 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36065 unhidePanel : function(panel){
36066 if(this.tabs && (panel = this.getPanel(panel))){
36067 this.tabs.unhideTab(panel.getEl().id);
36071 clearPanels : function(){
36072 while(this.panels.getCount() > 0){
36073 this.remove(this.panels.first());
36078 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36079 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36080 * @param {Boolean} preservePanel Overrides the config preservePanel option
36081 * @return {Roo.ContentPanel} The panel that was removed
36083 remove : function(panel, preservePanel)
36085 panel = this.getPanel(panel);
36090 this.fireEvent("beforeremove", this, panel, e);
36091 if(e.cancel === true){
36094 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36095 var panelId = panel.getId();
36096 this.panels.removeKey(panelId);
36098 document.body.appendChild(panel.getEl().dom);
36101 this.tabs.removeTab(panel.getEl().id);
36102 }else if (!preservePanel){
36103 this.bodyEl.dom.removeChild(panel.getEl().dom);
36105 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36106 var p = this.panels.first();
36107 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36108 tempEl.appendChild(p.getEl().dom);
36109 this.bodyEl.update("");
36110 this.bodyEl.dom.appendChild(p.getEl().dom);
36112 this.updateTitle(p.getTitle());
36114 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36115 this.setActivePanel(p);
36117 panel.setRegion(null);
36118 if(this.activePanel == panel){
36119 this.activePanel = null;
36121 if(this.config.autoDestroy !== false && preservePanel !== true){
36122 try{panel.destroy();}catch(e){}
36124 this.fireEvent("panelremoved", this, panel);
36129 * Returns the TabPanel component used by this region
36130 * @return {Roo.TabPanel}
36132 getTabs : function(){
36136 createTool : function(parentEl, className){
36137 var btn = Roo.DomHelper.append(parentEl, {
36139 cls: "x-layout-tools-button",
36142 cls: "roo-layout-tools-button-inner " + className,
36146 btn.addClassOnOver("roo-layout-tools-button-over");
36151 * Ext JS Library 1.1.1
36152 * Copyright(c) 2006-2007, Ext JS, LLC.
36154 * Originally Released Under LGPL - original licence link has changed is not relivant.
36157 * <script type="text/javascript">
36163 * @class Roo.SplitLayoutRegion
36164 * @extends Roo.LayoutRegion
36165 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36167 Roo.bootstrap.layout.Split = function(config){
36168 this.cursor = config.cursor;
36169 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36172 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36174 splitTip : "Drag to resize.",
36175 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36176 useSplitTips : false,
36178 applyConfig : function(config){
36179 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36182 onRender : function(ctr,pos) {
36184 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36185 if(!this.config.split){
36190 var splitEl = Roo.DomHelper.append(ctr.dom, {
36192 id: this.el.id + "-split",
36193 cls: "roo-layout-split roo-layout-split-"+this.position,
36196 /** The SplitBar for this region
36197 * @type Roo.SplitBar */
36198 // does not exist yet...
36199 Roo.log([this.position, this.orientation]);
36201 this.split = new Roo.bootstrap.SplitBar({
36202 dragElement : splitEl,
36203 resizingElement: this.el,
36204 orientation : this.orientation
36207 this.split.on("moved", this.onSplitMove, this);
36208 this.split.useShim = this.config.useShim === true;
36209 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36210 if(this.useSplitTips){
36211 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36213 //if(config.collapsible){
36214 // this.split.el.on("dblclick", this.collapse, this);
36217 if(typeof this.config.minSize != "undefined"){
36218 this.split.minSize = this.config.minSize;
36220 if(typeof this.config.maxSize != "undefined"){
36221 this.split.maxSize = this.config.maxSize;
36223 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36224 this.hideSplitter();
36229 getHMaxSize : function(){
36230 var cmax = this.config.maxSize || 10000;
36231 var center = this.mgr.getRegion("center");
36232 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36235 getVMaxSize : function(){
36236 var cmax = this.config.maxSize || 10000;
36237 var center = this.mgr.getRegion("center");
36238 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36241 onSplitMove : function(split, newSize){
36242 this.fireEvent("resized", this, newSize);
36246 * Returns the {@link Roo.SplitBar} for this region.
36247 * @return {Roo.SplitBar}
36249 getSplitBar : function(){
36254 this.hideSplitter();
36255 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36258 hideSplitter : function(){
36260 this.split.el.setLocation(-2000,-2000);
36261 this.split.el.hide();
36267 this.split.el.show();
36269 Roo.bootstrap.layout.Split.superclass.show.call(this);
36272 beforeSlide: function(){
36273 if(Roo.isGecko){// firefox overflow auto bug workaround
36274 this.bodyEl.clip();
36276 this.tabs.bodyEl.clip();
36278 if(this.activePanel){
36279 this.activePanel.getEl().clip();
36281 if(this.activePanel.beforeSlide){
36282 this.activePanel.beforeSlide();
36288 afterSlide : function(){
36289 if(Roo.isGecko){// firefox overflow auto bug workaround
36290 this.bodyEl.unclip();
36292 this.tabs.bodyEl.unclip();
36294 if(this.activePanel){
36295 this.activePanel.getEl().unclip();
36296 if(this.activePanel.afterSlide){
36297 this.activePanel.afterSlide();
36303 initAutoHide : function(){
36304 if(this.autoHide !== false){
36305 if(!this.autoHideHd){
36306 var st = new Roo.util.DelayedTask(this.slideIn, this);
36307 this.autoHideHd = {
36308 "mouseout": function(e){
36309 if(!e.within(this.el, true)){
36313 "mouseover" : function(e){
36319 this.el.on(this.autoHideHd);
36323 clearAutoHide : function(){
36324 if(this.autoHide !== false){
36325 this.el.un("mouseout", this.autoHideHd.mouseout);
36326 this.el.un("mouseover", this.autoHideHd.mouseover);
36330 clearMonitor : function(){
36331 Roo.get(document).un("click", this.slideInIf, this);
36334 // these names are backwards but not changed for compat
36335 slideOut : function(){
36336 if(this.isSlid || this.el.hasActiveFx()){
36339 this.isSlid = true;
36340 if(this.collapseBtn){
36341 this.collapseBtn.hide();
36343 this.closeBtnState = this.closeBtn.getStyle('display');
36344 this.closeBtn.hide();
36346 this.stickBtn.show();
36349 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36350 this.beforeSlide();
36351 this.el.setStyle("z-index", 10001);
36352 this.el.slideIn(this.getSlideAnchor(), {
36353 callback: function(){
36355 this.initAutoHide();
36356 Roo.get(document).on("click", this.slideInIf, this);
36357 this.fireEvent("slideshow", this);
36364 afterSlideIn : function(){
36365 this.clearAutoHide();
36366 this.isSlid = false;
36367 this.clearMonitor();
36368 this.el.setStyle("z-index", "");
36369 if(this.collapseBtn){
36370 this.collapseBtn.show();
36372 this.closeBtn.setStyle('display', this.closeBtnState);
36374 this.stickBtn.hide();
36376 this.fireEvent("slidehide", this);
36379 slideIn : function(cb){
36380 if(!this.isSlid || this.el.hasActiveFx()){
36384 this.isSlid = false;
36385 this.beforeSlide();
36386 this.el.slideOut(this.getSlideAnchor(), {
36387 callback: function(){
36388 this.el.setLeftTop(-10000, -10000);
36390 this.afterSlideIn();
36398 slideInIf : function(e){
36399 if(!e.within(this.el)){
36404 animateCollapse : function(){
36405 this.beforeSlide();
36406 this.el.setStyle("z-index", 20000);
36407 var anchor = this.getSlideAnchor();
36408 this.el.slideOut(anchor, {
36409 callback : function(){
36410 this.el.setStyle("z-index", "");
36411 this.collapsedEl.slideIn(anchor, {duration:.3});
36413 this.el.setLocation(-10000,-10000);
36415 this.fireEvent("collapsed", this);
36422 animateExpand : function(){
36423 this.beforeSlide();
36424 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36425 this.el.setStyle("z-index", 20000);
36426 this.collapsedEl.hide({
36429 this.el.slideIn(this.getSlideAnchor(), {
36430 callback : function(){
36431 this.el.setStyle("z-index", "");
36434 this.split.el.show();
36436 this.fireEvent("invalidated", this);
36437 this.fireEvent("expanded", this);
36465 getAnchor : function(){
36466 return this.anchors[this.position];
36469 getCollapseAnchor : function(){
36470 return this.canchors[this.position];
36473 getSlideAnchor : function(){
36474 return this.sanchors[this.position];
36477 getAlignAdj : function(){
36478 var cm = this.cmargins;
36479 switch(this.position){
36495 getExpandAdj : function(){
36496 var c = this.collapsedEl, cm = this.cmargins;
36497 switch(this.position){
36499 return [-(cm.right+c.getWidth()+cm.left), 0];
36502 return [cm.right+c.getWidth()+cm.left, 0];
36505 return [0, -(cm.top+cm.bottom+c.getHeight())];
36508 return [0, cm.top+cm.bottom+c.getHeight()];
36514 * Ext JS Library 1.1.1
36515 * Copyright(c) 2006-2007, Ext JS, LLC.
36517 * Originally Released Under LGPL - original licence link has changed is not relivant.
36520 * <script type="text/javascript">
36523 * These classes are private internal classes
36525 Roo.bootstrap.layout.Center = function(config){
36526 config.region = "center";
36527 Roo.bootstrap.layout.Region.call(this, config);
36528 this.visible = true;
36529 this.minWidth = config.minWidth || 20;
36530 this.minHeight = config.minHeight || 20;
36533 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36535 // center panel can't be hidden
36539 // center panel can't be hidden
36542 getMinWidth: function(){
36543 return this.minWidth;
36546 getMinHeight: function(){
36547 return this.minHeight;
36560 Roo.bootstrap.layout.North = function(config)
36562 config.region = 'north';
36563 config.cursor = 'n-resize';
36565 Roo.bootstrap.layout.Split.call(this, config);
36569 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36570 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36571 this.split.el.addClass("roo-layout-split-v");
36573 var size = config.initialSize || config.height;
36574 if(typeof size != "undefined"){
36575 this.el.setHeight(size);
36578 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36580 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36584 getBox : function(){
36585 if(this.collapsed){
36586 return this.collapsedEl.getBox();
36588 var box = this.el.getBox();
36590 box.height += this.split.el.getHeight();
36595 updateBox : function(box){
36596 if(this.split && !this.collapsed){
36597 box.height -= this.split.el.getHeight();
36598 this.split.el.setLeft(box.x);
36599 this.split.el.setTop(box.y+box.height);
36600 this.split.el.setWidth(box.width);
36602 if(this.collapsed){
36603 this.updateBody(box.width, null);
36605 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36613 Roo.bootstrap.layout.South = function(config){
36614 config.region = 'south';
36615 config.cursor = 's-resize';
36616 Roo.bootstrap.layout.Split.call(this, config);
36618 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36619 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36620 this.split.el.addClass("roo-layout-split-v");
36622 var size = config.initialSize || config.height;
36623 if(typeof size != "undefined"){
36624 this.el.setHeight(size);
36628 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36629 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36630 getBox : function(){
36631 if(this.collapsed){
36632 return this.collapsedEl.getBox();
36634 var box = this.el.getBox();
36636 var sh = this.split.el.getHeight();
36643 updateBox : function(box){
36644 if(this.split && !this.collapsed){
36645 var sh = this.split.el.getHeight();
36648 this.split.el.setLeft(box.x);
36649 this.split.el.setTop(box.y-sh);
36650 this.split.el.setWidth(box.width);
36652 if(this.collapsed){
36653 this.updateBody(box.width, null);
36655 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36659 Roo.bootstrap.layout.East = function(config){
36660 config.region = "east";
36661 config.cursor = "e-resize";
36662 Roo.bootstrap.layout.Split.call(this, config);
36664 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36665 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36666 this.split.el.addClass("roo-layout-split-h");
36668 var size = config.initialSize || config.width;
36669 if(typeof size != "undefined"){
36670 this.el.setWidth(size);
36673 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36674 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36675 getBox : function(){
36676 if(this.collapsed){
36677 return this.collapsedEl.getBox();
36679 var box = this.el.getBox();
36681 var sw = this.split.el.getWidth();
36688 updateBox : function(box){
36689 if(this.split && !this.collapsed){
36690 var sw = this.split.el.getWidth();
36692 this.split.el.setLeft(box.x);
36693 this.split.el.setTop(box.y);
36694 this.split.el.setHeight(box.height);
36697 if(this.collapsed){
36698 this.updateBody(null, box.height);
36700 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36704 Roo.bootstrap.layout.West = function(config){
36705 config.region = "west";
36706 config.cursor = "w-resize";
36708 Roo.bootstrap.layout.Split.call(this, config);
36710 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36711 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36712 this.split.el.addClass("roo-layout-split-h");
36716 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36717 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36719 onRender: function(ctr, pos)
36721 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36722 var size = this.config.initialSize || this.config.width;
36723 if(typeof size != "undefined"){
36724 this.el.setWidth(size);
36728 getBox : function(){
36729 if(this.collapsed){
36730 return this.collapsedEl.getBox();
36732 var box = this.el.getBox();
36734 box.width += this.split.el.getWidth();
36739 updateBox : function(box){
36740 if(this.split && !this.collapsed){
36741 var sw = this.split.el.getWidth();
36743 this.split.el.setLeft(box.x+box.width);
36744 this.split.el.setTop(box.y);
36745 this.split.el.setHeight(box.height);
36747 if(this.collapsed){
36748 this.updateBody(null, box.height);
36750 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36753 Roo.namespace("Roo.bootstrap.panel");/*
36755 * Ext JS Library 1.1.1
36756 * Copyright(c) 2006-2007, Ext JS, LLC.
36758 * Originally Released Under LGPL - original licence link has changed is not relivant.
36761 * <script type="text/javascript">
36764 * @class Roo.ContentPanel
36765 * @extends Roo.util.Observable
36766 * A basic ContentPanel element.
36767 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36768 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36769 * @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
36770 * @cfg {Boolean} closable True if the panel can be closed/removed
36771 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36772 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36773 * @cfg {Toolbar} toolbar A toolbar for this panel
36774 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36775 * @cfg {String} title The title for this panel
36776 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36777 * @cfg {String} url Calls {@link #setUrl} with this value
36778 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36779 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36780 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36781 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36782 * @cfg {Boolean} badges render the badges
36785 * Create a new ContentPanel.
36786 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36787 * @param {String/Object} config A string to set only the title or a config object
36788 * @param {String} content (optional) Set the HTML content for this panel
36789 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36791 Roo.bootstrap.panel.Content = function( config){
36793 this.tpl = config.tpl || false;
36795 var el = config.el;
36796 var content = config.content;
36798 if(config.autoCreate){ // xtype is available if this is called from factory
36801 this.el = Roo.get(el);
36802 if(!this.el && config && config.autoCreate){
36803 if(typeof config.autoCreate == "object"){
36804 if(!config.autoCreate.id){
36805 config.autoCreate.id = config.id||el;
36807 this.el = Roo.DomHelper.append(document.body,
36808 config.autoCreate, true);
36810 var elcfg = { tag: "div",
36811 cls: "roo-layout-inactive-content",
36815 elcfg.html = config.html;
36819 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36822 this.closable = false;
36823 this.loaded = false;
36824 this.active = false;
36827 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36829 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36831 this.wrapEl = this.el; //this.el.wrap();
36833 if (config.toolbar.items) {
36834 ti = config.toolbar.items ;
36835 delete config.toolbar.items ;
36839 this.toolbar.render(this.wrapEl, 'before');
36840 for(var i =0;i < ti.length;i++) {
36841 // Roo.log(['add child', items[i]]);
36842 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36844 this.toolbar.items = nitems;
36845 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36846 delete config.toolbar;
36850 // xtype created footer. - not sure if will work as we normally have to render first..
36851 if (this.footer && !this.footer.el && this.footer.xtype) {
36852 if (!this.wrapEl) {
36853 this.wrapEl = this.el.wrap();
36856 this.footer.container = this.wrapEl.createChild();
36858 this.footer = Roo.factory(this.footer, Roo);
36863 if(typeof config == "string"){
36864 this.title = config;
36866 Roo.apply(this, config);
36870 this.resizeEl = Roo.get(this.resizeEl, true);
36872 this.resizeEl = this.el;
36874 // handle view.xtype
36882 * Fires when this panel is activated.
36883 * @param {Roo.ContentPanel} this
36887 * @event deactivate
36888 * Fires when this panel is activated.
36889 * @param {Roo.ContentPanel} this
36891 "deactivate" : true,
36895 * Fires when this panel is resized if fitToFrame is true.
36896 * @param {Roo.ContentPanel} this
36897 * @param {Number} width The width after any component adjustments
36898 * @param {Number} height The height after any component adjustments
36904 * Fires when this tab is created
36905 * @param {Roo.ContentPanel} this
36916 if(this.autoScroll){
36917 this.resizeEl.setStyle("overflow", "auto");
36919 // fix randome scrolling
36920 //this.el.on('scroll', function() {
36921 // Roo.log('fix random scolling');
36922 // this.scrollTo('top',0);
36925 content = content || this.content;
36927 this.setContent(content);
36929 if(config && config.url){
36930 this.setUrl(this.url, this.params, this.loadOnce);
36935 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36937 if (this.view && typeof(this.view.xtype) != 'undefined') {
36938 this.view.el = this.el.appendChild(document.createElement("div"));
36939 this.view = Roo.factory(this.view);
36940 this.view.render && this.view.render(false, '');
36944 this.fireEvent('render', this);
36947 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36951 setRegion : function(region){
36952 this.region = region;
36953 this.setActiveClass(region && !this.background);
36957 setActiveClass: function(state)
36960 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36961 this.el.setStyle('position','relative');
36963 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36964 this.el.setStyle('position', 'absolute');
36969 * Returns the toolbar for this Panel if one was configured.
36970 * @return {Roo.Toolbar}
36972 getToolbar : function(){
36973 return this.toolbar;
36976 setActiveState : function(active)
36978 this.active = active;
36979 this.setActiveClass(active);
36981 if(this.fireEvent("deactivate", this) === false){
36986 this.fireEvent("activate", this);
36990 * Updates this panel's element
36991 * @param {String} content The new content
36992 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36994 setContent : function(content, loadScripts){
36995 this.el.update(content, loadScripts);
36998 ignoreResize : function(w, h){
36999 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37002 this.lastSize = {width: w, height: h};
37007 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37008 * @return {Roo.UpdateManager} The UpdateManager
37010 getUpdateManager : function(){
37011 return this.el.getUpdateManager();
37014 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37015 * @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:
37018 url: "your-url.php",
37019 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37020 callback: yourFunction,
37021 scope: yourObject, //(optional scope)
37024 text: "Loading...",
37029 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37030 * 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.
37031 * @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}
37032 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37033 * @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.
37034 * @return {Roo.ContentPanel} this
37037 var um = this.el.getUpdateManager();
37038 um.update.apply(um, arguments);
37044 * 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.
37045 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37046 * @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)
37047 * @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)
37048 * @return {Roo.UpdateManager} The UpdateManager
37050 setUrl : function(url, params, loadOnce){
37051 if(this.refreshDelegate){
37052 this.removeListener("activate", this.refreshDelegate);
37054 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37055 this.on("activate", this.refreshDelegate);
37056 return this.el.getUpdateManager();
37059 _handleRefresh : function(url, params, loadOnce){
37060 if(!loadOnce || !this.loaded){
37061 var updater = this.el.getUpdateManager();
37062 updater.update(url, params, this._setLoaded.createDelegate(this));
37066 _setLoaded : function(){
37067 this.loaded = true;
37071 * Returns this panel's id
37074 getId : function(){
37079 * Returns this panel's element - used by regiosn to add.
37080 * @return {Roo.Element}
37082 getEl : function(){
37083 return this.wrapEl || this.el;
37088 adjustForComponents : function(width, height)
37090 //Roo.log('adjustForComponents ');
37091 if(this.resizeEl != this.el){
37092 width -= this.el.getFrameWidth('lr');
37093 height -= this.el.getFrameWidth('tb');
37096 var te = this.toolbar.getEl();
37097 te.setWidth(width);
37098 height -= te.getHeight();
37101 var te = this.footer.getEl();
37102 te.setWidth(width);
37103 height -= te.getHeight();
37107 if(this.adjustments){
37108 width += this.adjustments[0];
37109 height += this.adjustments[1];
37111 return {"width": width, "height": height};
37114 setSize : function(width, height){
37115 if(this.fitToFrame && !this.ignoreResize(width, height)){
37116 if(this.fitContainer && this.resizeEl != this.el){
37117 this.el.setSize(width, height);
37119 var size = this.adjustForComponents(width, height);
37120 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37121 this.fireEvent('resize', this, size.width, size.height);
37126 * Returns this panel's title
37129 getTitle : function(){
37131 if (typeof(this.title) != 'object') {
37136 for (var k in this.title) {
37137 if (!this.title.hasOwnProperty(k)) {
37141 if (k.indexOf('-') >= 0) {
37142 var s = k.split('-');
37143 for (var i = 0; i<s.length; i++) {
37144 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37147 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37154 * Set this panel's title
37155 * @param {String} title
37157 setTitle : function(title){
37158 this.title = title;
37160 this.region.updatePanelTitle(this, title);
37165 * Returns true is this panel was configured to be closable
37166 * @return {Boolean}
37168 isClosable : function(){
37169 return this.closable;
37172 beforeSlide : function(){
37174 this.resizeEl.clip();
37177 afterSlide : function(){
37179 this.resizeEl.unclip();
37183 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37184 * Will fail silently if the {@link #setUrl} method has not been called.
37185 * This does not activate the panel, just updates its content.
37187 refresh : function(){
37188 if(this.refreshDelegate){
37189 this.loaded = false;
37190 this.refreshDelegate();
37195 * Destroys this panel
37197 destroy : function(){
37198 this.el.removeAllListeners();
37199 var tempEl = document.createElement("span");
37200 tempEl.appendChild(this.el.dom);
37201 tempEl.innerHTML = "";
37207 * form - if the content panel contains a form - this is a reference to it.
37208 * @type {Roo.form.Form}
37212 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37213 * This contains a reference to it.
37219 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37229 * @param {Object} cfg Xtype definition of item to add.
37233 getChildContainer: function () {
37234 return this.getEl();
37239 var ret = new Roo.factory(cfg);
37244 if (cfg.xtype.match(/^Form$/)) {
37247 //if (this.footer) {
37248 // el = this.footer.container.insertSibling(false, 'before');
37250 el = this.el.createChild();
37253 this.form = new Roo.form.Form(cfg);
37256 if ( this.form.allItems.length) {
37257 this.form.render(el.dom);
37261 // should only have one of theses..
37262 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37263 // views.. should not be just added - used named prop 'view''
37265 cfg.el = this.el.appendChild(document.createElement("div"));
37268 var ret = new Roo.factory(cfg);
37270 ret.render && ret.render(false, ''); // render blank..
37280 * @class Roo.bootstrap.panel.Grid
37281 * @extends Roo.bootstrap.panel.Content
37283 * Create a new GridPanel.
37284 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37285 * @param {Object} config A the config object
37291 Roo.bootstrap.panel.Grid = function(config)
37295 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37296 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37298 config.el = this.wrapper;
37299 //this.el = this.wrapper;
37301 if (config.container) {
37302 // ctor'ed from a Border/panel.grid
37305 this.wrapper.setStyle("overflow", "hidden");
37306 this.wrapper.addClass('roo-grid-container');
37311 if(config.toolbar){
37312 var tool_el = this.wrapper.createChild();
37313 this.toolbar = Roo.factory(config.toolbar);
37315 if (config.toolbar.items) {
37316 ti = config.toolbar.items ;
37317 delete config.toolbar.items ;
37321 this.toolbar.render(tool_el);
37322 for(var i =0;i < ti.length;i++) {
37323 // Roo.log(['add child', items[i]]);
37324 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37326 this.toolbar.items = nitems;
37328 delete config.toolbar;
37331 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37332 config.grid.scrollBody = true;;
37333 config.grid.monitorWindowResize = false; // turn off autosizing
37334 config.grid.autoHeight = false;
37335 config.grid.autoWidth = false;
37337 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37339 if (config.background) {
37340 // render grid on panel activation (if panel background)
37341 this.on('activate', function(gp) {
37342 if (!gp.grid.rendered) {
37343 gp.grid.render(this.wrapper);
37344 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37349 this.grid.render(this.wrapper);
37350 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37353 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37354 // ??? needed ??? config.el = this.wrapper;
37359 // xtype created footer. - not sure if will work as we normally have to render first..
37360 if (this.footer && !this.footer.el && this.footer.xtype) {
37362 var ctr = this.grid.getView().getFooterPanel(true);
37363 this.footer.dataSource = this.grid.dataSource;
37364 this.footer = Roo.factory(this.footer, Roo);
37365 this.footer.render(ctr);
37375 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37376 getId : function(){
37377 return this.grid.id;
37381 * Returns the grid for this panel
37382 * @return {Roo.bootstrap.Table}
37384 getGrid : function(){
37388 setSize : function(width, height){
37389 if(!this.ignoreResize(width, height)){
37390 var grid = this.grid;
37391 var size = this.adjustForComponents(width, height);
37392 var gridel = grid.getGridEl();
37393 gridel.setSize(size.width, size.height);
37395 var thd = grid.getGridEl().select('thead',true).first();
37396 var tbd = grid.getGridEl().select('tbody', true).first();
37398 tbd.setSize(width, height - thd.getHeight());
37407 beforeSlide : function(){
37408 this.grid.getView().scroller.clip();
37411 afterSlide : function(){
37412 this.grid.getView().scroller.unclip();
37415 destroy : function(){
37416 this.grid.destroy();
37418 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37423 * @class Roo.bootstrap.panel.Nest
37424 * @extends Roo.bootstrap.panel.Content
37426 * Create a new Panel, that can contain a layout.Border.
37429 * @param {Roo.BorderLayout} layout The layout for this panel
37430 * @param {String/Object} config A string to set only the title or a config object
37432 Roo.bootstrap.panel.Nest = function(config)
37434 // construct with only one argument..
37435 /* FIXME - implement nicer consturctors
37436 if (layout.layout) {
37438 layout = config.layout;
37439 delete config.layout;
37441 if (layout.xtype && !layout.getEl) {
37442 // then layout needs constructing..
37443 layout = Roo.factory(layout, Roo);
37447 config.el = config.layout.getEl();
37449 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37451 config.layout.monitorWindowResize = false; // turn off autosizing
37452 this.layout = config.layout;
37453 this.layout.getEl().addClass("roo-layout-nested-layout");
37460 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37462 setSize : function(width, height){
37463 if(!this.ignoreResize(width, height)){
37464 var size = this.adjustForComponents(width, height);
37465 var el = this.layout.getEl();
37466 if (size.height < 1) {
37467 el.setWidth(size.width);
37469 el.setSize(size.width, size.height);
37471 var touch = el.dom.offsetWidth;
37472 this.layout.layout();
37473 // ie requires a double layout on the first pass
37474 if(Roo.isIE && !this.initialized){
37475 this.initialized = true;
37476 this.layout.layout();
37481 // activate all subpanels if not currently active..
37483 setActiveState : function(active){
37484 this.active = active;
37485 this.setActiveClass(active);
37488 this.fireEvent("deactivate", this);
37492 this.fireEvent("activate", this);
37493 // not sure if this should happen before or after..
37494 if (!this.layout) {
37495 return; // should not happen..
37498 for (var r in this.layout.regions) {
37499 reg = this.layout.getRegion(r);
37500 if (reg.getActivePanel()) {
37501 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37502 reg.setActivePanel(reg.getActivePanel());
37505 if (!reg.panels.length) {
37508 reg.showPanel(reg.getPanel(0));
37517 * Returns the nested BorderLayout for this panel
37518 * @return {Roo.BorderLayout}
37520 getLayout : function(){
37521 return this.layout;
37525 * Adds a xtype elements to the layout of the nested panel
37529 xtype : 'ContentPanel',
37536 xtype : 'NestedLayoutPanel',
37542 items : [ ... list of content panels or nested layout panels.. ]
37546 * @param {Object} cfg Xtype definition of item to add.
37548 addxtype : function(cfg) {
37549 return this.layout.addxtype(cfg);
37554 * Ext JS Library 1.1.1
37555 * Copyright(c) 2006-2007, Ext JS, LLC.
37557 * Originally Released Under LGPL - original licence link has changed is not relivant.
37560 * <script type="text/javascript">
37563 * @class Roo.TabPanel
37564 * @extends Roo.util.Observable
37565 * A lightweight tab container.
37569 // basic tabs 1, built from existing content
37570 var tabs = new Roo.TabPanel("tabs1");
37571 tabs.addTab("script", "View Script");
37572 tabs.addTab("markup", "View Markup");
37573 tabs.activate("script");
37575 // more advanced tabs, built from javascript
37576 var jtabs = new Roo.TabPanel("jtabs");
37577 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37579 // set up the UpdateManager
37580 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37581 var updater = tab2.getUpdateManager();
37582 updater.setDefaultUrl("ajax1.htm");
37583 tab2.on('activate', updater.refresh, updater, true);
37585 // Use setUrl for Ajax loading
37586 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37587 tab3.setUrl("ajax2.htm", null, true);
37590 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37593 jtabs.activate("jtabs-1");
37596 * Create a new TabPanel.
37597 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37598 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37600 Roo.bootstrap.panel.Tabs = function(config){
37602 * The container element for this TabPanel.
37603 * @type Roo.Element
37605 this.el = Roo.get(config.el);
37608 if(typeof config == "boolean"){
37609 this.tabPosition = config ? "bottom" : "top";
37611 Roo.apply(this, config);
37615 if(this.tabPosition == "bottom"){
37616 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37617 this.el.addClass("roo-tabs-bottom");
37619 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37620 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37621 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37623 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37625 if(this.tabPosition != "bottom"){
37626 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37627 * @type Roo.Element
37629 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37630 this.el.addClass("roo-tabs-top");
37634 this.bodyEl.setStyle("position", "relative");
37636 this.active = null;
37637 this.activateDelegate = this.activate.createDelegate(this);
37642 * Fires when the active tab changes
37643 * @param {Roo.TabPanel} this
37644 * @param {Roo.TabPanelItem} activePanel The new active tab
37648 * @event beforetabchange
37649 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37650 * @param {Roo.TabPanel} this
37651 * @param {Object} e Set cancel to true on this object to cancel the tab change
37652 * @param {Roo.TabPanelItem} tab The tab being changed to
37654 "beforetabchange" : true
37657 Roo.EventManager.onWindowResize(this.onResize, this);
37658 this.cpad = this.el.getPadding("lr");
37659 this.hiddenCount = 0;
37662 // toolbar on the tabbar support...
37663 if (this.toolbar) {
37664 alert("no toolbar support yet");
37665 this.toolbar = false;
37667 var tcfg = this.toolbar;
37668 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37669 this.toolbar = new Roo.Toolbar(tcfg);
37670 if (Roo.isSafari) {
37671 var tbl = tcfg.container.child('table', true);
37672 tbl.setAttribute('width', '100%');
37680 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37683 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37685 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37687 tabPosition : "top",
37689 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37691 currentTabWidth : 0,
37693 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37697 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37701 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37703 preferredTabWidth : 175,
37705 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37707 resizeTabs : false,
37709 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37711 monitorResize : true,
37713 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37718 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37719 * @param {String} id The id of the div to use <b>or create</b>
37720 * @param {String} text The text for the tab
37721 * @param {String} content (optional) Content to put in the TabPanelItem body
37722 * @param {Boolean} closable (optional) True to create a close icon on the tab
37723 * @return {Roo.TabPanelItem} The created TabPanelItem
37725 addTab : function(id, text, content, closable, tpl)
37727 var item = new Roo.bootstrap.panel.TabItem({
37731 closable : closable,
37734 this.addTabItem(item);
37736 item.setContent(content);
37742 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37743 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37744 * @return {Roo.TabPanelItem}
37746 getTab : function(id){
37747 return this.items[id];
37751 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37752 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37754 hideTab : function(id){
37755 var t = this.items[id];
37758 this.hiddenCount++;
37759 this.autoSizeTabs();
37764 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37765 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37767 unhideTab : function(id){
37768 var t = this.items[id];
37770 t.setHidden(false);
37771 this.hiddenCount--;
37772 this.autoSizeTabs();
37777 * Adds an existing {@link Roo.TabPanelItem}.
37778 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37780 addTabItem : function(item){
37781 this.items[item.id] = item;
37782 this.items.push(item);
37783 // if(this.resizeTabs){
37784 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37785 // this.autoSizeTabs();
37787 // item.autoSize();
37792 * Removes a {@link Roo.TabPanelItem}.
37793 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37795 removeTab : function(id){
37796 var items = this.items;
37797 var tab = items[id];
37798 if(!tab) { return; }
37799 var index = items.indexOf(tab);
37800 if(this.active == tab && items.length > 1){
37801 var newTab = this.getNextAvailable(index);
37806 this.stripEl.dom.removeChild(tab.pnode.dom);
37807 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37808 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37810 items.splice(index, 1);
37811 delete this.items[tab.id];
37812 tab.fireEvent("close", tab);
37813 tab.purgeListeners();
37814 this.autoSizeTabs();
37817 getNextAvailable : function(start){
37818 var items = this.items;
37820 // look for a next tab that will slide over to
37821 // replace the one being removed
37822 while(index < items.length){
37823 var item = items[++index];
37824 if(item && !item.isHidden()){
37828 // if one isn't found select the previous tab (on the left)
37831 var item = items[--index];
37832 if(item && !item.isHidden()){
37840 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37841 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37843 disableTab : function(id){
37844 var tab = this.items[id];
37845 if(tab && this.active != tab){
37851 * Enables a {@link Roo.TabPanelItem} that is disabled.
37852 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37854 enableTab : function(id){
37855 var tab = this.items[id];
37860 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37861 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37862 * @return {Roo.TabPanelItem} The TabPanelItem.
37864 activate : function(id){
37865 var tab = this.items[id];
37869 if(tab == this.active || tab.disabled){
37873 this.fireEvent("beforetabchange", this, e, tab);
37874 if(e.cancel !== true && !tab.disabled){
37876 this.active.hide();
37878 this.active = this.items[id];
37879 this.active.show();
37880 this.fireEvent("tabchange", this, this.active);
37886 * Gets the active {@link Roo.TabPanelItem}.
37887 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37889 getActiveTab : function(){
37890 return this.active;
37894 * Updates the tab body element to fit the height of the container element
37895 * for overflow scrolling
37896 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37898 syncHeight : function(targetHeight){
37899 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37900 var bm = this.bodyEl.getMargins();
37901 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37902 this.bodyEl.setHeight(newHeight);
37906 onResize : function(){
37907 if(this.monitorResize){
37908 this.autoSizeTabs();
37913 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37915 beginUpdate : function(){
37916 this.updating = true;
37920 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37922 endUpdate : function(){
37923 this.updating = false;
37924 this.autoSizeTabs();
37928 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37930 autoSizeTabs : function(){
37931 var count = this.items.length;
37932 var vcount = count - this.hiddenCount;
37933 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37936 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37937 var availWidth = Math.floor(w / vcount);
37938 var b = this.stripBody;
37939 if(b.getWidth() > w){
37940 var tabs = this.items;
37941 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37942 if(availWidth < this.minTabWidth){
37943 /*if(!this.sleft){ // incomplete scrolling code
37944 this.createScrollButtons();
37947 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37950 if(this.currentTabWidth < this.preferredTabWidth){
37951 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37957 * Returns the number of tabs in this TabPanel.
37960 getCount : function(){
37961 return this.items.length;
37965 * Resizes all the tabs to the passed width
37966 * @param {Number} The new width
37968 setTabWidth : function(width){
37969 this.currentTabWidth = width;
37970 for(var i = 0, len = this.items.length; i < len; i++) {
37971 if(!this.items[i].isHidden()) {
37972 this.items[i].setWidth(width);
37978 * Destroys this TabPanel
37979 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37981 destroy : function(removeEl){
37982 Roo.EventManager.removeResizeListener(this.onResize, this);
37983 for(var i = 0, len = this.items.length; i < len; i++){
37984 this.items[i].purgeListeners();
37986 if(removeEl === true){
37987 this.el.update("");
37992 createStrip : function(container)
37994 var strip = document.createElement("nav");
37995 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37996 container.appendChild(strip);
38000 createStripList : function(strip)
38002 // div wrapper for retard IE
38003 // returns the "tr" element.
38004 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38005 //'<div class="x-tabs-strip-wrap">'+
38006 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38007 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38008 return strip.firstChild; //.firstChild.firstChild.firstChild;
38010 createBody : function(container)
38012 var body = document.createElement("div");
38013 Roo.id(body, "tab-body");
38014 //Roo.fly(body).addClass("x-tabs-body");
38015 Roo.fly(body).addClass("tab-content");
38016 container.appendChild(body);
38019 createItemBody :function(bodyEl, id){
38020 var body = Roo.getDom(id);
38022 body = document.createElement("div");
38025 //Roo.fly(body).addClass("x-tabs-item-body");
38026 Roo.fly(body).addClass("tab-pane");
38027 bodyEl.insertBefore(body, bodyEl.firstChild);
38031 createStripElements : function(stripEl, text, closable, tpl)
38033 var td = document.createElement("li"); // was td..
38036 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38039 stripEl.appendChild(td);
38041 td.className = "x-tabs-closable";
38042 if(!this.closeTpl){
38043 this.closeTpl = new Roo.Template(
38044 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38045 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38046 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38049 var el = this.closeTpl.overwrite(td, {"text": text});
38050 var close = el.getElementsByTagName("div")[0];
38051 var inner = el.getElementsByTagName("em")[0];
38052 return {"el": el, "close": close, "inner": inner};
38055 // not sure what this is..
38056 // if(!this.tabTpl){
38057 //this.tabTpl = new Roo.Template(
38058 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38059 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38061 // this.tabTpl = new Roo.Template(
38062 // '<a href="#">' +
38063 // '<span unselectable="on"' +
38064 // (this.disableTooltips ? '' : ' title="{text}"') +
38065 // ' >{text}</span></a>'
38071 var template = tpl || this.tabTpl || false;
38075 template = new Roo.Template(
38077 '<span unselectable="on"' +
38078 (this.disableTooltips ? '' : ' title="{text}"') +
38079 ' >{text}</span></a>'
38083 switch (typeof(template)) {
38087 template = new Roo.Template(template);
38093 var el = template.overwrite(td, {"text": text});
38095 var inner = el.getElementsByTagName("span")[0];
38097 return {"el": el, "inner": inner};
38105 * @class Roo.TabPanelItem
38106 * @extends Roo.util.Observable
38107 * Represents an individual item (tab plus body) in a TabPanel.
38108 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38109 * @param {String} id The id of this TabPanelItem
38110 * @param {String} text The text for the tab of this TabPanelItem
38111 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38113 Roo.bootstrap.panel.TabItem = function(config){
38115 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38116 * @type Roo.TabPanel
38118 this.tabPanel = config.panel;
38120 * The id for this TabPanelItem
38123 this.id = config.id;
38125 this.disabled = false;
38127 this.text = config.text;
38129 this.loaded = false;
38130 this.closable = config.closable;
38133 * The body element for this TabPanelItem.
38134 * @type Roo.Element
38136 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38137 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38138 this.bodyEl.setStyle("display", "block");
38139 this.bodyEl.setStyle("zoom", "1");
38140 //this.hideAction();
38142 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38144 this.el = Roo.get(els.el);
38145 this.inner = Roo.get(els.inner, true);
38146 this.textEl = Roo.get(this.el.dom.firstChild, true);
38147 this.pnode = Roo.get(els.el.parentNode, true);
38148 // this.el.on("mousedown", this.onTabMouseDown, this);
38149 this.el.on("click", this.onTabClick, this);
38151 if(config.closable){
38152 var c = Roo.get(els.close, true);
38153 c.dom.title = this.closeText;
38154 c.addClassOnOver("close-over");
38155 c.on("click", this.closeClick, this);
38161 * Fires when this tab becomes the active tab.
38162 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38163 * @param {Roo.TabPanelItem} this
38167 * @event beforeclose
38168 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38169 * @param {Roo.TabPanelItem} this
38170 * @param {Object} e Set cancel to true on this object to cancel the close.
38172 "beforeclose": true,
38175 * Fires when this tab is closed.
38176 * @param {Roo.TabPanelItem} this
38180 * @event deactivate
38181 * Fires when this tab is no longer the active tab.
38182 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38183 * @param {Roo.TabPanelItem} this
38185 "deactivate" : true
38187 this.hidden = false;
38189 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38192 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38194 purgeListeners : function(){
38195 Roo.util.Observable.prototype.purgeListeners.call(this);
38196 this.el.removeAllListeners();
38199 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38202 this.pnode.addClass("active");
38205 this.tabPanel.stripWrap.repaint();
38207 this.fireEvent("activate", this.tabPanel, this);
38211 * Returns true if this tab is the active tab.
38212 * @return {Boolean}
38214 isActive : function(){
38215 return this.tabPanel.getActiveTab() == this;
38219 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38222 this.pnode.removeClass("active");
38224 this.fireEvent("deactivate", this.tabPanel, this);
38227 hideAction : function(){
38228 this.bodyEl.hide();
38229 this.bodyEl.setStyle("position", "absolute");
38230 this.bodyEl.setLeft("-20000px");
38231 this.bodyEl.setTop("-20000px");
38234 showAction : function(){
38235 this.bodyEl.setStyle("position", "relative");
38236 this.bodyEl.setTop("");
38237 this.bodyEl.setLeft("");
38238 this.bodyEl.show();
38242 * Set the tooltip for the tab.
38243 * @param {String} tooltip The tab's tooltip
38245 setTooltip : function(text){
38246 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38247 this.textEl.dom.qtip = text;
38248 this.textEl.dom.removeAttribute('title');
38250 this.textEl.dom.title = text;
38254 onTabClick : function(e){
38255 e.preventDefault();
38256 this.tabPanel.activate(this.id);
38259 onTabMouseDown : function(e){
38260 e.preventDefault();
38261 this.tabPanel.activate(this.id);
38264 getWidth : function(){
38265 return this.inner.getWidth();
38268 setWidth : function(width){
38269 var iwidth = width - this.pnode.getPadding("lr");
38270 this.inner.setWidth(iwidth);
38271 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38272 this.pnode.setWidth(width);
38276 * Show or hide the tab
38277 * @param {Boolean} hidden True to hide or false to show.
38279 setHidden : function(hidden){
38280 this.hidden = hidden;
38281 this.pnode.setStyle("display", hidden ? "none" : "");
38285 * Returns true if this tab is "hidden"
38286 * @return {Boolean}
38288 isHidden : function(){
38289 return this.hidden;
38293 * Returns the text for this tab
38296 getText : function(){
38300 autoSize : function(){
38301 //this.el.beginMeasure();
38302 this.textEl.setWidth(1);
38304 * #2804 [new] Tabs in Roojs
38305 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38307 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38308 //this.el.endMeasure();
38312 * Sets the text for the tab (Note: this also sets the tooltip text)
38313 * @param {String} text The tab's text and tooltip
38315 setText : function(text){
38317 this.textEl.update(text);
38318 this.setTooltip(text);
38319 //if(!this.tabPanel.resizeTabs){
38320 // this.autoSize();
38324 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38326 activate : function(){
38327 this.tabPanel.activate(this.id);
38331 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38333 disable : function(){
38334 if(this.tabPanel.active != this){
38335 this.disabled = true;
38336 this.pnode.addClass("disabled");
38341 * Enables this TabPanelItem if it was previously disabled.
38343 enable : function(){
38344 this.disabled = false;
38345 this.pnode.removeClass("disabled");
38349 * Sets the content for this TabPanelItem.
38350 * @param {String} content The content
38351 * @param {Boolean} loadScripts true to look for and load scripts
38353 setContent : function(content, loadScripts){
38354 this.bodyEl.update(content, loadScripts);
38358 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38359 * @return {Roo.UpdateManager} The UpdateManager
38361 getUpdateManager : function(){
38362 return this.bodyEl.getUpdateManager();
38366 * Set a URL to be used to load the content for this TabPanelItem.
38367 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38368 * @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)
38369 * @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)
38370 * @return {Roo.UpdateManager} The UpdateManager
38372 setUrl : function(url, params, loadOnce){
38373 if(this.refreshDelegate){
38374 this.un('activate', this.refreshDelegate);
38376 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38377 this.on("activate", this.refreshDelegate);
38378 return this.bodyEl.getUpdateManager();
38382 _handleRefresh : function(url, params, loadOnce){
38383 if(!loadOnce || !this.loaded){
38384 var updater = this.bodyEl.getUpdateManager();
38385 updater.update(url, params, this._setLoaded.createDelegate(this));
38390 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38391 * Will fail silently if the setUrl method has not been called.
38392 * This does not activate the panel, just updates its content.
38394 refresh : function(){
38395 if(this.refreshDelegate){
38396 this.loaded = false;
38397 this.refreshDelegate();
38402 _setLoaded : function(){
38403 this.loaded = true;
38407 closeClick : function(e){
38410 this.fireEvent("beforeclose", this, o);
38411 if(o.cancel !== true){
38412 this.tabPanel.removeTab(this.id);
38416 * The text displayed in the tooltip for the close icon.
38419 closeText : "Close this tab"
38422 * This script refer to:
38423 * Title: International Telephone Input
38424 * Author: Jack O'Connor
38425 * Code version: v12.1.12
38426 * Availability: https://github.com/jackocnr/intl-tel-input.git
38429 Roo.bootstrap.PhoneInputData = function() {
38432 "Afghanistan (افغانستان)",
38437 "Albania (Shqipëri)",
38442 "Algeria (الجزائر)",
38467 "Antigua and Barbuda",
38477 "Armenia (Հայաստան)",
38493 "Austria (Österreich)",
38498 "Azerbaijan (Azərbaycan)",
38508 "Bahrain (البحرين)",
38513 "Bangladesh (বাংলাদেশ)",
38523 "Belarus (Беларусь)",
38528 "Belgium (België)",
38558 "Bosnia and Herzegovina (Босна и Херцеговина)",
38573 "British Indian Ocean Territory",
38578 "British Virgin Islands",
38588 "Bulgaria (България)",
38598 "Burundi (Uburundi)",
38603 "Cambodia (កម្ពុជា)",
38608 "Cameroon (Cameroun)",
38617 ["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"]
38620 "Cape Verde (Kabu Verdi)",
38625 "Caribbean Netherlands",
38636 "Central African Republic (République centrafricaine)",
38656 "Christmas Island",
38662 "Cocos (Keeling) Islands",
38673 "Comoros (جزر القمر)",
38678 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38683 "Congo (Republic) (Congo-Brazzaville)",
38703 "Croatia (Hrvatska)",
38724 "Czech Republic (Česká republika)",
38729 "Denmark (Danmark)",
38744 "Dominican Republic (República Dominicana)",
38748 ["809", "829", "849"]
38766 "Equatorial Guinea (Guinea Ecuatorial)",
38786 "Falkland Islands (Islas Malvinas)",
38791 "Faroe Islands (Føroyar)",
38812 "French Guiana (Guyane française)",
38817 "French Polynesia (Polynésie française)",
38832 "Georgia (საქართველო)",
38837 "Germany (Deutschland)",
38857 "Greenland (Kalaallit Nunaat)",
38894 "Guinea-Bissau (Guiné Bissau)",
38919 "Hungary (Magyarország)",
38924 "Iceland (Ísland)",
38944 "Iraq (العراق)",
38960 "Israel (ישראל)",
38987 "Jordan (الأردن)",
38992 "Kazakhstan (Казахстан)",
39013 "Kuwait (الكويت)",
39018 "Kyrgyzstan (Кыргызстан)",
39028 "Latvia (Latvija)",
39033 "Lebanon (لبنان)",
39048 "Libya (ليبيا)",
39058 "Lithuania (Lietuva)",
39073 "Macedonia (FYROM) (Македонија)",
39078 "Madagascar (Madagasikara)",
39108 "Marshall Islands",
39118 "Mauritania (موريتانيا)",
39123 "Mauritius (Moris)",
39144 "Moldova (Republica Moldova)",
39154 "Mongolia (Монгол)",
39159 "Montenegro (Crna Gora)",
39169 "Morocco (المغرب)",
39175 "Mozambique (Moçambique)",
39180 "Myanmar (Burma) (မြန်မာ)",
39185 "Namibia (Namibië)",
39200 "Netherlands (Nederland)",
39205 "New Caledonia (Nouvelle-Calédonie)",
39240 "North Korea (조선 민주주의 인민 공화국)",
39245 "Northern Mariana Islands",
39261 "Pakistan (پاکستان)",
39271 "Palestine (فلسطين)",
39281 "Papua New Guinea",
39323 "Réunion (La Réunion)",
39329 "Romania (România)",
39345 "Saint Barthélemy",
39356 "Saint Kitts and Nevis",
39366 "Saint Martin (Saint-Martin (partie française))",
39372 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39377 "Saint Vincent and the Grenadines",
39392 "São Tomé and Príncipe (São Tomé e Príncipe)",
39397 "Saudi Arabia (المملكة العربية السعودية)",
39402 "Senegal (Sénégal)",
39432 "Slovakia (Slovensko)",
39437 "Slovenia (Slovenija)",
39447 "Somalia (Soomaaliya)",
39457 "South Korea (대한민국)",
39462 "South Sudan (جنوب السودان)",
39472 "Sri Lanka (ශ්රී ලංකාව)",
39477 "Sudan (السودان)",
39487 "Svalbard and Jan Mayen",
39498 "Sweden (Sverige)",
39503 "Switzerland (Schweiz)",
39508 "Syria (سوريا)",
39553 "Trinidad and Tobago",
39558 "Tunisia (تونس)",
39563 "Turkey (Türkiye)",
39573 "Turks and Caicos Islands",
39583 "U.S. Virgin Islands",
39593 "Ukraine (Україна)",
39598 "United Arab Emirates (الإمارات العربية المتحدة)",
39620 "Uzbekistan (Oʻzbekiston)",
39630 "Vatican City (Città del Vaticano)",
39641 "Vietnam (Việt Nam)",
39646 "Wallis and Futuna (Wallis-et-Futuna)",
39651 "Western Sahara (الصحراء الغربية)",
39657 "Yemen (اليمن)",
39681 * This script refer to:
39682 * Title: International Telephone Input
39683 * Author: Jack O'Connor
39684 * Code version: v12.1.12
39685 * Availability: https://github.com/jackocnr/intl-tel-input.git
39689 * @class Roo.bootstrap.PhoneInput
39690 * @extends Roo.bootstrap.TriggerField
39691 * An input with International dial-code selection
39693 * @cfg {String} defaultDialCode default '+852'
39694 * @cfg {Array} preferedCountries default []
39697 * Create a new PhoneInput.
39698 * @param {Object} config Configuration options
39701 Roo.bootstrap.PhoneInput = function(config) {
39702 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39705 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39707 listWidth: undefined,
39709 selectedClass: 'active',
39711 invalidClass : "has-warning",
39713 validClass: 'has-success',
39715 allowed: '0123456789',
39718 * @cfg {String} defaultDialCode The default dial code when initializing the input
39720 defaultDialCode: '+852',
39723 * @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
39725 preferedCountries: false,
39727 getAutoCreate : function()
39729 var data = Roo.bootstrap.PhoneInputData();
39730 var align = this.labelAlign || this.parentLabelAlign();
39733 this.allCountries = [];
39734 this.dialCodeMapping = [];
39736 for (var i = 0; i < data.length; i++) {
39738 this.allCountries[i] = {
39742 priority: c[3] || 0,
39743 areaCodes: c[4] || null
39745 this.dialCodeMapping[c[2]] = {
39748 priority: c[3] || 0,
39749 areaCodes: c[4] || null
39761 cls : 'form-control tel-input',
39762 autocomplete: 'new-password'
39765 var hiddenInput = {
39768 cls: 'hidden-tel-input'
39772 hiddenInput.name = this.name;
39775 if (this.disabled) {
39776 input.disabled = true;
39779 var flag_container = {
39796 cls: this.hasFeedback ? 'has-feedback' : '',
39802 cls: 'dial-code-holder',
39809 cls: 'roo-select2-container input-group',
39816 if (this.fieldLabel.length) {
39819 tooltip: 'This field is required'
39825 cls: 'control-label',
39831 html: this.fieldLabel
39834 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39840 if(this.indicatorpos == 'right') {
39841 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39848 if(align == 'left') {
39856 if(this.labelWidth > 12){
39857 label.style = "width: " + this.labelWidth + 'px';
39859 if(this.labelWidth < 13 && this.labelmd == 0){
39860 this.labelmd = this.labelWidth;
39862 if(this.labellg > 0){
39863 label.cls += ' col-lg-' + this.labellg;
39864 input.cls += ' col-lg-' + (12 - this.labellg);
39866 if(this.labelmd > 0){
39867 label.cls += ' col-md-' + this.labelmd;
39868 container.cls += ' col-md-' + (12 - this.labelmd);
39870 if(this.labelsm > 0){
39871 label.cls += ' col-sm-' + this.labelsm;
39872 container.cls += ' col-sm-' + (12 - this.labelsm);
39874 if(this.labelxs > 0){
39875 label.cls += ' col-xs-' + this.labelxs;
39876 container.cls += ' col-xs-' + (12 - this.labelxs);
39886 var settings = this;
39888 ['xs','sm','md','lg'].map(function(size){
39889 if (settings[size]) {
39890 cfg.cls += ' col-' + size + '-' + settings[size];
39894 this.store = new Roo.data.Store({
39895 proxy : new Roo.data.MemoryProxy({}),
39896 reader : new Roo.data.JsonReader({
39907 'name' : 'dialCode',
39911 'name' : 'priority',
39915 'name' : 'areaCodes',
39922 if(!this.preferedCountries) {
39923 this.preferedCountries = [
39930 var p = this.preferedCountries.reverse();
39933 for (var i = 0; i < p.length; i++) {
39934 for (var j = 0; j < this.allCountries.length; j++) {
39935 if(this.allCountries[j].iso2 == p[i]) {
39936 var t = this.allCountries[j];
39937 this.allCountries.splice(j,1);
39938 this.allCountries.unshift(t);
39944 this.store.proxy.data = {
39946 data: this.allCountries
39952 initEvents : function()
39955 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39957 this.indicator = this.indicatorEl();
39958 this.flag = this.flagEl();
39959 this.dialCodeHolder = this.dialCodeHolderEl();
39961 this.trigger = this.el.select('div.flag-box',true).first();
39962 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39967 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39968 _this.list.setWidth(lw);
39971 this.list.on('mouseover', this.onViewOver, this);
39972 this.list.on('mousemove', this.onViewMove, this);
39973 this.inputEl().on("keyup", this.onKeyUp, this);
39975 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39977 this.view = new Roo.View(this.list, this.tpl, {
39978 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39981 this.view.on('click', this.onViewClick, this);
39982 this.setValue(this.defaultDialCode);
39985 onTriggerClick : function(e)
39987 Roo.log('trigger click');
39992 if(this.isExpanded()){
39994 this.hasFocus = false;
39996 this.store.load({});
39997 this.hasFocus = true;
40002 isExpanded : function()
40004 return this.list.isVisible();
40007 collapse : function()
40009 if(!this.isExpanded()){
40013 Roo.get(document).un('mousedown', this.collapseIf, this);
40014 Roo.get(document).un('mousewheel', this.collapseIf, this);
40015 this.fireEvent('collapse', this);
40019 expand : function()
40023 if(this.isExpanded() || !this.hasFocus){
40027 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40028 this.list.setWidth(lw);
40031 this.restrictHeight();
40033 Roo.get(document).on('mousedown', this.collapseIf, this);
40034 Roo.get(document).on('mousewheel', this.collapseIf, this);
40036 this.fireEvent('expand', this);
40039 restrictHeight : function()
40041 this.list.alignTo(this.inputEl(), this.listAlign);
40042 this.list.alignTo(this.inputEl(), this.listAlign);
40045 onViewOver : function(e, t)
40047 if(this.inKeyMode){
40050 var item = this.view.findItemFromChild(t);
40053 var index = this.view.indexOf(item);
40054 this.select(index, false);
40059 onViewClick : function(view, doFocus, el, e)
40061 var index = this.view.getSelectedIndexes()[0];
40063 var r = this.store.getAt(index);
40066 this.onSelect(r, index);
40068 if(doFocus !== false && !this.blockFocus){
40069 this.inputEl().focus();
40073 onViewMove : function(e, t)
40075 this.inKeyMode = false;
40078 select : function(index, scrollIntoView)
40080 this.selectedIndex = index;
40081 this.view.select(index);
40082 if(scrollIntoView !== false){
40083 var el = this.view.getNode(index);
40085 this.list.scrollChildIntoView(el, false);
40090 createList : function()
40092 this.list = Roo.get(document.body).createChild({
40094 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40095 style: 'display:none'
40098 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40101 collapseIf : function(e)
40103 var in_combo = e.within(this.el);
40104 var in_list = e.within(this.list);
40105 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40107 if (in_combo || in_list || is_list) {
40113 onSelect : function(record, index)
40115 if(this.fireEvent('beforeselect', this, record, index) !== false){
40117 this.setFlagClass(record.data.iso2);
40118 this.setDialCode(record.data.dialCode);
40119 this.hasFocus = false;
40121 this.fireEvent('select', this, record, index);
40125 flagEl : function()
40127 var flag = this.el.select('div.flag',true).first();
40134 dialCodeHolderEl : function()
40136 var d = this.el.select('input.dial-code-holder',true).first();
40143 setDialCode : function(v)
40145 this.dialCodeHolder.dom.value = '+'+v;
40148 setFlagClass : function(n)
40150 this.flag.dom.className = 'flag '+n;
40153 getValue : function()
40155 var v = this.inputEl().getValue();
40156 if(this.dialCodeHolder) {
40157 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40162 setValue : function(v)
40164 var d = this.getDialCode(v);
40166 //invalid dial code
40167 if(v.length == 0 || !d || d.length == 0) {
40169 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40170 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40176 this.setFlagClass(this.dialCodeMapping[d].iso2);
40177 this.setDialCode(d);
40178 this.inputEl().dom.value = v.replace('+'+d,'');
40179 this.hiddenEl().dom.value = this.getValue();
40184 getDialCode : function(v)
40188 if (v.length == 0) {
40189 return this.dialCodeHolder.dom.value;
40193 if (v.charAt(0) != "+") {
40196 var numericChars = "";
40197 for (var i = 1; i < v.length; i++) {
40198 var c = v.charAt(i);
40201 if (this.dialCodeMapping[numericChars]) {
40202 dialCode = v.substr(1, i);
40204 if (numericChars.length == 4) {
40214 this.setValue(this.defaultDialCode);
40218 hiddenEl : function()
40220 return this.el.select('input.hidden-tel-input',true).first();
40223 onKeyUp : function(e){
40225 var k = e.getKey();
40226 var c = e.getCharCode();
40229 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40230 this.allowed.indexOf(String.fromCharCode(c)) === -1
40235 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40238 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40242 this.setValue(this.getValue());
40247 * @class Roo.bootstrap.MoneyField
40248 * @extends Roo.bootstrap.ComboBox
40249 * Bootstrap MoneyField class
40252 * Create a new MoneyField.
40253 * @param {Object} config Configuration options
40256 Roo.bootstrap.MoneyField = function(config) {
40258 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40262 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40265 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40267 allowDecimals : true,
40269 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40271 decimalSeparator : ".",
40273 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40275 decimalPrecision : 0,
40277 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40279 allowNegative : true,
40281 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40285 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40287 minValue : Number.NEGATIVE_INFINITY,
40289 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40291 maxValue : Number.MAX_VALUE,
40293 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40295 minText : "The minimum value for this field is {0}",
40297 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40299 maxText : "The maximum value for this field is {0}",
40301 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40302 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40304 nanText : "{0} is not a valid number",
40306 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40310 * @cfg {String} defaults currency of the MoneyField
40311 * value should be in lkey
40313 defaultCurrency : false,
40315 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40317 thousandsDelimiter : false,
40327 getAutoCreate : function()
40329 var align = this.labelAlign || this.parentLabelAlign();
40341 cls : 'form-control roo-money-amount-input',
40342 autocomplete: 'new-password'
40345 var hiddenInput = {
40349 cls: 'hidden-number-input'
40353 hiddenInput.name = this.name;
40356 if (this.disabled) {
40357 input.disabled = true;
40360 var clg = 12 - this.inputlg;
40361 var cmd = 12 - this.inputmd;
40362 var csm = 12 - this.inputsm;
40363 var cxs = 12 - this.inputxs;
40367 cls : 'row roo-money-field',
40371 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40375 cls: 'roo-select2-container input-group',
40379 cls : 'form-control roo-money-currency-input',
40380 autocomplete: 'new-password',
40382 name : this.currencyName
40386 cls : 'input-group-addon',
40400 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40404 cls: this.hasFeedback ? 'has-feedback' : '',
40415 if (this.fieldLabel.length) {
40418 tooltip: 'This field is required'
40424 cls: 'control-label',
40430 html: this.fieldLabel
40433 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40439 if(this.indicatorpos == 'right') {
40440 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40447 if(align == 'left') {
40455 if(this.labelWidth > 12){
40456 label.style = "width: " + this.labelWidth + 'px';
40458 if(this.labelWidth < 13 && this.labelmd == 0){
40459 this.labelmd = this.labelWidth;
40461 if(this.labellg > 0){
40462 label.cls += ' col-lg-' + this.labellg;
40463 input.cls += ' col-lg-' + (12 - this.labellg);
40465 if(this.labelmd > 0){
40466 label.cls += ' col-md-' + this.labelmd;
40467 container.cls += ' col-md-' + (12 - this.labelmd);
40469 if(this.labelsm > 0){
40470 label.cls += ' col-sm-' + this.labelsm;
40471 container.cls += ' col-sm-' + (12 - this.labelsm);
40473 if(this.labelxs > 0){
40474 label.cls += ' col-xs-' + this.labelxs;
40475 container.cls += ' col-xs-' + (12 - this.labelxs);
40486 var settings = this;
40488 ['xs','sm','md','lg'].map(function(size){
40489 if (settings[size]) {
40490 cfg.cls += ' col-' + size + '-' + settings[size];
40497 initEvents : function()
40499 this.indicator = this.indicatorEl();
40501 this.initCurrencyEvent();
40503 this.initNumberEvent();
40506 initCurrencyEvent : function()
40509 throw "can not find store for combo";
40512 this.store = Roo.factory(this.store, Roo.data);
40513 this.store.parent = this;
40517 this.triggerEl = this.el.select('.input-group-addon', true).first();
40519 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40524 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40525 _this.list.setWidth(lw);
40528 this.list.on('mouseover', this.onViewOver, this);
40529 this.list.on('mousemove', this.onViewMove, this);
40530 this.list.on('scroll', this.onViewScroll, this);
40533 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40536 this.view = new Roo.View(this.list, this.tpl, {
40537 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40540 this.view.on('click', this.onViewClick, this);
40542 this.store.on('beforeload', this.onBeforeLoad, this);
40543 this.store.on('load', this.onLoad, this);
40544 this.store.on('loadexception', this.onLoadException, this);
40546 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40547 "up" : function(e){
40548 this.inKeyMode = true;
40552 "down" : function(e){
40553 if(!this.isExpanded()){
40554 this.onTriggerClick();
40556 this.inKeyMode = true;
40561 "enter" : function(e){
40564 if(this.fireEvent("specialkey", this, e)){
40565 this.onViewClick(false);
40571 "esc" : function(e){
40575 "tab" : function(e){
40578 if(this.fireEvent("specialkey", this, e)){
40579 this.onViewClick(false);
40587 doRelay : function(foo, bar, hname){
40588 if(hname == 'down' || this.scope.isExpanded()){
40589 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40597 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40601 initNumberEvent : function(e)
40603 this.inputEl().on("keydown" , this.fireKey, this);
40604 this.inputEl().on("focus", this.onFocus, this);
40605 this.inputEl().on("blur", this.onBlur, this);
40607 this.inputEl().relayEvent('keyup', this);
40609 if(this.indicator){
40610 this.indicator.addClass('invisible');
40613 this.originalValue = this.getValue();
40615 if(this.validationEvent == 'keyup'){
40616 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40617 this.inputEl().on('keyup', this.filterValidation, this);
40619 else if(this.validationEvent !== false){
40620 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40623 if(this.selectOnFocus){
40624 this.on("focus", this.preFocus, this);
40627 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40628 this.inputEl().on("keypress", this.filterKeys, this);
40630 this.inputEl().relayEvent('keypress', this);
40633 var allowed = "0123456789";
40635 if(this.allowDecimals){
40636 allowed += this.decimalSeparator;
40639 if(this.allowNegative){
40643 if(this.thousandsDelimiter) {
40647 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40649 var keyPress = function(e){
40651 var k = e.getKey();
40653 var c = e.getCharCode();
40656 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40657 allowed.indexOf(String.fromCharCode(c)) === -1
40663 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40667 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40672 this.inputEl().on("keypress", keyPress, this);
40676 onTriggerClick : function(e)
40683 this.loadNext = false;
40685 if(this.isExpanded()){
40690 this.hasFocus = true;
40692 if(this.triggerAction == 'all') {
40693 this.doQuery(this.allQuery, true);
40697 this.doQuery(this.getRawValue());
40700 getCurrency : function()
40702 var v = this.currencyEl().getValue();
40707 restrictHeight : function()
40709 this.list.alignTo(this.currencyEl(), this.listAlign);
40710 this.list.alignTo(this.currencyEl(), this.listAlign);
40713 onViewClick : function(view, doFocus, el, e)
40715 var index = this.view.getSelectedIndexes()[0];
40717 var r = this.store.getAt(index);
40720 this.onSelect(r, index);
40724 onSelect : function(record, index){
40726 if(this.fireEvent('beforeselect', this, record, index) !== false){
40728 this.setFromCurrencyData(index > -1 ? record.data : false);
40732 this.fireEvent('select', this, record, index);
40736 setFromCurrencyData : function(o)
40740 this.lastCurrency = o;
40742 if (this.currencyField) {
40743 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40745 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40748 this.lastSelectionText = currency;
40750 //setting default currency
40751 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40752 this.setCurrency(this.defaultCurrency);
40756 this.setCurrency(currency);
40759 setFromData : function(o)
40763 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40765 this.setFromCurrencyData(c);
40770 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40772 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40775 this.setValue(value);
40779 setCurrency : function(v)
40781 this.currencyValue = v;
40784 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40789 setValue : function(v)
40791 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40797 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40799 this.inputEl().dom.value = (v == '') ? '' :
40800 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40802 if(!this.allowZero && v === '0') {
40803 this.hiddenEl().dom.value = '';
40804 this.inputEl().dom.value = '';
40811 getRawValue : function()
40813 var v = this.inputEl().getValue();
40818 getValue : function()
40820 return this.fixPrecision(this.parseValue(this.getRawValue()));
40823 parseValue : function(value)
40825 if(this.thousandsDelimiter) {
40827 r = new RegExp(",", "g");
40828 value = value.replace(r, "");
40831 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40832 return isNaN(value) ? '' : value;
40836 fixPrecision : function(value)
40838 if(this.thousandsDelimiter) {
40840 r = new RegExp(",", "g");
40841 value = value.replace(r, "");
40844 var nan = isNaN(value);
40846 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40847 return nan ? '' : value;
40849 return parseFloat(value).toFixed(this.decimalPrecision);
40852 decimalPrecisionFcn : function(v)
40854 return Math.floor(v);
40857 validateValue : function(value)
40859 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40863 var num = this.parseValue(value);
40866 this.markInvalid(String.format(this.nanText, value));
40870 if(num < this.minValue){
40871 this.markInvalid(String.format(this.minText, this.minValue));
40875 if(num > this.maxValue){
40876 this.markInvalid(String.format(this.maxText, this.maxValue));
40883 validate : function()
40885 if(this.disabled || this.allowBlank){
40890 var currency = this.getCurrency();
40892 if(this.validateValue(this.getRawValue()) && currency.length){
40897 this.markInvalid();
40901 getName: function()
40906 beforeBlur : function()
40912 var v = this.parseValue(this.getRawValue());
40919 onBlur : function()
40923 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40924 //this.el.removeClass(this.focusClass);
40927 this.hasFocus = false;
40929 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40933 var v = this.getValue();
40935 if(String(v) !== String(this.startValue)){
40936 this.fireEvent('change', this, v, this.startValue);
40939 this.fireEvent("blur", this);
40942 inputEl : function()
40944 return this.el.select('.roo-money-amount-input', true).first();
40947 currencyEl : function()
40949 return this.el.select('.roo-money-currency-input', true).first();
40952 hiddenEl : function()
40954 return this.el.select('input.hidden-number-input',true).first();