4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2650 onRender : function(ct, position)
2652 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2655 var cfg = Roo.apply({}, this.getAutoCreate());
2658 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660 //if (!cfg.name.length) {
2664 cfg.cls += ' ' + this.cls;
2667 cfg.style = this.style;
2669 this.el = Roo.get(document.body).createChild(cfg, position);
2671 //var type = this.el.dom.type;
2674 if(this.tabIndex !== undefined){
2675 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2678 this.dialogEl = this.el.select('.modal-dialog',true).first();
2679 this.bodyEl = this.el.select('.modal-body',true).first();
2680 this.closeEl = this.el.select('.modal-header .close', true).first();
2681 this.headerEl = this.el.select('.modal-header',true).first();
2682 this.titleEl = this.el.select('.modal-title',true).first();
2683 this.footerEl = this.el.select('.modal-footer',true).first();
2685 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687 //this.el.addClass("x-dlg-modal");
2689 if (this.buttons.length) {
2690 Roo.each(this.buttons, function(bb) {
2691 var b = Roo.apply({}, bb);
2692 b.xns = b.xns || Roo.bootstrap;
2693 b.xtype = b.xtype || 'Button';
2694 if (typeof(b.listeners) == 'undefined') {
2695 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2698 var btn = Roo.factory(b);
2700 btn.render(this.el.select('.modal-footer div').first());
2704 // render the children.
2707 if(typeof(this.items) != 'undefined'){
2708 var items = this.items;
2711 for(var i =0;i < items.length;i++) {
2712 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2716 this.items = nitems;
2718 // where are these used - they used to be body/close/footer
2722 //this.el.addClass([this.fieldClass, this.cls]);
2726 getAutoCreate : function()
2730 html : this.html || ''
2735 cls : 'modal-title',
2739 if(this.specificTitle){
2745 if (this.allow_close) {
2757 if(this.size.length){
2758 size = 'modal-' + this.size;
2765 cls: "modal-dialog " + size,
2768 cls : "modal-content",
2771 cls : 'modal-header',
2776 cls : 'modal-footer',
2780 cls: 'btn-' + this.buttonPosition
2797 modal.cls += ' fade';
2803 getChildContainer : function() {
2808 getButtonContainer : function() {
2809 return this.el.select('.modal-footer div',true).first();
2812 initEvents : function()
2814 if (this.allow_close) {
2815 this.closeEl.on('click', this.hide, this);
2817 Roo.EventManager.onWindowResize(this.resize, this, true);
2824 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2826 if (this.fitwindow) {
2827 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2832 if(!this.fitwindow && this.max_width !== 0){
2834 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2838 this.setSize(w, this.height);
2842 var body_childs = this.bodyEl.dom.childNodes;
2843 var full_height = this.headerEl.getHeight() + this.footerEl.getHeight();
2844 for(var i = 0; i < body_childs.length; i++) {
2845 full_height += body_childs[i].offsetHeight;
2848 // this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2853 setSize : function(w,h)
2863 if (!this.rendered) {
2867 //this.el.setStyle('display', 'block');
2868 this.el.removeClass('hideing');
2869 this.el.addClass('show');
2871 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2874 this.el.addClass('in');
2877 this.el.addClass('in');
2880 // not sure how we can show data in here..
2882 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2885 Roo.get(document.body).addClass("x-body-masked");
2887 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2888 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2889 this.maskEl.addClass('show');
2893 this.fireEvent('show', this);
2895 // set zindex here - otherwise it appears to be ignored...
2896 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2899 this.items.forEach( function(e) {
2900 e.layout ? e.layout() : false;
2908 if(this.fireEvent("beforehide", this) !== false){
2909 this.maskEl.removeClass('show');
2910 Roo.get(document.body).removeClass("x-body-masked");
2911 this.el.removeClass('in');
2912 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2914 if(this.animate){ // why
2915 this.el.addClass('hideing');
2917 if (!this.el.hasClass('hideing')) {
2918 return; // it's been shown again...
2920 this.el.removeClass('show');
2921 this.el.removeClass('hideing');
2925 this.el.removeClass('show');
2927 this.fireEvent('hide', this);
2930 isVisible : function()
2933 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2937 addButton : function(str, cb)
2941 var b = Roo.apply({}, { html : str } );
2942 b.xns = b.xns || Roo.bootstrap;
2943 b.xtype = b.xtype || 'Button';
2944 if (typeof(b.listeners) == 'undefined') {
2945 b.listeners = { click : cb.createDelegate(this) };
2948 var btn = Roo.factory(b);
2950 btn.render(this.el.select('.modal-footer div').first());
2956 setDefaultButton : function(btn)
2958 //this.el.select('.modal-footer').()
2962 resizeTo: function(w,h)
2966 this.dialogEl.setWidth(w);
2967 if (this.diff === false) {
2968 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2971 this.bodyEl.setHeight(h-this.diff);
2973 this.fireEvent('resize', this);
2976 setContentSize : function(w, h)
2980 onButtonClick: function(btn,e)
2983 this.fireEvent('btnclick', btn.name, e);
2986 * Set the title of the Dialog
2987 * @param {String} str new Title
2989 setTitle: function(str) {
2990 this.titleEl.dom.innerHTML = str;
2993 * Set the body of the Dialog
2994 * @param {String} str new Title
2996 setBody: function(str) {
2997 this.bodyEl.dom.innerHTML = str;
3000 * Set the body of the Dialog using the template
3001 * @param {Obj} data - apply this data to the template and replace the body contents.
3003 applyBody: function(obj)
3006 Roo.log("Error - using apply Body without a template");
3009 this.tmpl.overwrite(this.bodyEl, obj);
3015 Roo.apply(Roo.bootstrap.Modal, {
3017 * Button config that displays a single OK button
3026 * Button config that displays Yes and No buttons
3042 * Button config that displays OK and Cancel buttons
3057 * Button config that displays Yes, No and Cancel buttons
3081 * messagebox - can be used as a replace
3085 * @class Roo.MessageBox
3086 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3090 Roo.Msg.alert('Status', 'Changes saved successfully.');
3092 // Prompt for user data:
3093 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3095 // process text value...
3099 // Show a dialog using config options:
3101 title:'Save Changes?',
3102 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3103 buttons: Roo.Msg.YESNOCANCEL,
3110 Roo.bootstrap.MessageBox = function(){
3111 var dlg, opt, mask, waitTimer;
3112 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3113 var buttons, activeTextEl, bwidth;
3117 var handleButton = function(button){
3119 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3123 var handleHide = function(){
3125 dlg.el.removeClass(opt.cls);
3128 // Roo.TaskMgr.stop(waitTimer);
3129 // waitTimer = null;
3134 var updateButtons = function(b){
3137 buttons["ok"].hide();
3138 buttons["cancel"].hide();
3139 buttons["yes"].hide();
3140 buttons["no"].hide();
3141 //dlg.footer.dom.style.display = 'none';
3144 dlg.footerEl.dom.style.display = '';
3145 for(var k in buttons){
3146 if(typeof buttons[k] != "function"){
3149 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3150 width += buttons[k].el.getWidth()+15;
3160 var handleEsc = function(d, k, e){
3161 if(opt && opt.closable !== false){
3171 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3172 * @return {Roo.BasicDialog} The BasicDialog element
3174 getDialog : function(){
3176 dlg = new Roo.bootstrap.Modal( {
3179 //constraintoviewport:false,
3181 //collapsible : false,
3186 //buttonAlign:"center",
3187 closeClick : function(){
3188 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3191 handleButton("cancel");
3196 dlg.on("hide", handleHide);
3198 //dlg.addKeyListener(27, handleEsc);
3200 this.buttons = buttons;
3201 var bt = this.buttonText;
3202 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3203 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3204 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3205 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3207 bodyEl = dlg.bodyEl.createChild({
3209 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3210 '<textarea class="roo-mb-textarea"></textarea>' +
3211 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3213 msgEl = bodyEl.dom.firstChild;
3214 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3215 textboxEl.enableDisplayMode();
3216 textboxEl.addKeyListener([10,13], function(){
3217 if(dlg.isVisible() && opt && opt.buttons){
3220 }else if(opt.buttons.yes){
3221 handleButton("yes");
3225 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3226 textareaEl.enableDisplayMode();
3227 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3228 progressEl.enableDisplayMode();
3230 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3231 var pf = progressEl.dom.firstChild;
3233 pp = Roo.get(pf.firstChild);
3234 pp.setHeight(pf.offsetHeight);
3242 * Updates the message box body text
3243 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3244 * the XHTML-compliant non-breaking space character '&#160;')
3245 * @return {Roo.MessageBox} This message box
3247 updateText : function(text)
3249 if(!dlg.isVisible() && !opt.width){
3250 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3251 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3253 msgEl.innerHTML = text || ' ';
3255 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3256 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3258 Math.min(opt.width || cw , this.maxWidth),
3259 Math.max(opt.minWidth || this.minWidth, bwidth)
3262 activeTextEl.setWidth(w);
3264 if(dlg.isVisible()){
3265 dlg.fixedcenter = false;
3267 // to big, make it scroll. = But as usual stupid IE does not support
3270 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3271 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3272 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3274 bodyEl.dom.style.height = '';
3275 bodyEl.dom.style.overflowY = '';
3278 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3280 bodyEl.dom.style.overflowX = '';
3283 dlg.setContentSize(w, bodyEl.getHeight());
3284 if(dlg.isVisible()){
3285 dlg.fixedcenter = true;
3291 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3292 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3293 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3294 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3295 * @return {Roo.MessageBox} This message box
3297 updateProgress : function(value, text){
3299 this.updateText(text);
3302 if (pp) { // weird bug on my firefox - for some reason this is not defined
3303 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3304 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3310 * Returns true if the message box is currently displayed
3311 * @return {Boolean} True if the message box is visible, else false
3313 isVisible : function(){
3314 return dlg && dlg.isVisible();
3318 * Hides the message box if it is displayed
3321 if(this.isVisible()){
3327 * Displays a new message box, or reinitializes an existing message box, based on the config options
3328 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3329 * The following config object properties are supported:
3331 Property Type Description
3332 ---------- --------------- ------------------------------------------------------------------------------------
3333 animEl String/Element An id or Element from which the message box should animate as it opens and
3334 closes (defaults to undefined)
3335 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3336 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3337 closable Boolean False to hide the top-right close button (defaults to true). Note that
3338 progress and wait dialogs will ignore this property and always hide the
3339 close button as they can only be closed programmatically.
3340 cls String A custom CSS class to apply to the message box element
3341 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3342 displayed (defaults to 75)
3343 fn Function A callback function to execute after closing the dialog. The arguments to the
3344 function will be btn (the name of the button that was clicked, if applicable,
3345 e.g. "ok"), and text (the value of the active text field, if applicable).
3346 Progress and wait dialogs will ignore this option since they do not respond to
3347 user actions and can only be closed programmatically, so any required function
3348 should be called by the same code after it closes the dialog.
3349 icon String A CSS class that provides a background image to be used as an icon for
3350 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3351 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3352 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3353 modal Boolean False to allow user interaction with the page while the message box is
3354 displayed (defaults to true)
3355 msg String A string that will replace the existing message box body text (defaults
3356 to the XHTML-compliant non-breaking space character ' ')
3357 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3358 progress Boolean True to display a progress bar (defaults to false)
3359 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3360 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3361 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3362 title String The title text
3363 value String The string value to set into the active textbox element if displayed
3364 wait Boolean True to display a progress bar (defaults to false)
3365 width Number The width of the dialog in pixels
3372 msg: 'Please enter your address:',
3374 buttons: Roo.MessageBox.OKCANCEL,
3377 animEl: 'addAddressBtn'
3380 * @param {Object} config Configuration options
3381 * @return {Roo.MessageBox} This message box
3383 show : function(options)
3386 // this causes nightmares if you show one dialog after another
3387 // especially on callbacks..
3389 if(this.isVisible()){
3392 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3393 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3394 Roo.log("New Dialog Message:" + options.msg )
3395 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3396 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3399 var d = this.getDialog();
3401 d.setTitle(opt.title || " ");
3402 d.closeEl.setDisplayed(opt.closable !== false);
3403 activeTextEl = textboxEl;
3404 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3409 textareaEl.setHeight(typeof opt.multiline == "number" ?
3410 opt.multiline : this.defaultTextHeight);
3411 activeTextEl = textareaEl;
3420 progressEl.setDisplayed(opt.progress === true);
3421 this.updateProgress(0);
3422 activeTextEl.dom.value = opt.value || "";
3424 dlg.setDefaultButton(activeTextEl);
3426 var bs = opt.buttons;
3430 }else if(bs && bs.yes){
3431 db = buttons["yes"];
3433 dlg.setDefaultButton(db);
3435 bwidth = updateButtons(opt.buttons);
3436 this.updateText(opt.msg);
3438 d.el.addClass(opt.cls);
3440 d.proxyDrag = opt.proxyDrag === true;
3441 d.modal = opt.modal !== false;
3442 d.mask = opt.modal !== false ? mask : false;
3444 // force it to the end of the z-index stack so it gets a cursor in FF
3445 document.body.appendChild(dlg.el.dom);
3446 d.animateTarget = null;
3447 d.show(options.animEl);
3453 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3454 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3455 * and closing the message box when the process is complete.
3456 * @param {String} title The title bar text
3457 * @param {String} msg The message box body text
3458 * @return {Roo.MessageBox} This message box
3460 progress : function(title, msg){
3467 minWidth: this.minProgressWidth,
3474 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3475 * If a callback function is passed it will be called after the user clicks the button, and the
3476 * id of the button that was clicked will be passed as the only parameter to the callback
3477 * (could also be the top-right close button).
3478 * @param {String} title The title bar text
3479 * @param {String} msg The message box body text
3480 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3481 * @param {Object} scope (optional) The scope of the callback function
3482 * @return {Roo.MessageBox} This message box
3484 alert : function(title, msg, fn, scope)
3499 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3500 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3501 * You are responsible for closing the message box when the process is complete.
3502 * @param {String} msg The message box body text
3503 * @param {String} title (optional) The title bar text
3504 * @return {Roo.MessageBox} This message box
3506 wait : function(msg, title){
3517 waitTimer = Roo.TaskMgr.start({
3519 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3527 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3528 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3529 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3530 * @param {String} title The title bar text
3531 * @param {String} msg The message box body text
3532 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3533 * @param {Object} scope (optional) The scope of the callback function
3534 * @return {Roo.MessageBox} This message box
3536 confirm : function(title, msg, fn, scope){
3540 buttons: this.YESNO,
3549 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3550 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3551 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3552 * (could also be the top-right close button) and the text that was entered will be passed as the two
3553 * parameters to the callback.
3554 * @param {String} title The title bar text
3555 * @param {String} msg The message box body text
3556 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3557 * @param {Object} scope (optional) The scope of the callback function
3558 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3559 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3560 * @return {Roo.MessageBox} This message box
3562 prompt : function(title, msg, fn, scope, multiline){
3566 buttons: this.OKCANCEL,
3571 multiline: multiline,
3578 * Button config that displays a single OK button
3583 * Button config that displays Yes and No buttons
3586 YESNO : {yes:true, no:true},
3588 * Button config that displays OK and Cancel buttons
3591 OKCANCEL : {ok:true, cancel:true},
3593 * Button config that displays Yes, No and Cancel buttons
3596 YESNOCANCEL : {yes:true, no:true, cancel:true},
3599 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3602 defaultTextHeight : 75,
3604 * The maximum width in pixels of the message box (defaults to 600)
3609 * The minimum width in pixels of the message box (defaults to 100)
3614 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3615 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3618 minProgressWidth : 250,
3620 * An object containing the default button text strings that can be overriden for localized language support.
3621 * Supported properties are: ok, cancel, yes and no.
3622 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3635 * Shorthand for {@link Roo.MessageBox}
3637 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3638 Roo.Msg = Roo.Msg || Roo.MessageBox;
3647 * @class Roo.bootstrap.Navbar
3648 * @extends Roo.bootstrap.Component
3649 * Bootstrap Navbar class
3652 * Create a new Navbar
3653 * @param {Object} config The config object
3657 Roo.bootstrap.Navbar = function(config){
3658 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3662 * @event beforetoggle
3663 * Fire before toggle the menu
3664 * @param {Roo.EventObject} e
3666 "beforetoggle" : true
3670 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3679 getAutoCreate : function(){
3682 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3686 initEvents :function ()
3688 //Roo.log(this.el.select('.navbar-toggle',true));
3689 this.el.select('.navbar-toggle',true).on('click', function() {
3690 if(this.fireEvent('beforetoggle', this) !== false){
3691 this.el.select('.navbar-collapse',true).toggleClass('in');
3701 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3703 var size = this.el.getSize();
3704 this.maskEl.setSize(size.width, size.height);
3705 this.maskEl.enableDisplayMode("block");
3714 getChildContainer : function()
3716 if (this.el.select('.collapse').getCount()) {
3717 return this.el.select('.collapse',true).first();
3750 * @class Roo.bootstrap.NavSimplebar
3751 * @extends Roo.bootstrap.Navbar
3752 * Bootstrap Sidebar class
3754 * @cfg {Boolean} inverse is inverted color
3756 * @cfg {String} type (nav | pills | tabs)
3757 * @cfg {Boolean} arrangement stacked | justified
3758 * @cfg {String} align (left | right) alignment
3760 * @cfg {Boolean} main (true|false) main nav bar? default false
3761 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3763 * @cfg {String} tag (header|footer|nav|div) default is nav
3769 * Create a new Sidebar
3770 * @param {Object} config The config object
3774 Roo.bootstrap.NavSimplebar = function(config){
3775 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3778 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3794 getAutoCreate : function(){
3798 tag : this.tag || 'div',
3811 this.type = this.type || 'nav';
3812 if (['tabs','pills'].indexOf(this.type)!==-1) {
3813 cfg.cn[0].cls += ' nav-' + this.type
3817 if (this.type!=='nav') {
3818 Roo.log('nav type must be nav/tabs/pills')
3820 cfg.cn[0].cls += ' navbar-nav'
3826 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3827 cfg.cn[0].cls += ' nav-' + this.arrangement;
3831 if (this.align === 'right') {
3832 cfg.cn[0].cls += ' navbar-right';
3836 cfg.cls += ' navbar-inverse';
3863 * @class Roo.bootstrap.NavHeaderbar
3864 * @extends Roo.bootstrap.NavSimplebar
3865 * Bootstrap Sidebar class
3867 * @cfg {String} brand what is brand
3868 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3869 * @cfg {String} brand_href href of the brand
3870 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3871 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3872 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3873 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3876 * Create a new Sidebar
3877 * @param {Object} config The config object
3881 Roo.bootstrap.NavHeaderbar = function(config){
3882 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3886 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3893 desktopCenter : false,
3896 getAutoCreate : function(){
3899 tag: this.nav || 'nav',
3906 if (this.desktopCenter) {
3907 cn.push({cls : 'container', cn : []});
3914 cls: 'navbar-header',
3919 cls: 'navbar-toggle',
3920 'data-toggle': 'collapse',
3925 html: 'Toggle navigation'
3947 cls: 'collapse navbar-collapse',
3951 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3953 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3954 cfg.cls += ' navbar-' + this.position;
3956 // tag can override this..
3958 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3961 if (this.brand !== '') {
3964 href: this.brand_href ? this.brand_href : '#',
3965 cls: 'navbar-brand',
3973 cfg.cls += ' main-nav';
3981 getHeaderChildContainer : function()
3983 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3984 return this.el.select('.navbar-header',true).first();
3987 return this.getChildContainer();
3991 initEvents : function()
3993 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3995 if (this.autohide) {
4000 Roo.get(document).on('scroll',function(e) {
4001 var ns = Roo.get(document).getScroll().top;
4002 var os = prevScroll;
4006 ft.removeClass('slideDown');
4007 ft.addClass('slideUp');
4010 ft.removeClass('slideUp');
4011 ft.addClass('slideDown');
4032 * @class Roo.bootstrap.NavSidebar
4033 * @extends Roo.bootstrap.Navbar
4034 * Bootstrap Sidebar class
4037 * Create a new Sidebar
4038 * @param {Object} config The config object
4042 Roo.bootstrap.NavSidebar = function(config){
4043 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4046 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4048 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4050 getAutoCreate : function(){
4055 cls: 'sidebar sidebar-nav'
4077 * @class Roo.bootstrap.NavGroup
4078 * @extends Roo.bootstrap.Component
4079 * Bootstrap NavGroup class
4080 * @cfg {String} align (left|right)
4081 * @cfg {Boolean} inverse
4082 * @cfg {String} type (nav|pills|tab) default nav
4083 * @cfg {String} navId - reference Id for navbar.
4087 * Create a new nav group
4088 * @param {Object} config The config object
4091 Roo.bootstrap.NavGroup = function(config){
4092 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4095 Roo.bootstrap.NavGroup.register(this);
4099 * Fires when the active item changes
4100 * @param {Roo.bootstrap.NavGroup} this
4101 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4102 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4109 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4120 getAutoCreate : function()
4122 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4129 if (['tabs','pills'].indexOf(this.type)!==-1) {
4130 cfg.cls += ' nav-' + this.type
4132 if (this.type!=='nav') {
4133 Roo.log('nav type must be nav/tabs/pills')
4135 cfg.cls += ' navbar-nav'
4138 if (this.parent() && this.parent().sidebar) {
4141 cls: 'dashboard-menu sidebar-menu'
4147 if (this.form === true) {
4153 if (this.align === 'right') {
4154 cfg.cls += ' navbar-right';
4156 cfg.cls += ' navbar-left';
4160 if (this.align === 'right') {
4161 cfg.cls += ' navbar-right';
4165 cfg.cls += ' navbar-inverse';
4173 * sets the active Navigation item
4174 * @param {Roo.bootstrap.NavItem} the new current navitem
4176 setActiveItem : function(item)
4179 Roo.each(this.navItems, function(v){
4184 v.setActive(false, true);
4191 item.setActive(true, true);
4192 this.fireEvent('changed', this, item, prev);
4197 * gets the active Navigation item
4198 * @return {Roo.bootstrap.NavItem} the current navitem
4200 getActive : function()
4204 Roo.each(this.navItems, function(v){
4215 indexOfNav : function()
4219 Roo.each(this.navItems, function(v,i){
4230 * adds a Navigation item
4231 * @param {Roo.bootstrap.NavItem} the navitem to add
4233 addItem : function(cfg)
4235 var cn = new Roo.bootstrap.NavItem(cfg);
4237 cn.parentId = this.id;
4238 cn.onRender(this.el, null);
4242 * register a Navigation item
4243 * @param {Roo.bootstrap.NavItem} the navitem to add
4245 register : function(item)
4247 this.navItems.push( item);
4248 item.navId = this.navId;
4253 * clear all the Navigation item
4256 clearAll : function()
4259 this.el.dom.innerHTML = '';
4262 getNavItem: function(tabId)
4265 Roo.each(this.navItems, function(e) {
4266 if (e.tabId == tabId) {
4276 setActiveNext : function()
4278 var i = this.indexOfNav(this.getActive());
4279 if (i > this.navItems.length) {
4282 this.setActiveItem(this.navItems[i+1]);
4284 setActivePrev : function()
4286 var i = this.indexOfNav(this.getActive());
4290 this.setActiveItem(this.navItems[i-1]);
4292 clearWasActive : function(except) {
4293 Roo.each(this.navItems, function(e) {
4294 if (e.tabId != except.tabId && e.was_active) {
4295 e.was_active = false;
4302 getWasActive : function ()
4305 Roo.each(this.navItems, function(e) {
4320 Roo.apply(Roo.bootstrap.NavGroup, {
4324 * register a Navigation Group
4325 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4327 register : function(navgrp)
4329 this.groups[navgrp.navId] = navgrp;
4333 * fetch a Navigation Group based on the navigation ID
4334 * @param {string} the navgroup to add
4335 * @returns {Roo.bootstrap.NavGroup} the navgroup
4337 get: function(navId) {
4338 if (typeof(this.groups[navId]) == 'undefined') {
4340 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4342 return this.groups[navId] ;
4357 * @class Roo.bootstrap.NavItem
4358 * @extends Roo.bootstrap.Component
4359 * Bootstrap Navbar.NavItem class
4360 * @cfg {String} href link to
4361 * @cfg {String} html content of button
4362 * @cfg {String} badge text inside badge
4363 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4364 * @cfg {String} glyphicon name of glyphicon
4365 * @cfg {String} icon name of font awesome icon
4366 * @cfg {Boolean} active Is item active
4367 * @cfg {Boolean} disabled Is item disabled
4369 * @cfg {Boolean} preventDefault (true | false) default false
4370 * @cfg {String} tabId the tab that this item activates.
4371 * @cfg {String} tagtype (a|span) render as a href or span?
4372 * @cfg {Boolean} animateRef (true|false) link to element default false
4375 * Create a new Navbar Item
4376 * @param {Object} config The config object
4378 Roo.bootstrap.NavItem = function(config){
4379 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4384 * The raw click event for the entire grid.
4385 * @param {Roo.EventObject} e
4390 * Fires when the active item active state changes
4391 * @param {Roo.bootstrap.NavItem} this
4392 * @param {boolean} state the new state
4398 * Fires when scroll to element
4399 * @param {Roo.bootstrap.NavItem} this
4400 * @param {Object} options
4401 * @param {Roo.EventObject} e
4409 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4417 preventDefault : false,
4424 getAutoCreate : function(){
4433 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4435 if (this.disabled) {
4436 cfg.cls += ' disabled';
4439 if (this.href || this.html || this.glyphicon || this.icon) {
4443 href : this.href || "#",
4444 html: this.html || ''
4449 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4452 if(this.glyphicon) {
4453 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4458 cfg.cn[0].html += " <span class='caret'></span>";
4462 if (this.badge !== '') {
4464 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4472 initEvents: function()
4474 if (typeof (this.menu) != 'undefined') {
4475 this.menu.parentType = this.xtype;
4476 this.menu.triggerEl = this.el;
4477 this.menu = this.addxtype(Roo.apply({}, this.menu));
4480 this.el.select('a',true).on('click', this.onClick, this);
4482 if(this.tagtype == 'span'){
4483 this.el.select('span',true).on('click', this.onClick, this);
4486 // at this point parent should be available..
4487 this.parent().register(this);
4490 onClick : function(e)
4492 if (e.getTarget('.dropdown-menu-item')) {
4493 // did you click on a menu itemm.... - then don't trigger onclick..
4498 this.preventDefault ||
4501 Roo.log("NavItem - prevent Default?");
4505 if (this.disabled) {
4509 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4510 if (tg && tg.transition) {
4511 Roo.log("waiting for the transitionend");
4517 //Roo.log("fire event clicked");
4518 if(this.fireEvent('click', this, e) === false){
4522 if(this.tagtype == 'span'){
4526 //Roo.log(this.href);
4527 var ael = this.el.select('a',true).first();
4530 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4531 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4532 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4533 return; // ignore... - it's a 'hash' to another page.
4535 Roo.log("NavItem - prevent Default?");
4537 this.scrollToElement(e);
4541 var p = this.parent();
4543 if (['tabs','pills'].indexOf(p.type)!==-1) {
4544 if (typeof(p.setActiveItem) !== 'undefined') {
4545 p.setActiveItem(this);
4549 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4550 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4551 // remove the collapsed menu expand...
4552 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4556 isActive: function () {
4559 setActive : function(state, fire, is_was_active)
4561 if (this.active && !state && this.navId) {
4562 this.was_active = true;
4563 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4565 nv.clearWasActive(this);
4569 this.active = state;
4572 this.el.removeClass('active');
4573 } else if (!this.el.hasClass('active')) {
4574 this.el.addClass('active');
4577 this.fireEvent('changed', this, state);
4580 // show a panel if it's registered and related..
4582 if (!this.navId || !this.tabId || !state || is_was_active) {
4586 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4590 var pan = tg.getPanelByName(this.tabId);
4594 // if we can not flip to new panel - go back to old nav highlight..
4595 if (false == tg.showPanel(pan)) {
4596 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4598 var onav = nv.getWasActive();
4600 onav.setActive(true, false, true);
4609 // this should not be here...
4610 setDisabled : function(state)
4612 this.disabled = state;
4614 this.el.removeClass('disabled');
4615 } else if (!this.el.hasClass('disabled')) {
4616 this.el.addClass('disabled');
4622 * Fetch the element to display the tooltip on.
4623 * @return {Roo.Element} defaults to this.el
4625 tooltipEl : function()
4627 return this.el.select('' + this.tagtype + '', true).first();
4630 scrollToElement : function(e)
4632 var c = document.body;
4635 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4637 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4638 c = document.documentElement;
4641 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4647 var o = target.calcOffsetsTo(c);
4654 this.fireEvent('scrollto', this, options, e);
4656 Roo.get(c).scrollTo('top', options.value, true);
4669 * <span> icon </span>
4670 * <span> text </span>
4671 * <span>badge </span>
4675 * @class Roo.bootstrap.NavSidebarItem
4676 * @extends Roo.bootstrap.NavItem
4677 * Bootstrap Navbar.NavSidebarItem class
4678 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4679 * {Boolean} open is the menu open
4680 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4681 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4682 * {String} buttonSize (sm|md|lg)the extra classes for the button
4683 * {Boolean} showArrow show arrow next to the text (default true)
4685 * Create a new Navbar Button
4686 * @param {Object} config The config object
4688 Roo.bootstrap.NavSidebarItem = function(config){
4689 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4694 * The raw click event for the entire grid.
4695 * @param {Roo.EventObject} e
4700 * Fires when the active item active state changes
4701 * @param {Roo.bootstrap.NavSidebarItem} this
4702 * @param {boolean} state the new state
4710 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4712 badgeWeight : 'default',
4718 buttonWeight : 'default',
4724 getAutoCreate : function(){
4729 href : this.href || '#',
4735 if(this.buttonView){
4738 href : this.href || '#',
4739 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4752 cfg.cls += ' active';
4755 if (this.disabled) {
4756 cfg.cls += ' disabled';
4759 cfg.cls += ' open x-open';
4762 if (this.glyphicon || this.icon) {
4763 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4764 a.cn.push({ tag : 'i', cls : c }) ;
4767 if(!this.buttonView){
4770 html : this.html || ''
4777 if (this.badge !== '') {
4778 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4784 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4787 a.cls += ' dropdown-toggle treeview' ;
4793 initEvents : function()
4795 if (typeof (this.menu) != 'undefined') {
4796 this.menu.parentType = this.xtype;
4797 this.menu.triggerEl = this.el;
4798 this.menu = this.addxtype(Roo.apply({}, this.menu));
4801 this.el.on('click', this.onClick, this);
4803 if(this.badge !== ''){
4804 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4809 onClick : function(e)
4816 if(this.preventDefault){
4820 this.fireEvent('click', this);
4823 disable : function()
4825 this.setDisabled(true);
4830 this.setDisabled(false);
4833 setDisabled : function(state)
4835 if(this.disabled == state){
4839 this.disabled = state;
4842 this.el.addClass('disabled');
4846 this.el.removeClass('disabled');
4851 setActive : function(state)
4853 if(this.active == state){
4857 this.active = state;
4860 this.el.addClass('active');
4864 this.el.removeClass('active');
4869 isActive: function ()
4874 setBadge : function(str)
4880 this.badgeEl.dom.innerHTML = str;
4897 * @class Roo.bootstrap.Row
4898 * @extends Roo.bootstrap.Component
4899 * Bootstrap Row class (contains columns...)
4903 * @param {Object} config The config object
4906 Roo.bootstrap.Row = function(config){
4907 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4910 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4912 getAutoCreate : function(){
4931 * @class Roo.bootstrap.Element
4932 * @extends Roo.bootstrap.Component
4933 * Bootstrap Element class
4934 * @cfg {String} html contents of the element
4935 * @cfg {String} tag tag of the element
4936 * @cfg {String} cls class of the element
4937 * @cfg {Boolean} preventDefault (true|false) default false
4938 * @cfg {Boolean} clickable (true|false) default false
4941 * Create a new Element
4942 * @param {Object} config The config object
4945 Roo.bootstrap.Element = function(config){
4946 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4952 * When a element is chick
4953 * @param {Roo.bootstrap.Element} this
4954 * @param {Roo.EventObject} e
4960 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4965 preventDefault: false,
4968 getAutoCreate : function(){
4972 // cls: this.cls, double assign in parent class Component.js :: onRender
4979 initEvents: function()
4981 Roo.bootstrap.Element.superclass.initEvents.call(this);
4984 this.el.on('click', this.onClick, this);
4989 onClick : function(e)
4991 if(this.preventDefault){
4995 this.fireEvent('click', this, e);
4998 getValue : function()
5000 return this.el.dom.innerHTML;
5003 setValue : function(value)
5005 this.el.dom.innerHTML = value;
5020 * @class Roo.bootstrap.Pagination
5021 * @extends Roo.bootstrap.Component
5022 * Bootstrap Pagination class
5023 * @cfg {String} size xs | sm | md | lg
5024 * @cfg {Boolean} inverse false | true
5027 * Create a new Pagination
5028 * @param {Object} config The config object
5031 Roo.bootstrap.Pagination = function(config){
5032 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5035 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5041 getAutoCreate : function(){
5047 cfg.cls += ' inverse';
5053 cfg.cls += " " + this.cls;
5071 * @class Roo.bootstrap.PaginationItem
5072 * @extends Roo.bootstrap.Component
5073 * Bootstrap PaginationItem class
5074 * @cfg {String} html text
5075 * @cfg {String} href the link
5076 * @cfg {Boolean} preventDefault (true | false) default true
5077 * @cfg {Boolean} active (true | false) default false
5078 * @cfg {Boolean} disabled default false
5082 * Create a new PaginationItem
5083 * @param {Object} config The config object
5087 Roo.bootstrap.PaginationItem = function(config){
5088 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5093 * The raw click event for the entire grid.
5094 * @param {Roo.EventObject} e
5100 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5104 preventDefault: true,
5109 getAutoCreate : function(){
5115 href : this.href ? this.href : '#',
5116 html : this.html ? this.html : ''
5126 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5130 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5136 initEvents: function() {
5138 this.el.on('click', this.onClick, this);
5141 onClick : function(e)
5143 Roo.log('PaginationItem on click ');
5144 if(this.preventDefault){
5152 this.fireEvent('click', this, e);
5168 * @class Roo.bootstrap.Slider
5169 * @extends Roo.bootstrap.Component
5170 * Bootstrap Slider class
5173 * Create a new Slider
5174 * @param {Object} config The config object
5177 Roo.bootstrap.Slider = function(config){
5178 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5181 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5183 getAutoCreate : function(){
5187 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5191 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5203 * Ext JS Library 1.1.1
5204 * Copyright(c) 2006-2007, Ext JS, LLC.
5206 * Originally Released Under LGPL - original licence link has changed is not relivant.
5209 * <script type="text/javascript">
5214 * @class Roo.grid.ColumnModel
5215 * @extends Roo.util.Observable
5216 * This is the default implementation of a ColumnModel used by the Grid. It defines
5217 * the columns in the grid.
5220 var colModel = new Roo.grid.ColumnModel([
5221 {header: "Ticker", width: 60, sortable: true, locked: true},
5222 {header: "Company Name", width: 150, sortable: true},
5223 {header: "Market Cap.", width: 100, sortable: true},
5224 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5225 {header: "Employees", width: 100, sortable: true, resizable: false}
5230 * The config options listed for this class are options which may appear in each
5231 * individual column definition.
5232 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5234 * @param {Object} config An Array of column config objects. See this class's
5235 * config objects for details.
5237 Roo.grid.ColumnModel = function(config){
5239 * The config passed into the constructor
5241 this.config = config;
5244 // if no id, create one
5245 // if the column does not have a dataIndex mapping,
5246 // map it to the order it is in the config
5247 for(var i = 0, len = config.length; i < len; i++){
5249 if(typeof c.dataIndex == "undefined"){
5252 if(typeof c.renderer == "string"){
5253 c.renderer = Roo.util.Format[c.renderer];
5255 if(typeof c.id == "undefined"){
5258 if(c.editor && c.editor.xtype){
5259 c.editor = Roo.factory(c.editor, Roo.grid);
5261 if(c.editor && c.editor.isFormField){
5262 c.editor = new Roo.grid.GridEditor(c.editor);
5264 this.lookup[c.id] = c;
5268 * The width of columns which have no width specified (defaults to 100)
5271 this.defaultWidth = 100;
5274 * Default sortable of columns which have no sortable specified (defaults to false)
5277 this.defaultSortable = false;
5281 * @event widthchange
5282 * Fires when the width of a column changes.
5283 * @param {ColumnModel} this
5284 * @param {Number} columnIndex The column index
5285 * @param {Number} newWidth The new width
5287 "widthchange": true,
5289 * @event headerchange
5290 * Fires when the text of a header changes.
5291 * @param {ColumnModel} this
5292 * @param {Number} columnIndex The column index
5293 * @param {Number} newText The new header text
5295 "headerchange": true,
5297 * @event hiddenchange
5298 * Fires when a column is hidden or "unhidden".
5299 * @param {ColumnModel} this
5300 * @param {Number} columnIndex The column index
5301 * @param {Boolean} hidden true if hidden, false otherwise
5303 "hiddenchange": true,
5305 * @event columnmoved
5306 * Fires when a column is moved.
5307 * @param {ColumnModel} this
5308 * @param {Number} oldIndex
5309 * @param {Number} newIndex
5311 "columnmoved" : true,
5313 * @event columlockchange
5314 * Fires when a column's locked state is changed
5315 * @param {ColumnModel} this
5316 * @param {Number} colIndex
5317 * @param {Boolean} locked true if locked
5319 "columnlockchange" : true
5321 Roo.grid.ColumnModel.superclass.constructor.call(this);
5323 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5325 * @cfg {String} header The header text to display in the Grid view.
5328 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5329 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5330 * specified, the column's index is used as an index into the Record's data Array.
5333 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5334 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5337 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5338 * Defaults to the value of the {@link #defaultSortable} property.
5339 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5342 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5345 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5348 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5351 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5354 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5355 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5356 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5357 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5360 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5363 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5366 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5369 * @cfg {String} cursor (Optional)
5372 * @cfg {String} tooltip (Optional)
5375 * @cfg {Number} xs (Optional)
5378 * @cfg {Number} sm (Optional)
5381 * @cfg {Number} md (Optional)
5384 * @cfg {Number} lg (Optional)
5387 * Returns the id of the column at the specified index.
5388 * @param {Number} index The column index
5389 * @return {String} the id
5391 getColumnId : function(index){
5392 return this.config[index].id;
5396 * Returns the column for a specified id.
5397 * @param {String} id The column id
5398 * @return {Object} the column
5400 getColumnById : function(id){
5401 return this.lookup[id];
5406 * Returns the column for a specified dataIndex.
5407 * @param {String} dataIndex The column dataIndex
5408 * @return {Object|Boolean} the column or false if not found
5410 getColumnByDataIndex: function(dataIndex){
5411 var index = this.findColumnIndex(dataIndex);
5412 return index > -1 ? this.config[index] : false;
5416 * Returns the index for a specified column id.
5417 * @param {String} id The column id
5418 * @return {Number} the index, or -1 if not found
5420 getIndexById : function(id){
5421 for(var i = 0, len = this.config.length; i < len; i++){
5422 if(this.config[i].id == id){
5430 * Returns the index for a specified column dataIndex.
5431 * @param {String} dataIndex The column dataIndex
5432 * @return {Number} the index, or -1 if not found
5435 findColumnIndex : function(dataIndex){
5436 for(var i = 0, len = this.config.length; i < len; i++){
5437 if(this.config[i].dataIndex == dataIndex){
5445 moveColumn : function(oldIndex, newIndex){
5446 var c = this.config[oldIndex];
5447 this.config.splice(oldIndex, 1);
5448 this.config.splice(newIndex, 0, c);
5449 this.dataMap = null;
5450 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5453 isLocked : function(colIndex){
5454 return this.config[colIndex].locked === true;
5457 setLocked : function(colIndex, value, suppressEvent){
5458 if(this.isLocked(colIndex) == value){
5461 this.config[colIndex].locked = value;
5463 this.fireEvent("columnlockchange", this, colIndex, value);
5467 getTotalLockedWidth : function(){
5469 for(var i = 0; i < this.config.length; i++){
5470 if(this.isLocked(i) && !this.isHidden(i)){
5471 this.totalWidth += this.getColumnWidth(i);
5477 getLockedCount : function(){
5478 for(var i = 0, len = this.config.length; i < len; i++){
5479 if(!this.isLocked(i)){
5484 return this.config.length;
5488 * Returns the number of columns.
5491 getColumnCount : function(visibleOnly){
5492 if(visibleOnly === true){
5494 for(var i = 0, len = this.config.length; i < len; i++){
5495 if(!this.isHidden(i)){
5501 return this.config.length;
5505 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5506 * @param {Function} fn
5507 * @param {Object} scope (optional)
5508 * @return {Array} result
5510 getColumnsBy : function(fn, scope){
5512 for(var i = 0, len = this.config.length; i < len; i++){
5513 var c = this.config[i];
5514 if(fn.call(scope||this, c, i) === true){
5522 * Returns true if the specified column is sortable.
5523 * @param {Number} col The column index
5526 isSortable : function(col){
5527 if(typeof this.config[col].sortable == "undefined"){
5528 return this.defaultSortable;
5530 return this.config[col].sortable;
5534 * Returns the rendering (formatting) function defined for the column.
5535 * @param {Number} col The column index.
5536 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5538 getRenderer : function(col){
5539 if(!this.config[col].renderer){
5540 return Roo.grid.ColumnModel.defaultRenderer;
5542 return this.config[col].renderer;
5546 * Sets the rendering (formatting) function for a column.
5547 * @param {Number} col The column index
5548 * @param {Function} fn The function to use to process the cell's raw data
5549 * to return HTML markup for the grid view. The render function is called with
5550 * the following parameters:<ul>
5551 * <li>Data value.</li>
5552 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5553 * <li>css A CSS style string to apply to the table cell.</li>
5554 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5555 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5556 * <li>Row index</li>
5557 * <li>Column index</li>
5558 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5560 setRenderer : function(col, fn){
5561 this.config[col].renderer = fn;
5565 * Returns the width for the specified column.
5566 * @param {Number} col The column index
5569 getColumnWidth : function(col){
5570 return this.config[col].width * 1 || this.defaultWidth;
5574 * Sets the width for a column.
5575 * @param {Number} col The column index
5576 * @param {Number} width The new width
5578 setColumnWidth : function(col, width, suppressEvent){
5579 this.config[col].width = width;
5580 this.totalWidth = null;
5582 this.fireEvent("widthchange", this, col, width);
5587 * Returns the total width of all columns.
5588 * @param {Boolean} includeHidden True to include hidden column widths
5591 getTotalWidth : function(includeHidden){
5592 if(!this.totalWidth){
5593 this.totalWidth = 0;
5594 for(var i = 0, len = this.config.length; i < len; i++){
5595 if(includeHidden || !this.isHidden(i)){
5596 this.totalWidth += this.getColumnWidth(i);
5600 return this.totalWidth;
5604 * Returns the header for the specified column.
5605 * @param {Number} col The column index
5608 getColumnHeader : function(col){
5609 return this.config[col].header;
5613 * Sets the header for a column.
5614 * @param {Number} col The column index
5615 * @param {String} header The new header
5617 setColumnHeader : function(col, header){
5618 this.config[col].header = header;
5619 this.fireEvent("headerchange", this, col, header);
5623 * Returns the tooltip for the specified column.
5624 * @param {Number} col The column index
5627 getColumnTooltip : function(col){
5628 return this.config[col].tooltip;
5631 * Sets the tooltip for a column.
5632 * @param {Number} col The column index
5633 * @param {String} tooltip The new tooltip
5635 setColumnTooltip : function(col, tooltip){
5636 this.config[col].tooltip = tooltip;
5640 * Returns the dataIndex for the specified column.
5641 * @param {Number} col The column index
5644 getDataIndex : function(col){
5645 return this.config[col].dataIndex;
5649 * Sets the dataIndex for a column.
5650 * @param {Number} col The column index
5651 * @param {Number} dataIndex The new dataIndex
5653 setDataIndex : function(col, dataIndex){
5654 this.config[col].dataIndex = dataIndex;
5660 * Returns true if the cell is editable.
5661 * @param {Number} colIndex The column index
5662 * @param {Number} rowIndex The row index - this is nto actually used..?
5665 isCellEditable : function(colIndex, rowIndex){
5666 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5670 * Returns the editor defined for the cell/column.
5671 * return false or null to disable editing.
5672 * @param {Number} colIndex The column index
5673 * @param {Number} rowIndex The row index
5676 getCellEditor : function(colIndex, rowIndex){
5677 return this.config[colIndex].editor;
5681 * Sets if a column is editable.
5682 * @param {Number} col The column index
5683 * @param {Boolean} editable True if the column is editable
5685 setEditable : function(col, editable){
5686 this.config[col].editable = editable;
5691 * Returns true if the column is hidden.
5692 * @param {Number} colIndex The column index
5695 isHidden : function(colIndex){
5696 return this.config[colIndex].hidden;
5701 * Returns true if the column width cannot be changed
5703 isFixed : function(colIndex){
5704 return this.config[colIndex].fixed;
5708 * Returns true if the column can be resized
5711 isResizable : function(colIndex){
5712 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5715 * Sets if a column is hidden.
5716 * @param {Number} colIndex The column index
5717 * @param {Boolean} hidden True if the column is hidden
5719 setHidden : function(colIndex, hidden){
5720 this.config[colIndex].hidden = hidden;
5721 this.totalWidth = null;
5722 this.fireEvent("hiddenchange", this, colIndex, hidden);
5726 * Sets the editor for a column.
5727 * @param {Number} col The column index
5728 * @param {Object} editor The editor object
5730 setEditor : function(col, editor){
5731 this.config[col].editor = editor;
5735 Roo.grid.ColumnModel.defaultRenderer = function(value)
5737 if(typeof value == "object") {
5740 if(typeof value == "string" && value.length < 1){
5744 return String.format("{0}", value);
5747 // Alias for backwards compatibility
5748 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5751 * Ext JS Library 1.1.1
5752 * Copyright(c) 2006-2007, Ext JS, LLC.
5754 * Originally Released Under LGPL - original licence link has changed is not relivant.
5757 * <script type="text/javascript">
5761 * @class Roo.LoadMask
5762 * A simple utility class for generically masking elements while loading data. If the element being masked has
5763 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5764 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5765 * element's UpdateManager load indicator and will be destroyed after the initial load.
5767 * Create a new LoadMask
5768 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5769 * @param {Object} config The config object
5771 Roo.LoadMask = function(el, config){
5772 this.el = Roo.get(el);
5773 Roo.apply(this, config);
5775 this.store.on('beforeload', this.onBeforeLoad, this);
5776 this.store.on('load', this.onLoad, this);
5777 this.store.on('loadexception', this.onLoadException, this);
5778 this.removeMask = false;
5780 var um = this.el.getUpdateManager();
5781 um.showLoadIndicator = false; // disable the default indicator
5782 um.on('beforeupdate', this.onBeforeLoad, this);
5783 um.on('update', this.onLoad, this);
5784 um.on('failure', this.onLoad, this);
5785 this.removeMask = true;
5789 Roo.LoadMask.prototype = {
5791 * @cfg {Boolean} removeMask
5792 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5793 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5797 * The text to display in a centered loading message box (defaults to 'Loading...')
5801 * @cfg {String} msgCls
5802 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5804 msgCls : 'x-mask-loading',
5807 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5813 * Disables the mask to prevent it from being displayed
5815 disable : function(){
5816 this.disabled = true;
5820 * Enables the mask so that it can be displayed
5822 enable : function(){
5823 this.disabled = false;
5826 onLoadException : function()
5830 if (typeof(arguments[3]) != 'undefined') {
5831 Roo.MessageBox.alert("Error loading",arguments[3]);
5835 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5836 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5843 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5848 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5852 onBeforeLoad : function(){
5854 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5859 destroy : function(){
5861 this.store.un('beforeload', this.onBeforeLoad, this);
5862 this.store.un('load', this.onLoad, this);
5863 this.store.un('loadexception', this.onLoadException, this);
5865 var um = this.el.getUpdateManager();
5866 um.un('beforeupdate', this.onBeforeLoad, this);
5867 um.un('update', this.onLoad, this);
5868 um.un('failure', this.onLoad, this);
5879 * @class Roo.bootstrap.Table
5880 * @extends Roo.bootstrap.Component
5881 * Bootstrap Table class
5882 * @cfg {String} cls table class
5883 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5884 * @cfg {String} bgcolor Specifies the background color for a table
5885 * @cfg {Number} border Specifies whether the table cells should have borders or not
5886 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5887 * @cfg {Number} cellspacing Specifies the space between cells
5888 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5889 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5890 * @cfg {String} sortable Specifies that the table should be sortable
5891 * @cfg {String} summary Specifies a summary of the content of a table
5892 * @cfg {Number} width Specifies the width of a table
5893 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5895 * @cfg {boolean} striped Should the rows be alternative striped
5896 * @cfg {boolean} bordered Add borders to the table
5897 * @cfg {boolean} hover Add hover highlighting
5898 * @cfg {boolean} condensed Format condensed
5899 * @cfg {boolean} responsive Format condensed
5900 * @cfg {Boolean} loadMask (true|false) default false
5901 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5902 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5903 * @cfg {Boolean} rowSelection (true|false) default false
5904 * @cfg {Boolean} cellSelection (true|false) default false
5905 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5906 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5907 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5908 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5912 * Create a new Table
5913 * @param {Object} config The config object
5916 Roo.bootstrap.Table = function(config){
5917 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5922 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5923 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5924 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5925 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5927 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5929 this.sm.grid = this;
5930 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5931 this.sm = this.selModel;
5932 this.sm.xmodule = this.xmodule || false;
5935 if (this.cm && typeof(this.cm.config) == 'undefined') {
5936 this.colModel = new Roo.grid.ColumnModel(this.cm);
5937 this.cm = this.colModel;
5938 this.cm.xmodule = this.xmodule || false;
5941 this.store= Roo.factory(this.store, Roo.data);
5942 this.ds = this.store;
5943 this.ds.xmodule = this.xmodule || false;
5946 if (this.footer && this.store) {
5947 this.footer.dataSource = this.ds;
5948 this.footer = Roo.factory(this.footer);
5955 * Fires when a cell is clicked
5956 * @param {Roo.bootstrap.Table} this
5957 * @param {Roo.Element} el
5958 * @param {Number} rowIndex
5959 * @param {Number} columnIndex
5960 * @param {Roo.EventObject} e
5964 * @event celldblclick
5965 * Fires when a cell is double clicked
5966 * @param {Roo.bootstrap.Table} this
5967 * @param {Roo.Element} el
5968 * @param {Number} rowIndex
5969 * @param {Number} columnIndex
5970 * @param {Roo.EventObject} e
5972 "celldblclick" : true,
5975 * Fires when a row is clicked
5976 * @param {Roo.bootstrap.Table} this
5977 * @param {Roo.Element} el
5978 * @param {Number} rowIndex
5979 * @param {Roo.EventObject} e
5983 * @event rowdblclick
5984 * Fires when a row is double clicked
5985 * @param {Roo.bootstrap.Table} this
5986 * @param {Roo.Element} el
5987 * @param {Number} rowIndex
5988 * @param {Roo.EventObject} e
5990 "rowdblclick" : true,
5993 * Fires when a mouseover occur
5994 * @param {Roo.bootstrap.Table} this
5995 * @param {Roo.Element} el
5996 * @param {Number} rowIndex
5997 * @param {Number} columnIndex
5998 * @param {Roo.EventObject} e
6003 * Fires when a mouseout occur
6004 * @param {Roo.bootstrap.Table} this
6005 * @param {Roo.Element} el
6006 * @param {Number} rowIndex
6007 * @param {Number} columnIndex
6008 * @param {Roo.EventObject} e
6013 * Fires when a row is rendered, so you can change add a style to it.
6014 * @param {Roo.bootstrap.Table} this
6015 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6019 * @event rowsrendered
6020 * Fires when all the rows have been rendered
6021 * @param {Roo.bootstrap.Table} this
6023 'rowsrendered' : true,
6025 * @event contextmenu
6026 * The raw contextmenu event for the entire grid.
6027 * @param {Roo.EventObject} e
6029 "contextmenu" : true,
6031 * @event rowcontextmenu
6032 * Fires when a row is right clicked
6033 * @param {Roo.bootstrap.Table} this
6034 * @param {Number} rowIndex
6035 * @param {Roo.EventObject} e
6037 "rowcontextmenu" : true,
6039 * @event cellcontextmenu
6040 * Fires when a cell is right clicked
6041 * @param {Roo.bootstrap.Table} this
6042 * @param {Number} rowIndex
6043 * @param {Number} cellIndex
6044 * @param {Roo.EventObject} e
6046 "cellcontextmenu" : true,
6048 * @event headercontextmenu
6049 * Fires when a header is right clicked
6050 * @param {Roo.bootstrap.Table} this
6051 * @param {Number} columnIndex
6052 * @param {Roo.EventObject} e
6054 "headercontextmenu" : true
6058 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6084 rowSelection : false,
6085 cellSelection : false,
6088 // Roo.Element - the tbody
6090 // Roo.Element - thead element
6093 container: false, // used by gridpanel...
6099 auto_hide_footer : false,
6101 getAutoCreate : function()
6103 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6110 if (this.scrollBody) {
6111 cfg.cls += ' table-body-fixed';
6114 cfg.cls += ' table-striped';
6118 cfg.cls += ' table-hover';
6120 if (this.bordered) {
6121 cfg.cls += ' table-bordered';
6123 if (this.condensed) {
6124 cfg.cls += ' table-condensed';
6126 if (this.responsive) {
6127 cfg.cls += ' table-responsive';
6131 cfg.cls+= ' ' +this.cls;
6134 // this lot should be simplifed...
6147 ].forEach(function(k) {
6155 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6158 if(this.store || this.cm){
6159 if(this.headerShow){
6160 cfg.cn.push(this.renderHeader());
6163 cfg.cn.push(this.renderBody());
6165 if(this.footerShow){
6166 cfg.cn.push(this.renderFooter());
6168 // where does this come from?
6169 //cfg.cls+= ' TableGrid';
6172 return { cn : [ cfg ] };
6175 initEvents : function()
6177 if(!this.store || !this.cm){
6180 if (this.selModel) {
6181 this.selModel.initEvents();
6185 //Roo.log('initEvents with ds!!!!');
6187 this.mainBody = this.el.select('tbody', true).first();
6188 this.mainHead = this.el.select('thead', true).first();
6189 this.mainFoot = this.el.select('tfoot', true).first();
6195 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6196 e.on('click', _this.sort, _this);
6199 this.mainBody.on("click", this.onClick, this);
6200 this.mainBody.on("dblclick", this.onDblClick, this);
6202 // why is this done????? = it breaks dialogs??
6203 //this.parent().el.setStyle('position', 'relative');
6207 this.footer.parentId = this.id;
6208 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6211 this.el.select('tfoot tr td').first().addClass('hide');
6216 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6219 this.store.on('load', this.onLoad, this);
6220 this.store.on('beforeload', this.onBeforeLoad, this);
6221 this.store.on('update', this.onUpdate, this);
6222 this.store.on('add', this.onAdd, this);
6223 this.store.on("clear", this.clear, this);
6225 this.el.on("contextmenu", this.onContextMenu, this);
6227 this.mainBody.on('scroll', this.onBodyScroll, this);
6229 this.cm.on("headerchange", this.onHeaderChange, this);
6231 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6235 onContextMenu : function(e, t)
6237 this.processEvent("contextmenu", e);
6240 processEvent : function(name, e)
6242 if (name != 'touchstart' ) {
6243 this.fireEvent(name, e);
6246 var t = e.getTarget();
6248 var cell = Roo.get(t);
6254 if(cell.findParent('tfoot', false, true)){
6258 if(cell.findParent('thead', false, true)){
6260 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6261 cell = Roo.get(t).findParent('th', false, true);
6263 Roo.log("failed to find th in thead?");
6264 Roo.log(e.getTarget());
6269 var cellIndex = cell.dom.cellIndex;
6271 var ename = name == 'touchstart' ? 'click' : name;
6272 this.fireEvent("header" + ename, this, cellIndex, e);
6277 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6278 cell = Roo.get(t).findParent('td', false, true);
6280 Roo.log("failed to find th in tbody?");
6281 Roo.log(e.getTarget());
6286 var row = cell.findParent('tr', false, true);
6287 var cellIndex = cell.dom.cellIndex;
6288 var rowIndex = row.dom.rowIndex - 1;
6292 this.fireEvent("row" + name, this, rowIndex, e);
6296 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6302 onMouseover : function(e, el)
6304 var cell = Roo.get(el);
6310 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6311 cell = cell.findParent('td', false, true);
6314 var row = cell.findParent('tr', false, true);
6315 var cellIndex = cell.dom.cellIndex;
6316 var rowIndex = row.dom.rowIndex - 1; // start from 0
6318 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6322 onMouseout : function(e, el)
6324 var cell = Roo.get(el);
6330 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331 cell = cell.findParent('td', false, true);
6334 var row = cell.findParent('tr', false, true);
6335 var cellIndex = cell.dom.cellIndex;
6336 var rowIndex = row.dom.rowIndex - 1; // start from 0
6338 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6342 onClick : function(e, el)
6344 var cell = Roo.get(el);
6346 if(!cell || (!this.cellSelection && !this.rowSelection)){
6350 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6351 cell = cell.findParent('td', false, true);
6354 if(!cell || typeof(cell) == 'undefined'){
6358 var row = cell.findParent('tr', false, true);
6360 if(!row || typeof(row) == 'undefined'){
6364 var cellIndex = cell.dom.cellIndex;
6365 var rowIndex = this.getRowIndex(row);
6367 // why??? - should these not be based on SelectionModel?
6368 if(this.cellSelection){
6369 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6372 if(this.rowSelection){
6373 this.fireEvent('rowclick', this, row, rowIndex, e);
6379 onDblClick : function(e,el)
6381 var cell = Roo.get(el);
6383 if(!cell || (!this.cellSelection && !this.rowSelection)){
6387 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6388 cell = cell.findParent('td', false, true);
6391 if(!cell || typeof(cell) == 'undefined'){
6395 var row = cell.findParent('tr', false, true);
6397 if(!row || typeof(row) == 'undefined'){
6401 var cellIndex = cell.dom.cellIndex;
6402 var rowIndex = this.getRowIndex(row);
6404 if(this.cellSelection){
6405 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6408 if(this.rowSelection){
6409 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6413 sort : function(e,el)
6415 var col = Roo.get(el);
6417 if(!col.hasClass('sortable')){
6421 var sort = col.attr('sort');
6424 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6428 this.store.sortInfo = {field : sort, direction : dir};
6431 Roo.log("calling footer first");
6432 this.footer.onClick('first');
6435 this.store.load({ params : { start : 0 } });
6439 renderHeader : function()
6447 this.totalWidth = 0;
6449 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6451 var config = cm.config[i];
6455 cls : 'x-hcol-' + i,
6457 html: cm.getColumnHeader(i)
6462 if(typeof(config.sortable) != 'undefined' && config.sortable){
6464 c.html = '<i class="glyphicon"></i>' + c.html;
6467 if(typeof(config.lgHeader) != 'undefined'){
6468 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6471 if(typeof(config.mdHeader) != 'undefined'){
6472 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6475 if(typeof(config.smHeader) != 'undefined'){
6476 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6479 if(typeof(config.xsHeader) != 'undefined'){
6480 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6487 if(typeof(config.tooltip) != 'undefined'){
6488 c.tooltip = config.tooltip;
6491 if(typeof(config.colspan) != 'undefined'){
6492 c.colspan = config.colspan;
6495 if(typeof(config.hidden) != 'undefined' && config.hidden){
6496 c.style += ' display:none;';
6499 if(typeof(config.dataIndex) != 'undefined'){
6500 c.sort = config.dataIndex;
6505 if(typeof(config.align) != 'undefined' && config.align.length){
6506 c.style += ' text-align:' + config.align + ';';
6509 if(typeof(config.width) != 'undefined'){
6510 c.style += ' width:' + config.width + 'px;';
6511 this.totalWidth += config.width;
6513 this.totalWidth += 100; // assume minimum of 100 per column?
6516 if(typeof(config.cls) != 'undefined'){
6517 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6520 ['xs','sm','md','lg'].map(function(size){
6522 if(typeof(config[size]) == 'undefined'){
6526 if (!config[size]) { // 0 = hidden
6527 c.cls += ' hidden-' + size;
6531 c.cls += ' col-' + size + '-' + config[size];
6541 renderBody : function()
6551 colspan : this.cm.getColumnCount()
6561 renderFooter : function()
6571 colspan : this.cm.getColumnCount()
6585 // Roo.log('ds onload');
6590 var ds = this.store;
6592 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6593 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6594 if (_this.store.sortInfo) {
6596 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6597 e.select('i', true).addClass(['glyphicon-arrow-up']);
6600 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6601 e.select('i', true).addClass(['glyphicon-arrow-down']);
6606 var tbody = this.mainBody;
6608 if(ds.getCount() > 0){
6609 ds.data.each(function(d,rowIndex){
6610 var row = this.renderRow(cm, ds, rowIndex);
6612 tbody.createChild(row);
6616 if(row.cellObjects.length){
6617 Roo.each(row.cellObjects, function(r){
6618 _this.renderCellObject(r);
6625 var tfoot = this.el.select('tfoot', true).first();
6627 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6629 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6631 var total = this.ds.getTotalCount();
6633 if(this.footer.pageSize < total){
6634 this.mainFoot.show();
6638 Roo.each(this.el.select('tbody td', true).elements, function(e){
6639 e.on('mouseover', _this.onMouseover, _this);
6642 Roo.each(this.el.select('tbody td', true).elements, function(e){
6643 e.on('mouseout', _this.onMouseout, _this);
6645 this.fireEvent('rowsrendered', this);
6651 onUpdate : function(ds,record)
6653 this.refreshRow(record);
6657 onRemove : function(ds, record, index, isUpdate){
6658 if(isUpdate !== true){
6659 this.fireEvent("beforerowremoved", this, index, record);
6661 var bt = this.mainBody.dom;
6663 var rows = this.el.select('tbody > tr', true).elements;
6665 if(typeof(rows[index]) != 'undefined'){
6666 bt.removeChild(rows[index].dom);
6669 // if(bt.rows[index]){
6670 // bt.removeChild(bt.rows[index]);
6673 if(isUpdate !== true){
6674 //this.stripeRows(index);
6675 //this.syncRowHeights(index, index);
6677 this.fireEvent("rowremoved", this, index, record);
6681 onAdd : function(ds, records, rowIndex)
6683 //Roo.log('on Add called');
6684 // - note this does not handle multiple adding very well..
6685 var bt = this.mainBody.dom;
6686 for (var i =0 ; i < records.length;i++) {
6687 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6688 //Roo.log(records[i]);
6689 //Roo.log(this.store.getAt(rowIndex+i));
6690 this.insertRow(this.store, rowIndex + i, false);
6697 refreshRow : function(record){
6698 var ds = this.store, index;
6699 if(typeof record == 'number'){
6701 record = ds.getAt(index);
6703 index = ds.indexOf(record);
6705 this.insertRow(ds, index, true);
6707 this.onRemove(ds, record, index+1, true);
6709 //this.syncRowHeights(index, index);
6711 this.fireEvent("rowupdated", this, index, record);
6714 insertRow : function(dm, rowIndex, isUpdate){
6717 this.fireEvent("beforerowsinserted", this, rowIndex);
6719 //var s = this.getScrollState();
6720 var row = this.renderRow(this.cm, this.store, rowIndex);
6721 // insert before rowIndex..
6722 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6726 if(row.cellObjects.length){
6727 Roo.each(row.cellObjects, function(r){
6728 _this.renderCellObject(r);
6733 this.fireEvent("rowsinserted", this, rowIndex);
6734 //this.syncRowHeights(firstRow, lastRow);
6735 //this.stripeRows(firstRow);
6742 getRowDom : function(rowIndex)
6744 var rows = this.el.select('tbody > tr', true).elements;
6746 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6749 // returns the object tree for a tr..
6752 renderRow : function(cm, ds, rowIndex)
6754 var d = ds.getAt(rowIndex);
6758 cls : 'x-row-' + rowIndex,
6762 var cellObjects = [];
6764 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6765 var config = cm.config[i];
6767 var renderer = cm.getRenderer(i);
6771 if(typeof(renderer) !== 'undefined'){
6772 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6774 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6775 // and are rendered into the cells after the row is rendered - using the id for the element.
6777 if(typeof(value) === 'object'){
6787 rowIndex : rowIndex,
6792 this.fireEvent('rowclass', this, rowcfg);
6796 cls : rowcfg.rowClass + ' x-col-' + i,
6798 html: (typeof(value) === 'object') ? '' : value
6805 if(typeof(config.colspan) != 'undefined'){
6806 td.colspan = config.colspan;
6809 if(typeof(config.hidden) != 'undefined' && config.hidden){
6810 td.style += ' display:none;';
6813 if(typeof(config.align) != 'undefined' && config.align.length){
6814 td.style += ' text-align:' + config.align + ';';
6816 if(typeof(config.valign) != 'undefined' && config.valign.length){
6817 td.style += ' vertical-align:' + config.valign + ';';
6820 if(typeof(config.width) != 'undefined'){
6821 td.style += ' width:' + config.width + 'px;';
6824 if(typeof(config.cursor) != 'undefined'){
6825 td.style += ' cursor:' + config.cursor + ';';
6828 if(typeof(config.cls) != 'undefined'){
6829 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6832 ['xs','sm','md','lg'].map(function(size){
6834 if(typeof(config[size]) == 'undefined'){
6838 if (!config[size]) { // 0 = hidden
6839 td.cls += ' hidden-' + size;
6843 td.cls += ' col-' + size + '-' + config[size];
6851 row.cellObjects = cellObjects;
6859 onBeforeLoad : function()
6868 this.el.select('tbody', true).first().dom.innerHTML = '';
6871 * Show or hide a row.
6872 * @param {Number} rowIndex to show or hide
6873 * @param {Boolean} state hide
6875 setRowVisibility : function(rowIndex, state)
6877 var bt = this.mainBody.dom;
6879 var rows = this.el.select('tbody > tr', true).elements;
6881 if(typeof(rows[rowIndex]) == 'undefined'){
6884 rows[rowIndex].dom.style.display = state ? '' : 'none';
6888 getSelectionModel : function(){
6890 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6892 return this.selModel;
6895 * Render the Roo.bootstrap object from renderder
6897 renderCellObject : function(r)
6901 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6903 var t = r.cfg.render(r.container);
6906 Roo.each(r.cfg.cn, function(c){
6908 container: t.getChildContainer(),
6911 _this.renderCellObject(child);
6916 getRowIndex : function(row)
6920 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6931 * Returns the grid's underlying element = used by panel.Grid
6932 * @return {Element} The element
6934 getGridEl : function(){
6938 * Forces a resize - used by panel.Grid
6939 * @return {Element} The element
6941 autoSize : function()
6943 //var ctr = Roo.get(this.container.dom.parentElement);
6944 var ctr = Roo.get(this.el.dom);
6946 var thd = this.getGridEl().select('thead',true).first();
6947 var tbd = this.getGridEl().select('tbody', true).first();
6948 var tfd = this.getGridEl().select('tfoot', true).first();
6950 var cw = ctr.getWidth();
6954 tbd.setSize(ctr.getWidth(),
6955 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6957 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6960 cw = Math.max(cw, this.totalWidth);
6961 this.getGridEl().select('tr',true).setWidth(cw);
6962 // resize 'expandable coloumn?
6964 return; // we doe not have a view in this design..
6967 onBodyScroll: function()
6969 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6971 this.mainHead.setStyle({
6972 'position' : 'relative',
6973 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6979 var scrollHeight = this.mainBody.dom.scrollHeight;
6981 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6983 var height = this.mainBody.getHeight();
6985 if(scrollHeight - height == scrollTop) {
6987 var total = this.ds.getTotalCount();
6989 if(this.footer.cursor + this.footer.pageSize < total){
6991 this.footer.ds.load({
6993 start : this.footer.cursor + this.footer.pageSize,
6994 limit : this.footer.pageSize
7004 onHeaderChange : function()
7006 var header = this.renderHeader();
7007 var table = this.el.select('table', true).first();
7009 this.mainHead.remove();
7010 this.mainHead = table.createChild(header, this.mainBody, false);
7013 onHiddenChange : function(colModel, colIndex, hidden)
7015 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7016 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7018 this.CSS.updateRule(thSelector, "display", "");
7019 this.CSS.updateRule(tdSelector, "display", "");
7022 this.CSS.updateRule(thSelector, "display", "none");
7023 this.CSS.updateRule(tdSelector, "display", "none");
7026 this.onHeaderChange();
7043 * @class Roo.bootstrap.TableCell
7044 * @extends Roo.bootstrap.Component
7045 * Bootstrap TableCell class
7046 * @cfg {String} html cell contain text
7047 * @cfg {String} cls cell class
7048 * @cfg {String} tag cell tag (td|th) default td
7049 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7050 * @cfg {String} align Aligns the content in a cell
7051 * @cfg {String} axis Categorizes cells
7052 * @cfg {String} bgcolor Specifies the background color of a cell
7053 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7054 * @cfg {Number} colspan Specifies the number of columns a cell should span
7055 * @cfg {String} headers Specifies one or more header cells a cell is related to
7056 * @cfg {Number} height Sets the height of a cell
7057 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7058 * @cfg {Number} rowspan Sets the number of rows a cell should span
7059 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7060 * @cfg {String} valign Vertical aligns the content in a cell
7061 * @cfg {Number} width Specifies the width of a cell
7064 * Create a new TableCell
7065 * @param {Object} config The config object
7068 Roo.bootstrap.TableCell = function(config){
7069 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7072 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7092 getAutoCreate : function(){
7093 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7113 cfg.align=this.align
7119 cfg.bgcolor=this.bgcolor
7122 cfg.charoff=this.charoff
7125 cfg.colspan=this.colspan
7128 cfg.headers=this.headers
7131 cfg.height=this.height
7134 cfg.nowrap=this.nowrap
7137 cfg.rowspan=this.rowspan
7140 cfg.scope=this.scope
7143 cfg.valign=this.valign
7146 cfg.width=this.width
7165 * @class Roo.bootstrap.TableRow
7166 * @extends Roo.bootstrap.Component
7167 * Bootstrap TableRow class
7168 * @cfg {String} cls row class
7169 * @cfg {String} align Aligns the content in a table row
7170 * @cfg {String} bgcolor Specifies a background color for a table row
7171 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7172 * @cfg {String} valign Vertical aligns the content in a table row
7175 * Create a new TableRow
7176 * @param {Object} config The config object
7179 Roo.bootstrap.TableRow = function(config){
7180 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7183 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7191 getAutoCreate : function(){
7192 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7202 cfg.align = this.align;
7205 cfg.bgcolor = this.bgcolor;
7208 cfg.charoff = this.charoff;
7211 cfg.valign = this.valign;
7229 * @class Roo.bootstrap.TableBody
7230 * @extends Roo.bootstrap.Component
7231 * Bootstrap TableBody class
7232 * @cfg {String} cls element class
7233 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7234 * @cfg {String} align Aligns the content inside the element
7235 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7236 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7239 * Create a new TableBody
7240 * @param {Object} config The config object
7243 Roo.bootstrap.TableBody = function(config){
7244 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7247 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7255 getAutoCreate : function(){
7256 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7270 cfg.align = this.align;
7273 cfg.charoff = this.charoff;
7276 cfg.valign = this.valign;
7283 // initEvents : function()
7290 // this.store = Roo.factory(this.store, Roo.data);
7291 // this.store.on('load', this.onLoad, this);
7293 // this.store.load();
7297 // onLoad: function ()
7299 // this.fireEvent('load', this);
7309 * Ext JS Library 1.1.1
7310 * Copyright(c) 2006-2007, Ext JS, LLC.
7312 * Originally Released Under LGPL - original licence link has changed is not relivant.
7315 * <script type="text/javascript">
7318 // as we use this in bootstrap.
7319 Roo.namespace('Roo.form');
7321 * @class Roo.form.Action
7322 * Internal Class used to handle form actions
7324 * @param {Roo.form.BasicForm} el The form element or its id
7325 * @param {Object} config Configuration options
7330 // define the action interface
7331 Roo.form.Action = function(form, options){
7333 this.options = options || {};
7336 * Client Validation Failed
7339 Roo.form.Action.CLIENT_INVALID = 'client';
7341 * Server Validation Failed
7344 Roo.form.Action.SERVER_INVALID = 'server';
7346 * Connect to Server Failed
7349 Roo.form.Action.CONNECT_FAILURE = 'connect';
7351 * Reading Data from Server Failed
7354 Roo.form.Action.LOAD_FAILURE = 'load';
7356 Roo.form.Action.prototype = {
7358 failureType : undefined,
7359 response : undefined,
7363 run : function(options){
7368 success : function(response){
7373 handleResponse : function(response){
7377 // default connection failure
7378 failure : function(response){
7380 this.response = response;
7381 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7382 this.form.afterAction(this, false);
7385 processResponse : function(response){
7386 this.response = response;
7387 if(!response.responseText){
7390 this.result = this.handleResponse(response);
7394 // utility functions used internally
7395 getUrl : function(appendParams){
7396 var url = this.options.url || this.form.url || this.form.el.dom.action;
7398 var p = this.getParams();
7400 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7406 getMethod : function(){
7407 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7410 getParams : function(){
7411 var bp = this.form.baseParams;
7412 var p = this.options.params;
7414 if(typeof p == "object"){
7415 p = Roo.urlEncode(Roo.applyIf(p, bp));
7416 }else if(typeof p == 'string' && bp){
7417 p += '&' + Roo.urlEncode(bp);
7420 p = Roo.urlEncode(bp);
7425 createCallback : function(){
7427 success: this.success,
7428 failure: this.failure,
7430 timeout: (this.form.timeout*1000),
7431 upload: this.form.fileUpload ? this.success : undefined
7436 Roo.form.Action.Submit = function(form, options){
7437 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7440 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7443 haveProgress : false,
7444 uploadComplete : false,
7446 // uploadProgress indicator.
7447 uploadProgress : function()
7449 if (!this.form.progressUrl) {
7453 if (!this.haveProgress) {
7454 Roo.MessageBox.progress("Uploading", "Uploading");
7456 if (this.uploadComplete) {
7457 Roo.MessageBox.hide();
7461 this.haveProgress = true;
7463 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7465 var c = new Roo.data.Connection();
7467 url : this.form.progressUrl,
7472 success : function(req){
7473 //console.log(data);
7477 rdata = Roo.decode(req.responseText)
7479 Roo.log("Invalid data from server..");
7483 if (!rdata || !rdata.success) {
7485 Roo.MessageBox.alert(Roo.encode(rdata));
7488 var data = rdata.data;
7490 if (this.uploadComplete) {
7491 Roo.MessageBox.hide();
7496 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7497 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7500 this.uploadProgress.defer(2000,this);
7503 failure: function(data) {
7504 Roo.log('progress url failed ');
7515 // run get Values on the form, so it syncs any secondary forms.
7516 this.form.getValues();
7518 var o = this.options;
7519 var method = this.getMethod();
7520 var isPost = method == 'POST';
7521 if(o.clientValidation === false || this.form.isValid()){
7523 if (this.form.progressUrl) {
7524 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7525 (new Date() * 1) + '' + Math.random());
7530 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7531 form:this.form.el.dom,
7532 url:this.getUrl(!isPost),
7534 params:isPost ? this.getParams() : null,
7535 isUpload: this.form.fileUpload
7538 this.uploadProgress();
7540 }else if (o.clientValidation !== false){ // client validation failed
7541 this.failureType = Roo.form.Action.CLIENT_INVALID;
7542 this.form.afterAction(this, false);
7546 success : function(response)
7548 this.uploadComplete= true;
7549 if (this.haveProgress) {
7550 Roo.MessageBox.hide();
7554 var result = this.processResponse(response);
7555 if(result === true || result.success){
7556 this.form.afterAction(this, true);
7560 this.form.markInvalid(result.errors);
7561 this.failureType = Roo.form.Action.SERVER_INVALID;
7563 this.form.afterAction(this, false);
7565 failure : function(response)
7567 this.uploadComplete= true;
7568 if (this.haveProgress) {
7569 Roo.MessageBox.hide();
7572 this.response = response;
7573 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7574 this.form.afterAction(this, false);
7577 handleResponse : function(response){
7578 if(this.form.errorReader){
7579 var rs = this.form.errorReader.read(response);
7582 for(var i = 0, len = rs.records.length; i < len; i++) {
7583 var r = rs.records[i];
7587 if(errors.length < 1){
7591 success : rs.success,
7597 ret = Roo.decode(response.responseText);
7601 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7611 Roo.form.Action.Load = function(form, options){
7612 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7613 this.reader = this.form.reader;
7616 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7621 Roo.Ajax.request(Roo.apply(
7622 this.createCallback(), {
7623 method:this.getMethod(),
7624 url:this.getUrl(false),
7625 params:this.getParams()
7629 success : function(response){
7631 var result = this.processResponse(response);
7632 if(result === true || !result.success || !result.data){
7633 this.failureType = Roo.form.Action.LOAD_FAILURE;
7634 this.form.afterAction(this, false);
7637 this.form.clearInvalid();
7638 this.form.setValues(result.data);
7639 this.form.afterAction(this, true);
7642 handleResponse : function(response){
7643 if(this.form.reader){
7644 var rs = this.form.reader.read(response);
7645 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7647 success : rs.success,
7651 return Roo.decode(response.responseText);
7655 Roo.form.Action.ACTION_TYPES = {
7656 'load' : Roo.form.Action.Load,
7657 'submit' : Roo.form.Action.Submit
7666 * @class Roo.bootstrap.Form
7667 * @extends Roo.bootstrap.Component
7668 * Bootstrap Form class
7669 * @cfg {String} method GET | POST (default POST)
7670 * @cfg {String} labelAlign top | left (default top)
7671 * @cfg {String} align left | right - for navbars
7672 * @cfg {Boolean} loadMask load mask when submit (default true)
7677 * @param {Object} config The config object
7681 Roo.bootstrap.Form = function(config){
7683 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7685 Roo.bootstrap.Form.popover.apply();
7689 * @event clientvalidation
7690 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7691 * @param {Form} this
7692 * @param {Boolean} valid true if the form has passed client-side validation
7694 clientvalidation: true,
7696 * @event beforeaction
7697 * Fires before any action is performed. Return false to cancel the action.
7698 * @param {Form} this
7699 * @param {Action} action The action to be performed
7703 * @event actionfailed
7704 * Fires when an action fails.
7705 * @param {Form} this
7706 * @param {Action} action The action that failed
7708 actionfailed : true,
7710 * @event actioncomplete
7711 * Fires when an action is completed.
7712 * @param {Form} this
7713 * @param {Action} action The action that completed
7715 actioncomplete : true
7719 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7722 * @cfg {String} method
7723 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7728 * The URL to use for form actions if one isn't supplied in the action options.
7731 * @cfg {Boolean} fileUpload
7732 * Set to true if this form is a file upload.
7736 * @cfg {Object} baseParams
7737 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7741 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7745 * @cfg {Sting} align (left|right) for navbar forms
7750 activeAction : null,
7753 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7754 * element by passing it or its id or mask the form itself by passing in true.
7757 waitMsgTarget : false,
7762 * @cfg {Boolean} errorMask (true|false) default false
7767 * @cfg {Number} maskOffset Default 100
7772 * @cfg {Boolean} maskBody
7776 getAutoCreate : function(){
7780 method : this.method || 'POST',
7781 id : this.id || Roo.id(),
7784 if (this.parent().xtype.match(/^Nav/)) {
7785 cfg.cls = 'navbar-form navbar-' + this.align;
7789 if (this.labelAlign == 'left' ) {
7790 cfg.cls += ' form-horizontal';
7796 initEvents : function()
7798 this.el.on('submit', this.onSubmit, this);
7799 // this was added as random key presses on the form where triggering form submit.
7800 this.el.on('keypress', function(e) {
7801 if (e.getCharCode() != 13) {
7804 // we might need to allow it for textareas.. and some other items.
7805 // check e.getTarget().
7807 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7811 Roo.log("keypress blocked");
7819 onSubmit : function(e){
7824 * Returns true if client-side validation on the form is successful.
7827 isValid : function(){
7828 var items = this.getItems();
7832 items.each(function(f){
7838 Roo.log('invalid field: ' + f.name);
7842 if(!target && f.el.isVisible(true)){
7848 if(this.errorMask && !valid){
7849 Roo.bootstrap.Form.popover.mask(this, target);
7856 * Returns true if any fields in this form have changed since their original load.
7859 isDirty : function(){
7861 var items = this.getItems();
7862 items.each(function(f){
7872 * Performs a predefined action (submit or load) or custom actions you define on this form.
7873 * @param {String} actionName The name of the action type
7874 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7875 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7876 * accept other config options):
7878 Property Type Description
7879 ---------------- --------------- ----------------------------------------------------------------------------------
7880 url String The url for the action (defaults to the form's url)
7881 method String The form method to use (defaults to the form's method, or POST if not defined)
7882 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7883 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7884 validate the form on the client (defaults to false)
7886 * @return {BasicForm} this
7888 doAction : function(action, options){
7889 if(typeof action == 'string'){
7890 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7892 if(this.fireEvent('beforeaction', this, action) !== false){
7893 this.beforeAction(action);
7894 action.run.defer(100, action);
7900 beforeAction : function(action){
7901 var o = action.options;
7906 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7908 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7911 // not really supported yet.. ??
7913 //if(this.waitMsgTarget === true){
7914 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7915 //}else if(this.waitMsgTarget){
7916 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7917 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7919 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7925 afterAction : function(action, success){
7926 this.activeAction = null;
7927 var o = action.options;
7932 Roo.get(document.body).unmask();
7938 //if(this.waitMsgTarget === true){
7939 // this.el.unmask();
7940 //}else if(this.waitMsgTarget){
7941 // this.waitMsgTarget.unmask();
7943 // Roo.MessageBox.updateProgress(1);
7944 // Roo.MessageBox.hide();
7951 Roo.callback(o.success, o.scope, [this, action]);
7952 this.fireEvent('actioncomplete', this, action);
7956 // failure condition..
7957 // we have a scenario where updates need confirming.
7958 // eg. if a locking scenario exists..
7959 // we look for { errors : { needs_confirm : true }} in the response.
7961 (typeof(action.result) != 'undefined') &&
7962 (typeof(action.result.errors) != 'undefined') &&
7963 (typeof(action.result.errors.needs_confirm) != 'undefined')
7966 Roo.log("not supported yet");
7969 Roo.MessageBox.confirm(
7970 "Change requires confirmation",
7971 action.result.errorMsg,
7976 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7986 Roo.callback(o.failure, o.scope, [this, action]);
7987 // show an error message if no failed handler is set..
7988 if (!this.hasListener('actionfailed')) {
7989 Roo.log("need to add dialog support");
7991 Roo.MessageBox.alert("Error",
7992 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7993 action.result.errorMsg :
7994 "Saving Failed, please check your entries or try again"
7999 this.fireEvent('actionfailed', this, action);
8004 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8005 * @param {String} id The value to search for
8008 findField : function(id){
8009 var items = this.getItems();
8010 var field = items.get(id);
8012 items.each(function(f){
8013 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8020 return field || null;
8023 * Mark fields in this form invalid in bulk.
8024 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8025 * @return {BasicForm} this
8027 markInvalid : function(errors){
8028 if(errors instanceof Array){
8029 for(var i = 0, len = errors.length; i < len; i++){
8030 var fieldError = errors[i];
8031 var f = this.findField(fieldError.id);
8033 f.markInvalid(fieldError.msg);
8039 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8040 field.markInvalid(errors[id]);
8044 //Roo.each(this.childForms || [], function (f) {
8045 // f.markInvalid(errors);
8052 * Set values for fields in this form in bulk.
8053 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8054 * @return {BasicForm} this
8056 setValues : function(values){
8057 if(values instanceof Array){ // array of objects
8058 for(var i = 0, len = values.length; i < len; i++){
8060 var f = this.findField(v.id);
8062 f.setValue(v.value);
8063 if(this.trackResetOnLoad){
8064 f.originalValue = f.getValue();
8068 }else{ // object hash
8071 if(typeof values[id] != 'function' && (field = this.findField(id))){
8073 if (field.setFromData &&
8075 field.displayField &&
8076 // combos' with local stores can
8077 // be queried via setValue()
8078 // to set their value..
8079 (field.store && !field.store.isLocal)
8083 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8084 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8085 field.setFromData(sd);
8087 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8089 field.setFromData(values);
8092 field.setValue(values[id]);
8096 if(this.trackResetOnLoad){
8097 field.originalValue = field.getValue();
8103 //Roo.each(this.childForms || [], function (f) {
8104 // f.setValues(values);
8111 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8112 * they are returned as an array.
8113 * @param {Boolean} asString
8116 getValues : function(asString){
8117 //if (this.childForms) {
8118 // copy values from the child forms
8119 // Roo.each(this.childForms, function (f) {
8120 // this.setValues(f.getValues());
8126 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8127 if(asString === true){
8130 return Roo.urlDecode(fs);
8134 * Returns the fields in this form as an object with key/value pairs.
8135 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8138 getFieldValues : function(with_hidden)
8140 var items = this.getItems();
8142 items.each(function(f){
8148 var v = f.getValue();
8150 if (f.inputType =='radio') {
8151 if (typeof(ret[f.getName()]) == 'undefined') {
8152 ret[f.getName()] = ''; // empty..
8155 if (!f.el.dom.checked) {
8163 if(f.xtype == 'MoneyField'){
8164 ret[f.currencyName] = f.getCurrency();
8167 // not sure if this supported any more..
8168 if ((typeof(v) == 'object') && f.getRawValue) {
8169 v = f.getRawValue() ; // dates..
8171 // combo boxes where name != hiddenName...
8172 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8173 ret[f.name] = f.getRawValue();
8175 ret[f.getName()] = v;
8182 * Clears all invalid messages in this form.
8183 * @return {BasicForm} this
8185 clearInvalid : function(){
8186 var items = this.getItems();
8188 items.each(function(f){
8197 * @return {BasicForm} this
8200 var items = this.getItems();
8201 items.each(function(f){
8205 Roo.each(this.childForms || [], function (f) {
8213 getItems : function()
8215 var r=new Roo.util.MixedCollection(false, function(o){
8216 return o.id || (o.id = Roo.id());
8218 var iter = function(el) {
8225 Roo.each(el.items,function(e) {
8234 hideFields : function(items)
8236 Roo.each(items, function(i){
8238 var f = this.findField(i);
8244 if(f.xtype == 'DateField'){
8245 f.setVisible(false);
8254 showFields : function(items)
8256 Roo.each(items, function(i){
8258 var f = this.findField(i);
8264 if(f.xtype == 'DateField'){
8276 Roo.apply(Roo.bootstrap.Form, {
8303 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8304 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8305 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8306 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8309 this.maskEl.top.enableDisplayMode("block");
8310 this.maskEl.left.enableDisplayMode("block");
8311 this.maskEl.bottom.enableDisplayMode("block");
8312 this.maskEl.right.enableDisplayMode("block");
8314 this.toolTip = new Roo.bootstrap.Tooltip({
8315 cls : 'roo-form-error-popover',
8317 'left' : ['r-l', [-2,0], 'right'],
8318 'right' : ['l-r', [2,0], 'left'],
8319 'bottom' : ['tl-bl', [0,2], 'top'],
8320 'top' : [ 'bl-tl', [0,-2], 'bottom']
8324 this.toolTip.render(Roo.get(document.body));
8326 this.toolTip.el.enableDisplayMode("block");
8328 Roo.get(document.body).on('click', function(){
8332 Roo.get(document.body).on('touchstart', function(){
8336 this.isApplied = true
8339 mask : function(form, target)
8343 this.target = target;
8345 if(!this.form.errorMask || !target.el){
8349 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8351 Roo.log(scrollable);
8353 var ot = this.target.el.calcOffsetsTo(scrollable);
8355 var scrollTo = ot[1] - this.form.maskOffset;
8357 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8359 scrollable.scrollTo('top', scrollTo);
8361 var box = this.target.el.getBox();
8363 var zIndex = Roo.bootstrap.Modal.zIndex++;
8366 this.maskEl.top.setStyle('position', 'absolute');
8367 this.maskEl.top.setStyle('z-index', zIndex);
8368 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8369 this.maskEl.top.setLeft(0);
8370 this.maskEl.top.setTop(0);
8371 this.maskEl.top.show();
8373 this.maskEl.left.setStyle('position', 'absolute');
8374 this.maskEl.left.setStyle('z-index', zIndex);
8375 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8376 this.maskEl.left.setLeft(0);
8377 this.maskEl.left.setTop(box.y - this.padding);
8378 this.maskEl.left.show();
8380 this.maskEl.bottom.setStyle('position', 'absolute');
8381 this.maskEl.bottom.setStyle('z-index', zIndex);
8382 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8383 this.maskEl.bottom.setLeft(0);
8384 this.maskEl.bottom.setTop(box.bottom + this.padding);
8385 this.maskEl.bottom.show();
8387 this.maskEl.right.setStyle('position', 'absolute');
8388 this.maskEl.right.setStyle('z-index', zIndex);
8389 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8390 this.maskEl.right.setLeft(box.right + this.padding);
8391 this.maskEl.right.setTop(box.y - this.padding);
8392 this.maskEl.right.show();
8394 this.toolTip.bindEl = this.target.el;
8396 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8398 var tip = this.target.blankText;
8400 if(this.target.getValue() !== '' ) {
8402 if (this.target.invalidText.length) {
8403 tip = this.target.invalidText;
8404 } else if (this.target.regexText.length){
8405 tip = this.target.regexText;
8409 this.toolTip.show(tip);
8411 this.intervalID = window.setInterval(function() {
8412 Roo.bootstrap.Form.popover.unmask();
8415 window.onwheel = function(){ return false;};
8417 (function(){ this.isMasked = true; }).defer(500, this);
8423 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8427 this.maskEl.top.setStyle('position', 'absolute');
8428 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8429 this.maskEl.top.hide();
8431 this.maskEl.left.setStyle('position', 'absolute');
8432 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8433 this.maskEl.left.hide();
8435 this.maskEl.bottom.setStyle('position', 'absolute');
8436 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8437 this.maskEl.bottom.hide();
8439 this.maskEl.right.setStyle('position', 'absolute');
8440 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8441 this.maskEl.right.hide();
8443 this.toolTip.hide();
8445 this.toolTip.el.hide();
8447 window.onwheel = function(){ return true;};
8449 if(this.intervalID){
8450 window.clearInterval(this.intervalID);
8451 this.intervalID = false;
8454 this.isMasked = false;
8464 * Ext JS Library 1.1.1
8465 * Copyright(c) 2006-2007, Ext JS, LLC.
8467 * Originally Released Under LGPL - original licence link has changed is not relivant.
8470 * <script type="text/javascript">
8473 * @class Roo.form.VTypes
8474 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8477 Roo.form.VTypes = function(){
8478 // closure these in so they are only created once.
8479 var alpha = /^[a-zA-Z_]+$/;
8480 var alphanum = /^[a-zA-Z0-9_]+$/;
8481 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8482 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8484 // All these messages and functions are configurable
8487 * The function used to validate email addresses
8488 * @param {String} value The email address
8490 'email' : function(v){
8491 return email.test(v);
8494 * The error text to display when the email validation function returns false
8497 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8499 * The keystroke filter mask to be applied on email input
8502 'emailMask' : /[a-z0-9_\.\-@]/i,
8505 * The function used to validate URLs
8506 * @param {String} value The URL
8508 'url' : function(v){
8512 * The error text to display when the url validation function returns false
8515 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8518 * The function used to validate alpha values
8519 * @param {String} value The value
8521 'alpha' : function(v){
8522 return alpha.test(v);
8525 * The error text to display when the alpha validation function returns false
8528 'alphaText' : 'This field should only contain letters and _',
8530 * The keystroke filter mask to be applied on alpha input
8533 'alphaMask' : /[a-z_]/i,
8536 * The function used to validate alphanumeric values
8537 * @param {String} value The value
8539 'alphanum' : function(v){
8540 return alphanum.test(v);
8543 * The error text to display when the alphanumeric validation function returns false
8546 'alphanumText' : 'This field should only contain letters, numbers and _',
8548 * The keystroke filter mask to be applied on alphanumeric input
8551 'alphanumMask' : /[a-z0-9_]/i
8561 * @class Roo.bootstrap.Input
8562 * @extends Roo.bootstrap.Component
8563 * Bootstrap Input class
8564 * @cfg {Boolean} disabled is it disabled
8565 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8566 * @cfg {String} name name of the input
8567 * @cfg {string} fieldLabel - the label associated
8568 * @cfg {string} placeholder - placeholder to put in text.
8569 * @cfg {string} before - input group add on before
8570 * @cfg {string} after - input group add on after
8571 * @cfg {string} size - (lg|sm) or leave empty..
8572 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8573 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8574 * @cfg {Number} md colspan out of 12 for computer-sized screens
8575 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8576 * @cfg {string} value default value of the input
8577 * @cfg {Number} labelWidth set the width of label
8578 * @cfg {Number} labellg set the width of label (1-12)
8579 * @cfg {Number} labelmd set the width of label (1-12)
8580 * @cfg {Number} labelsm set the width of label (1-12)
8581 * @cfg {Number} labelxs set the width of label (1-12)
8582 * @cfg {String} labelAlign (top|left)
8583 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8584 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8585 * @cfg {String} indicatorpos (left|right) default left
8586 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8587 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8589 * @cfg {String} align (left|center|right) Default left
8590 * @cfg {Boolean} forceFeedback (true|false) Default false
8593 * Create a new Input
8594 * @param {Object} config The config object
8597 Roo.bootstrap.Input = function(config){
8599 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8604 * Fires when this field receives input focus.
8605 * @param {Roo.form.Field} this
8610 * Fires when this field loses input focus.
8611 * @param {Roo.form.Field} this
8616 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8617 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8618 * @param {Roo.form.Field} this
8619 * @param {Roo.EventObject} e The event object
8624 * Fires just before the field blurs if the field value has changed.
8625 * @param {Roo.form.Field} this
8626 * @param {Mixed} newValue The new value
8627 * @param {Mixed} oldValue The original value
8632 * Fires after the field has been marked as invalid.
8633 * @param {Roo.form.Field} this
8634 * @param {String} msg The validation message
8639 * Fires after the field has been validated with no errors.
8640 * @param {Roo.form.Field} this
8645 * Fires after the key up
8646 * @param {Roo.form.Field} this
8647 * @param {Roo.EventObject} e The event Object
8653 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8655 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8656 automatic validation (defaults to "keyup").
8658 validationEvent : "keyup",
8660 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8662 validateOnBlur : true,
8664 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8666 validationDelay : 250,
8668 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8670 focusClass : "x-form-focus", // not needed???
8674 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8676 invalidClass : "has-warning",
8679 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8681 validClass : "has-success",
8684 * @cfg {Boolean} hasFeedback (true|false) default true
8689 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8691 invalidFeedbackClass : "glyphicon-warning-sign",
8694 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8696 validFeedbackClass : "glyphicon-ok",
8699 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8701 selectOnFocus : false,
8704 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8708 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8713 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8715 disableKeyFilter : false,
8718 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8722 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8726 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8728 blankText : "Please complete this mandatory field",
8731 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8735 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8737 maxLength : Number.MAX_VALUE,
8739 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8741 minLengthText : "The minimum length for this field is {0}",
8743 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8745 maxLengthText : "The maximum length for this field is {0}",
8749 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8750 * If available, this function will be called only after the basic validators all return true, and will be passed the
8751 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8755 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8756 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8757 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8761 * @cfg {String} regexText -- Depricated - use Invalid Text
8766 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8772 autocomplete: false,
8791 formatedValue : false,
8792 forceFeedback : false,
8794 indicatorpos : 'left',
8804 parentLabelAlign : function()
8807 while (parent.parent()) {
8808 parent = parent.parent();
8809 if (typeof(parent.labelAlign) !='undefined') {
8810 return parent.labelAlign;
8817 getAutoCreate : function()
8819 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8825 if(this.inputType != 'hidden'){
8826 cfg.cls = 'form-group' //input-group
8832 type : this.inputType,
8834 cls : 'form-control',
8835 placeholder : this.placeholder || '',
8836 autocomplete : this.autocomplete || 'new-password'
8839 if(this.capture.length){
8840 input.capture = this.capture;
8843 if(this.accept.length){
8844 input.accept = this.accept + "/*";
8848 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8851 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8852 input.maxLength = this.maxLength;
8855 if (this.disabled) {
8856 input.disabled=true;
8859 if (this.readOnly) {
8860 input.readonly=true;
8864 input.name = this.name;
8868 input.cls += ' input-' + this.size;
8872 ['xs','sm','md','lg'].map(function(size){
8873 if (settings[size]) {
8874 cfg.cls += ' col-' + size + '-' + settings[size];
8878 var inputblock = input;
8882 cls: 'glyphicon form-control-feedback'
8885 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8888 cls : 'has-feedback',
8896 if (this.before || this.after) {
8899 cls : 'input-group',
8903 if (this.before && typeof(this.before) == 'string') {
8905 inputblock.cn.push({
8907 cls : 'roo-input-before input-group-addon',
8911 if (this.before && typeof(this.before) == 'object') {
8912 this.before = Roo.factory(this.before);
8914 inputblock.cn.push({
8916 cls : 'roo-input-before input-group-' +
8917 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8921 inputblock.cn.push(input);
8923 if (this.after && typeof(this.after) == 'string') {
8924 inputblock.cn.push({
8926 cls : 'roo-input-after input-group-addon',
8930 if (this.after && typeof(this.after) == 'object') {
8931 this.after = Roo.factory(this.after);
8933 inputblock.cn.push({
8935 cls : 'roo-input-after input-group-' +
8936 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8940 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8941 inputblock.cls += ' has-feedback';
8942 inputblock.cn.push(feedback);
8946 if (align ==='left' && this.fieldLabel.length) {
8948 cfg.cls += ' roo-form-group-label-left';
8953 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8954 tooltip : 'This field is required'
8959 cls : 'control-label',
8960 html : this.fieldLabel
8971 var labelCfg = cfg.cn[1];
8972 var contentCfg = cfg.cn[2];
8974 if(this.indicatorpos == 'right'){
8979 cls : 'control-label',
8983 html : this.fieldLabel
8987 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8988 tooltip : 'This field is required'
9001 labelCfg = cfg.cn[0];
9002 contentCfg = cfg.cn[1];
9006 if(this.labelWidth > 12){
9007 labelCfg.style = "width: " + this.labelWidth + 'px';
9010 if(this.labelWidth < 13 && this.labelmd == 0){
9011 this.labelmd = this.labelWidth;
9014 if(this.labellg > 0){
9015 labelCfg.cls += ' col-lg-' + this.labellg;
9016 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9019 if(this.labelmd > 0){
9020 labelCfg.cls += ' col-md-' + this.labelmd;
9021 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9024 if(this.labelsm > 0){
9025 labelCfg.cls += ' col-sm-' + this.labelsm;
9026 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9029 if(this.labelxs > 0){
9030 labelCfg.cls += ' col-xs-' + this.labelxs;
9031 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9035 } else if ( this.fieldLabel.length) {
9040 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9041 tooltip : 'This field is required'
9045 //cls : 'input-group-addon',
9046 html : this.fieldLabel
9054 if(this.indicatorpos == 'right'){
9059 //cls : 'input-group-addon',
9060 html : this.fieldLabel
9065 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9066 tooltip : 'This field is required'
9086 if (this.parentType === 'Navbar' && this.parent().bar) {
9087 cfg.cls += ' navbar-form';
9090 if (this.parentType === 'NavGroup') {
9091 cfg.cls += ' navbar-form';
9099 * return the real input element.
9101 inputEl: function ()
9103 return this.el.select('input.form-control',true).first();
9106 tooltipEl : function()
9108 return this.inputEl();
9111 indicatorEl : function()
9113 var indicator = this.el.select('i.roo-required-indicator',true).first();
9123 setDisabled : function(v)
9125 var i = this.inputEl().dom;
9127 i.removeAttribute('disabled');
9131 i.setAttribute('disabled','true');
9133 initEvents : function()
9136 this.inputEl().on("keydown" , this.fireKey, this);
9137 this.inputEl().on("focus", this.onFocus, this);
9138 this.inputEl().on("blur", this.onBlur, this);
9140 this.inputEl().relayEvent('keyup', this);
9142 this.indicator = this.indicatorEl();
9145 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9148 // reference to original value for reset
9149 this.originalValue = this.getValue();
9150 //Roo.form.TextField.superclass.initEvents.call(this);
9151 if(this.validationEvent == 'keyup'){
9152 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9153 this.inputEl().on('keyup', this.filterValidation, this);
9155 else if(this.validationEvent !== false){
9156 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9159 if(this.selectOnFocus){
9160 this.on("focus", this.preFocus, this);
9163 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9164 this.inputEl().on("keypress", this.filterKeys, this);
9166 this.inputEl().relayEvent('keypress', this);
9169 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9170 this.el.on("click", this.autoSize, this);
9173 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9174 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9177 if (typeof(this.before) == 'object') {
9178 this.before.render(this.el.select('.roo-input-before',true).first());
9180 if (typeof(this.after) == 'object') {
9181 this.after.render(this.el.select('.roo-input-after',true).first());
9184 this.inputEl().on('change', this.onChange, this);
9187 filterValidation : function(e){
9188 if(!e.isNavKeyPress()){
9189 this.validationTask.delay(this.validationDelay);
9193 * Validates the field value
9194 * @return {Boolean} True if the value is valid, else false
9196 validate : function(){
9197 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9198 if(this.disabled || this.validateValue(this.getRawValue())){
9209 * Validates a value according to the field's validation rules and marks the field as invalid
9210 * if the validation fails
9211 * @param {Mixed} value The value to validate
9212 * @return {Boolean} True if the value is valid, else false
9214 validateValue : function(value)
9216 if(this.getVisibilityEl().hasClass('hidden')){
9220 if(value.length < 1) { // if it's blank
9221 if(this.allowBlank){
9227 if(value.length < this.minLength){
9230 if(value.length > this.maxLength){
9234 var vt = Roo.form.VTypes;
9235 if(!vt[this.vtype](value, this)){
9239 if(typeof this.validator == "function"){
9240 var msg = this.validator(value);
9244 if (typeof(msg) == 'string') {
9245 this.invalidText = msg;
9249 if(this.regex && !this.regex.test(value)){
9257 fireKey : function(e){
9258 //Roo.log('field ' + e.getKey());
9259 if(e.isNavKeyPress()){
9260 this.fireEvent("specialkey", this, e);
9263 focus : function (selectText){
9265 this.inputEl().focus();
9266 if(selectText === true){
9267 this.inputEl().dom.select();
9273 onFocus : function(){
9274 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9275 // this.el.addClass(this.focusClass);
9278 this.hasFocus = true;
9279 this.startValue = this.getValue();
9280 this.fireEvent("focus", this);
9284 beforeBlur : Roo.emptyFn,
9288 onBlur : function(){
9290 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9291 //this.el.removeClass(this.focusClass);
9293 this.hasFocus = false;
9294 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9297 var v = this.getValue();
9298 if(String(v) !== String(this.startValue)){
9299 this.fireEvent('change', this, v, this.startValue);
9301 this.fireEvent("blur", this);
9304 onChange : function(e)
9306 var v = this.getValue();
9307 if(String(v) !== String(this.startValue)){
9308 this.fireEvent('change', this, v, this.startValue);
9314 * Resets the current field value to the originally loaded value and clears any validation messages
9317 this.setValue(this.originalValue);
9321 * Returns the name of the field
9322 * @return {Mixed} name The name field
9324 getName: function(){
9328 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9329 * @return {Mixed} value The field value
9331 getValue : function(){
9333 var v = this.inputEl().getValue();
9338 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9339 * @return {Mixed} value The field value
9341 getRawValue : function(){
9342 var v = this.inputEl().getValue();
9348 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9349 * @param {Mixed} value The value to set
9351 setRawValue : function(v){
9352 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9355 selectText : function(start, end){
9356 var v = this.getRawValue();
9358 start = start === undefined ? 0 : start;
9359 end = end === undefined ? v.length : end;
9360 var d = this.inputEl().dom;
9361 if(d.setSelectionRange){
9362 d.setSelectionRange(start, end);
9363 }else if(d.createTextRange){
9364 var range = d.createTextRange();
9365 range.moveStart("character", start);
9366 range.moveEnd("character", v.length-end);
9373 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9374 * @param {Mixed} value The value to set
9376 setValue : function(v){
9379 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9385 processValue : function(value){
9386 if(this.stripCharsRe){
9387 var newValue = value.replace(this.stripCharsRe, '');
9388 if(newValue !== value){
9389 this.setRawValue(newValue);
9396 preFocus : function(){
9398 if(this.selectOnFocus){
9399 this.inputEl().dom.select();
9402 filterKeys : function(e){
9404 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9407 var c = e.getCharCode(), cc = String.fromCharCode(c);
9408 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9411 if(!this.maskRe.test(cc)){
9416 * Clear any invalid styles/messages for this field
9418 clearInvalid : function(){
9420 if(!this.el || this.preventMark){ // not rendered
9425 this.el.removeClass(this.invalidClass);
9427 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9429 var feedback = this.el.select('.form-control-feedback', true).first();
9432 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9438 this.indicator.removeClass('visible');
9439 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9442 this.fireEvent('valid', this);
9446 * Mark this field as valid
9448 markValid : function()
9450 if(!this.el || this.preventMark){ // not rendered...
9454 this.el.removeClass([this.invalidClass, this.validClass]);
9456 var feedback = this.el.select('.form-control-feedback', true).first();
9459 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9463 this.indicator.removeClass('visible');
9464 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9471 if(this.allowBlank && !this.getRawValue().length){
9475 this.el.addClass(this.validClass);
9477 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9479 var feedback = this.el.select('.form-control-feedback', true).first();
9482 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9483 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9488 this.fireEvent('valid', this);
9492 * Mark this field as invalid
9493 * @param {String} msg The validation message
9495 markInvalid : function(msg)
9497 if(!this.el || this.preventMark){ // not rendered
9501 this.el.removeClass([this.invalidClass, this.validClass]);
9503 var feedback = this.el.select('.form-control-feedback', true).first();
9506 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9513 if(this.allowBlank && !this.getRawValue().length){
9518 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9519 this.indicator.addClass('visible');
9522 this.el.addClass(this.invalidClass);
9524 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9526 var feedback = this.el.select('.form-control-feedback', true).first();
9529 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9531 if(this.getValue().length || this.forceFeedback){
9532 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9539 this.fireEvent('invalid', this, msg);
9542 SafariOnKeyDown : function(event)
9544 // this is a workaround for a password hang bug on chrome/ webkit.
9545 if (this.inputEl().dom.type != 'password') {
9549 var isSelectAll = false;
9551 if(this.inputEl().dom.selectionEnd > 0){
9552 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9554 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9555 event.preventDefault();
9560 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9562 event.preventDefault();
9563 // this is very hacky as keydown always get's upper case.
9565 var cc = String.fromCharCode(event.getCharCode());
9566 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9570 adjustWidth : function(tag, w){
9571 tag = tag.toLowerCase();
9572 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9573 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9577 if(tag == 'textarea'){
9580 }else if(Roo.isOpera){
9584 if(tag == 'textarea'){
9592 setFieldLabel : function(v)
9599 var ar = this.el.select('label > span',true);
9601 if (ar.elements.length) {
9602 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9603 this.fieldLabel = v;
9607 var br = this.el.select('label',true);
9609 if(br.elements.length) {
9610 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9611 this.fieldLabel = v;
9615 Roo.log('Cannot Found any of label > span || label in input');
9619 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9620 this.fieldLabel = v;
9635 * @class Roo.bootstrap.TextArea
9636 * @extends Roo.bootstrap.Input
9637 * Bootstrap TextArea class
9638 * @cfg {Number} cols Specifies the visible width of a text area
9639 * @cfg {Number} rows Specifies the visible number of lines in a text area
9640 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9641 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9642 * @cfg {string} html text
9645 * Create a new TextArea
9646 * @param {Object} config The config object
9649 Roo.bootstrap.TextArea = function(config){
9650 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9654 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9664 getAutoCreate : function(){
9666 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9672 if(this.inputType != 'hidden'){
9673 cfg.cls = 'form-group' //input-group
9681 value : this.value || '',
9682 html: this.html || '',
9683 cls : 'form-control',
9684 placeholder : this.placeholder || ''
9688 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9689 input.maxLength = this.maxLength;
9693 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9697 input.cols = this.cols;
9700 if (this.readOnly) {
9701 input.readonly = true;
9705 input.name = this.name;
9709 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9713 ['xs','sm','md','lg'].map(function(size){
9714 if (settings[size]) {
9715 cfg.cls += ' col-' + size + '-' + settings[size];
9719 var inputblock = input;
9721 if(this.hasFeedback && !this.allowBlank){
9725 cls: 'glyphicon form-control-feedback'
9729 cls : 'has-feedback',
9738 if (this.before || this.after) {
9741 cls : 'input-group',
9745 inputblock.cn.push({
9747 cls : 'input-group-addon',
9752 inputblock.cn.push(input);
9754 if(this.hasFeedback && !this.allowBlank){
9755 inputblock.cls += ' has-feedback';
9756 inputblock.cn.push(feedback);
9760 inputblock.cn.push({
9762 cls : 'input-group-addon',
9769 if (align ==='left' && this.fieldLabel.length) {
9774 cls : 'control-label',
9775 html : this.fieldLabel
9786 if(this.labelWidth > 12){
9787 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9790 if(this.labelWidth < 13 && this.labelmd == 0){
9791 this.labelmd = this.labelWidth;
9794 if(this.labellg > 0){
9795 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9796 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9799 if(this.labelmd > 0){
9800 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9801 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9804 if(this.labelsm > 0){
9805 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9806 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9809 if(this.labelxs > 0){
9810 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9811 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9814 } else if ( this.fieldLabel.length) {
9819 //cls : 'input-group-addon',
9820 html : this.fieldLabel
9838 if (this.disabled) {
9839 input.disabled=true;
9846 * return the real textarea element.
9848 inputEl: function ()
9850 return this.el.select('textarea.form-control',true).first();
9854 * Clear any invalid styles/messages for this field
9856 clearInvalid : function()
9859 if(!this.el || this.preventMark){ // not rendered
9863 var label = this.el.select('label', true).first();
9864 var icon = this.el.select('i.fa-star', true).first();
9870 this.el.removeClass(this.invalidClass);
9872 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9874 var feedback = this.el.select('.form-control-feedback', true).first();
9877 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9882 this.fireEvent('valid', this);
9886 * Mark this field as valid
9888 markValid : function()
9890 if(!this.el || this.preventMark){ // not rendered
9894 this.el.removeClass([this.invalidClass, this.validClass]);
9896 var feedback = this.el.select('.form-control-feedback', true).first();
9899 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9902 if(this.disabled || this.allowBlank){
9906 var label = this.el.select('label', true).first();
9907 var icon = this.el.select('i.fa-star', true).first();
9913 this.el.addClass(this.validClass);
9915 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9917 var feedback = this.el.select('.form-control-feedback', true).first();
9920 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9921 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9926 this.fireEvent('valid', this);
9930 * Mark this field as invalid
9931 * @param {String} msg The validation message
9933 markInvalid : function(msg)
9935 if(!this.el || this.preventMark){ // not rendered
9939 this.el.removeClass([this.invalidClass, this.validClass]);
9941 var feedback = this.el.select('.form-control-feedback', true).first();
9944 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9947 if(this.disabled || this.allowBlank){
9951 var label = this.el.select('label', true).first();
9952 var icon = this.el.select('i.fa-star', true).first();
9954 if(!this.getValue().length && label && !icon){
9955 this.el.createChild({
9957 cls : 'text-danger fa fa-lg fa-star',
9958 tooltip : 'This field is required',
9959 style : 'margin-right:5px;'
9963 this.el.addClass(this.invalidClass);
9965 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9967 var feedback = this.el.select('.form-control-feedback', true).first();
9970 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9972 if(this.getValue().length || this.forceFeedback){
9973 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9980 this.fireEvent('invalid', this, msg);
9988 * trigger field - base class for combo..
9993 * @class Roo.bootstrap.TriggerField
9994 * @extends Roo.bootstrap.Input
9995 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9996 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9997 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9998 * for which you can provide a custom implementation. For example:
10000 var trigger = new Roo.bootstrap.TriggerField();
10001 trigger.onTriggerClick = myTriggerFn;
10002 trigger.applyTo('my-field');
10005 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10006 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10007 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10008 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10009 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10012 * Create a new TriggerField.
10013 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10014 * to the base TextField)
10016 Roo.bootstrap.TriggerField = function(config){
10017 this.mimicing = false;
10018 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10021 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10023 * @cfg {String} triggerClass A CSS class to apply to the trigger
10026 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10031 * @cfg {Boolean} removable (true|false) special filter default false
10035 /** @cfg {Boolean} grow @hide */
10036 /** @cfg {Number} growMin @hide */
10037 /** @cfg {Number} growMax @hide */
10043 autoSize: Roo.emptyFn,
10047 deferHeight : true,
10050 actionMode : 'wrap',
10055 getAutoCreate : function(){
10057 var align = this.labelAlign || this.parentLabelAlign();
10062 cls: 'form-group' //input-group
10069 type : this.inputType,
10070 cls : 'form-control',
10071 autocomplete: 'new-password',
10072 placeholder : this.placeholder || ''
10076 input.name = this.name;
10079 input.cls += ' input-' + this.size;
10082 if (this.disabled) {
10083 input.disabled=true;
10086 var inputblock = input;
10088 if(this.hasFeedback && !this.allowBlank){
10092 cls: 'glyphicon form-control-feedback'
10095 if(this.removable && !this.editable && !this.tickable){
10097 cls : 'has-feedback',
10103 cls : 'roo-combo-removable-btn close'
10110 cls : 'has-feedback',
10119 if(this.removable && !this.editable && !this.tickable){
10121 cls : 'roo-removable',
10127 cls : 'roo-combo-removable-btn close'
10134 if (this.before || this.after) {
10137 cls : 'input-group',
10141 inputblock.cn.push({
10143 cls : 'input-group-addon',
10148 inputblock.cn.push(input);
10150 if(this.hasFeedback && !this.allowBlank){
10151 inputblock.cls += ' has-feedback';
10152 inputblock.cn.push(feedback);
10156 inputblock.cn.push({
10158 cls : 'input-group-addon',
10171 cls: 'form-hidden-field'
10185 cls: 'form-hidden-field'
10189 cls: 'roo-select2-choices',
10193 cls: 'roo-select2-search-field',
10206 cls: 'roo-select2-container input-group',
10211 // cls: 'typeahead typeahead-long dropdown-menu',
10212 // style: 'display:none'
10217 if(!this.multiple && this.showToggleBtn){
10223 if (this.caret != false) {
10226 cls: 'fa fa-' + this.caret
10233 cls : 'input-group-addon btn dropdown-toggle',
10238 cls: 'combobox-clear',
10252 combobox.cls += ' roo-select2-container-multi';
10255 if (align ==='left' && this.fieldLabel.length) {
10257 cfg.cls += ' roo-form-group-label-left';
10262 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10263 tooltip : 'This field is required'
10268 cls : 'control-label',
10269 html : this.fieldLabel
10281 var labelCfg = cfg.cn[1];
10282 var contentCfg = cfg.cn[2];
10284 if(this.indicatorpos == 'right'){
10289 cls : 'control-label',
10293 html : this.fieldLabel
10297 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10298 tooltip : 'This field is required'
10311 labelCfg = cfg.cn[0];
10312 contentCfg = cfg.cn[1];
10315 if(this.labelWidth > 12){
10316 labelCfg.style = "width: " + this.labelWidth + 'px';
10319 if(this.labelWidth < 13 && this.labelmd == 0){
10320 this.labelmd = this.labelWidth;
10323 if(this.labellg > 0){
10324 labelCfg.cls += ' col-lg-' + this.labellg;
10325 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10328 if(this.labelmd > 0){
10329 labelCfg.cls += ' col-md-' + this.labelmd;
10330 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10333 if(this.labelsm > 0){
10334 labelCfg.cls += ' col-sm-' + this.labelsm;
10335 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10338 if(this.labelxs > 0){
10339 labelCfg.cls += ' col-xs-' + this.labelxs;
10340 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10343 } else if ( this.fieldLabel.length) {
10344 // Roo.log(" label");
10348 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10349 tooltip : 'This field is required'
10353 //cls : 'input-group-addon',
10354 html : this.fieldLabel
10362 if(this.indicatorpos == 'right'){
10370 html : this.fieldLabel
10374 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10375 tooltip : 'This field is required'
10388 // Roo.log(" no label && no align");
10395 ['xs','sm','md','lg'].map(function(size){
10396 if (settings[size]) {
10397 cfg.cls += ' col-' + size + '-' + settings[size];
10408 onResize : function(w, h){
10409 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10410 // if(typeof w == 'number'){
10411 // var x = w - this.trigger.getWidth();
10412 // this.inputEl().setWidth(this.adjustWidth('input', x));
10413 // this.trigger.setStyle('left', x+'px');
10418 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10421 getResizeEl : function(){
10422 return this.inputEl();
10426 getPositionEl : function(){
10427 return this.inputEl();
10431 alignErrorIcon : function(){
10432 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10436 initEvents : function(){
10440 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10441 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10442 if(!this.multiple && this.showToggleBtn){
10443 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10444 if(this.hideTrigger){
10445 this.trigger.setDisplayed(false);
10447 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10451 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10454 if(this.removable && !this.editable && !this.tickable){
10455 var close = this.closeTriggerEl();
10458 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10459 close.on('click', this.removeBtnClick, this, close);
10463 //this.trigger.addClassOnOver('x-form-trigger-over');
10464 //this.trigger.addClassOnClick('x-form-trigger-click');
10467 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10471 closeTriggerEl : function()
10473 var close = this.el.select('.roo-combo-removable-btn', true).first();
10474 return close ? close : false;
10477 removeBtnClick : function(e, h, el)
10479 e.preventDefault();
10481 if(this.fireEvent("remove", this) !== false){
10483 this.fireEvent("afterremove", this)
10487 createList : function()
10489 this.list = Roo.get(document.body).createChild({
10491 cls: 'typeahead typeahead-long dropdown-menu',
10492 style: 'display:none'
10495 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10500 initTrigger : function(){
10505 onDestroy : function(){
10507 this.trigger.removeAllListeners();
10508 // this.trigger.remove();
10511 // this.wrap.remove();
10513 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10517 onFocus : function(){
10518 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10520 if(!this.mimicing){
10521 this.wrap.addClass('x-trigger-wrap-focus');
10522 this.mimicing = true;
10523 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10524 if(this.monitorTab){
10525 this.el.on("keydown", this.checkTab, this);
10532 checkTab : function(e){
10533 if(e.getKey() == e.TAB){
10534 this.triggerBlur();
10539 onBlur : function(){
10544 mimicBlur : function(e, t){
10546 if(!this.wrap.contains(t) && this.validateBlur()){
10547 this.triggerBlur();
10553 triggerBlur : function(){
10554 this.mimicing = false;
10555 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10556 if(this.monitorTab){
10557 this.el.un("keydown", this.checkTab, this);
10559 //this.wrap.removeClass('x-trigger-wrap-focus');
10560 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10564 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10565 validateBlur : function(e, t){
10570 onDisable : function(){
10571 this.inputEl().dom.disabled = true;
10572 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10574 // this.wrap.addClass('x-item-disabled');
10579 onEnable : function(){
10580 this.inputEl().dom.disabled = false;
10581 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10583 // this.el.removeClass('x-item-disabled');
10588 onShow : function(){
10589 var ae = this.getActionEl();
10592 ae.dom.style.display = '';
10593 ae.dom.style.visibility = 'visible';
10599 onHide : function(){
10600 var ae = this.getActionEl();
10601 ae.dom.style.display = 'none';
10605 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10606 * by an implementing function.
10608 * @param {EventObject} e
10610 onTriggerClick : Roo.emptyFn
10614 * Ext JS Library 1.1.1
10615 * Copyright(c) 2006-2007, Ext JS, LLC.
10617 * Originally Released Under LGPL - original licence link has changed is not relivant.
10620 * <script type="text/javascript">
10625 * @class Roo.data.SortTypes
10627 * Defines the default sorting (casting?) comparison functions used when sorting data.
10629 Roo.data.SortTypes = {
10631 * Default sort that does nothing
10632 * @param {Mixed} s The value being converted
10633 * @return {Mixed} The comparison value
10635 none : function(s){
10640 * The regular expression used to strip tags
10644 stripTagsRE : /<\/?[^>]+>/gi,
10647 * Strips all HTML tags to sort on text only
10648 * @param {Mixed} s The value being converted
10649 * @return {String} The comparison value
10651 asText : function(s){
10652 return String(s).replace(this.stripTagsRE, "");
10656 * Strips all HTML tags to sort on text only - Case insensitive
10657 * @param {Mixed} s The value being converted
10658 * @return {String} The comparison value
10660 asUCText : function(s){
10661 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10665 * Case insensitive string
10666 * @param {Mixed} s The value being converted
10667 * @return {String} The comparison value
10669 asUCString : function(s) {
10670 return String(s).toUpperCase();
10675 * @param {Mixed} s The value being converted
10676 * @return {Number} The comparison value
10678 asDate : function(s) {
10682 if(s instanceof Date){
10683 return s.getTime();
10685 return Date.parse(String(s));
10690 * @param {Mixed} s The value being converted
10691 * @return {Float} The comparison value
10693 asFloat : function(s) {
10694 var val = parseFloat(String(s).replace(/,/g, ""));
10703 * @param {Mixed} s The value being converted
10704 * @return {Number} The comparison value
10706 asInt : function(s) {
10707 var val = parseInt(String(s).replace(/,/g, ""));
10715 * Ext JS Library 1.1.1
10716 * Copyright(c) 2006-2007, Ext JS, LLC.
10718 * Originally Released Under LGPL - original licence link has changed is not relivant.
10721 * <script type="text/javascript">
10725 * @class Roo.data.Record
10726 * Instances of this class encapsulate both record <em>definition</em> information, and record
10727 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10728 * to access Records cached in an {@link Roo.data.Store} object.<br>
10730 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10731 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10734 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10736 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10737 * {@link #create}. The parameters are the same.
10738 * @param {Array} data An associative Array of data values keyed by the field name.
10739 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10740 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10741 * not specified an integer id is generated.
10743 Roo.data.Record = function(data, id){
10744 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10749 * Generate a constructor for a specific record layout.
10750 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10751 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10752 * Each field definition object may contain the following properties: <ul>
10753 * <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,
10754 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10755 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10756 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10757 * is being used, then this is a string containing the javascript expression to reference the data relative to
10758 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10759 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10760 * this may be omitted.</p></li>
10761 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10762 * <ul><li>auto (Default, implies no conversion)</li>
10767 * <li>date</li></ul></p></li>
10768 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10769 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10770 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10771 * by the Reader into an object that will be stored in the Record. It is passed the
10772 * following parameters:<ul>
10773 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10775 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10777 * <br>usage:<br><pre><code>
10778 var TopicRecord = Roo.data.Record.create(
10779 {name: 'title', mapping: 'topic_title'},
10780 {name: 'author', mapping: 'username'},
10781 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10782 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10783 {name: 'lastPoster', mapping: 'user2'},
10784 {name: 'excerpt', mapping: 'post_text'}
10787 var myNewRecord = new TopicRecord({
10788 title: 'Do my job please',
10791 lastPost: new Date(),
10792 lastPoster: 'Animal',
10793 excerpt: 'No way dude!'
10795 myStore.add(myNewRecord);
10800 Roo.data.Record.create = function(o){
10801 var f = function(){
10802 f.superclass.constructor.apply(this, arguments);
10804 Roo.extend(f, Roo.data.Record);
10805 var p = f.prototype;
10806 p.fields = new Roo.util.MixedCollection(false, function(field){
10809 for(var i = 0, len = o.length; i < len; i++){
10810 p.fields.add(new Roo.data.Field(o[i]));
10812 f.getField = function(name){
10813 return p.fields.get(name);
10818 Roo.data.Record.AUTO_ID = 1000;
10819 Roo.data.Record.EDIT = 'edit';
10820 Roo.data.Record.REJECT = 'reject';
10821 Roo.data.Record.COMMIT = 'commit';
10823 Roo.data.Record.prototype = {
10825 * Readonly flag - true if this record has been modified.
10834 join : function(store){
10835 this.store = store;
10839 * Set the named field to the specified value.
10840 * @param {String} name The name of the field to set.
10841 * @param {Object} value The value to set the field to.
10843 set : function(name, value){
10844 if(this.data[name] == value){
10848 if(!this.modified){
10849 this.modified = {};
10851 if(typeof this.modified[name] == 'undefined'){
10852 this.modified[name] = this.data[name];
10854 this.data[name] = value;
10855 if(!this.editing && this.store){
10856 this.store.afterEdit(this);
10861 * Get the value of the named field.
10862 * @param {String} name The name of the field to get the value of.
10863 * @return {Object} The value of the field.
10865 get : function(name){
10866 return this.data[name];
10870 beginEdit : function(){
10871 this.editing = true;
10872 this.modified = {};
10876 cancelEdit : function(){
10877 this.editing = false;
10878 delete this.modified;
10882 endEdit : function(){
10883 this.editing = false;
10884 if(this.dirty && this.store){
10885 this.store.afterEdit(this);
10890 * Usually called by the {@link Roo.data.Store} which owns the Record.
10891 * Rejects all changes made to the Record since either creation, or the last commit operation.
10892 * Modified fields are reverted to their original values.
10894 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10895 * of reject operations.
10897 reject : function(){
10898 var m = this.modified;
10900 if(typeof m[n] != "function"){
10901 this.data[n] = m[n];
10904 this.dirty = false;
10905 delete this.modified;
10906 this.editing = false;
10908 this.store.afterReject(this);
10913 * Usually called by the {@link Roo.data.Store} which owns the Record.
10914 * Commits all changes made to the Record since either creation, or the last commit operation.
10916 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10917 * of commit operations.
10919 commit : function(){
10920 this.dirty = false;
10921 delete this.modified;
10922 this.editing = false;
10924 this.store.afterCommit(this);
10929 hasError : function(){
10930 return this.error != null;
10934 clearError : function(){
10939 * Creates a copy of this record.
10940 * @param {String} id (optional) A new record id if you don't want to use this record's id
10943 copy : function(newId) {
10944 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10948 * Ext JS Library 1.1.1
10949 * Copyright(c) 2006-2007, Ext JS, LLC.
10951 * Originally Released Under LGPL - original licence link has changed is not relivant.
10954 * <script type="text/javascript">
10960 * @class Roo.data.Store
10961 * @extends Roo.util.Observable
10962 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10963 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10965 * 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
10966 * has no knowledge of the format of the data returned by the Proxy.<br>
10968 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10969 * instances from the data object. These records are cached and made available through accessor functions.
10971 * Creates a new Store.
10972 * @param {Object} config A config object containing the objects needed for the Store to access data,
10973 * and read the data into Records.
10975 Roo.data.Store = function(config){
10976 this.data = new Roo.util.MixedCollection(false);
10977 this.data.getKey = function(o){
10980 this.baseParams = {};
10982 this.paramNames = {
10987 "multisort" : "_multisort"
10990 if(config && config.data){
10991 this.inlineData = config.data;
10992 delete config.data;
10995 Roo.apply(this, config);
10997 if(this.reader){ // reader passed
10998 this.reader = Roo.factory(this.reader, Roo.data);
10999 this.reader.xmodule = this.xmodule || false;
11000 if(!this.recordType){
11001 this.recordType = this.reader.recordType;
11003 if(this.reader.onMetaChange){
11004 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11008 if(this.recordType){
11009 this.fields = this.recordType.prototype.fields;
11011 this.modified = [];
11015 * @event datachanged
11016 * Fires when the data cache has changed, and a widget which is using this Store
11017 * as a Record cache should refresh its view.
11018 * @param {Store} this
11020 datachanged : true,
11022 * @event metachange
11023 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11024 * @param {Store} this
11025 * @param {Object} meta The JSON metadata
11030 * Fires when Records have been added to the Store
11031 * @param {Store} this
11032 * @param {Roo.data.Record[]} records The array of Records added
11033 * @param {Number} index The index at which the record(s) were added
11038 * Fires when a Record has been removed from the Store
11039 * @param {Store} this
11040 * @param {Roo.data.Record} record The Record that was removed
11041 * @param {Number} index The index at which the record was removed
11046 * Fires when a Record has been updated
11047 * @param {Store} this
11048 * @param {Roo.data.Record} record The Record that was updated
11049 * @param {String} operation The update operation being performed. Value may be one of:
11051 Roo.data.Record.EDIT
11052 Roo.data.Record.REJECT
11053 Roo.data.Record.COMMIT
11059 * Fires when the data cache has been cleared.
11060 * @param {Store} this
11064 * @event beforeload
11065 * Fires before a request is made for a new data object. If the beforeload handler returns false
11066 * the load action will be canceled.
11067 * @param {Store} this
11068 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11072 * @event beforeloadadd
11073 * Fires after a new set of Records has been loaded.
11074 * @param {Store} this
11075 * @param {Roo.data.Record[]} records The Records that were loaded
11076 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11078 beforeloadadd : true,
11081 * Fires after a new set of Records has been loaded, before they are added to the store.
11082 * @param {Store} this
11083 * @param {Roo.data.Record[]} records The Records that were loaded
11084 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11085 * @params {Object} return from reader
11089 * @event loadexception
11090 * Fires if an exception occurs in the Proxy during loading.
11091 * Called with the signature of the Proxy's "loadexception" event.
11092 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11095 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11096 * @param {Object} load options
11097 * @param {Object} jsonData from your request (normally this contains the Exception)
11099 loadexception : true
11103 this.proxy = Roo.factory(this.proxy, Roo.data);
11104 this.proxy.xmodule = this.xmodule || false;
11105 this.relayEvents(this.proxy, ["loadexception"]);
11107 this.sortToggle = {};
11108 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11110 Roo.data.Store.superclass.constructor.call(this);
11112 if(this.inlineData){
11113 this.loadData(this.inlineData);
11114 delete this.inlineData;
11118 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11120 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11121 * without a remote query - used by combo/forms at present.
11125 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11128 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11131 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11132 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11135 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11136 * on any HTTP request
11139 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11142 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11146 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11147 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11149 remoteSort : false,
11152 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11153 * loaded or when a record is removed. (defaults to false).
11155 pruneModifiedRecords : false,
11158 lastOptions : null,
11161 * Add Records to the Store and fires the add event.
11162 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11164 add : function(records){
11165 records = [].concat(records);
11166 for(var i = 0, len = records.length; i < len; i++){
11167 records[i].join(this);
11169 var index = this.data.length;
11170 this.data.addAll(records);
11171 this.fireEvent("add", this, records, index);
11175 * Remove a Record from the Store and fires the remove event.
11176 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11178 remove : function(record){
11179 var index = this.data.indexOf(record);
11180 this.data.removeAt(index);
11182 if(this.pruneModifiedRecords){
11183 this.modified.remove(record);
11185 this.fireEvent("remove", this, record, index);
11189 * Remove all Records from the Store and fires the clear event.
11191 removeAll : function(){
11193 if(this.pruneModifiedRecords){
11194 this.modified = [];
11196 this.fireEvent("clear", this);
11200 * Inserts Records to the Store at the given index and fires the add event.
11201 * @param {Number} index The start index at which to insert the passed Records.
11202 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11204 insert : function(index, records){
11205 records = [].concat(records);
11206 for(var i = 0, len = records.length; i < len; i++){
11207 this.data.insert(index, records[i]);
11208 records[i].join(this);
11210 this.fireEvent("add", this, records, index);
11214 * Get the index within the cache of the passed Record.
11215 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11216 * @return {Number} The index of the passed Record. Returns -1 if not found.
11218 indexOf : function(record){
11219 return this.data.indexOf(record);
11223 * Get the index within the cache of the Record with the passed id.
11224 * @param {String} id The id of the Record to find.
11225 * @return {Number} The index of the Record. Returns -1 if not found.
11227 indexOfId : function(id){
11228 return this.data.indexOfKey(id);
11232 * Get the Record with the specified id.
11233 * @param {String} id The id of the Record to find.
11234 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11236 getById : function(id){
11237 return this.data.key(id);
11241 * Get the Record at the specified index.
11242 * @param {Number} index The index of the Record to find.
11243 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11245 getAt : function(index){
11246 return this.data.itemAt(index);
11250 * Returns a range of Records between specified indices.
11251 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11252 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11253 * @return {Roo.data.Record[]} An array of Records
11255 getRange : function(start, end){
11256 return this.data.getRange(start, end);
11260 storeOptions : function(o){
11261 o = Roo.apply({}, o);
11264 this.lastOptions = o;
11268 * Loads the Record cache from the configured Proxy using the configured Reader.
11270 * If using remote paging, then the first load call must specify the <em>start</em>
11271 * and <em>limit</em> properties in the options.params property to establish the initial
11272 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11274 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11275 * and this call will return before the new data has been loaded. Perform any post-processing
11276 * in a callback function, or in a "load" event handler.</strong>
11278 * @param {Object} options An object containing properties which control loading options:<ul>
11279 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11280 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11281 * passed the following arguments:<ul>
11282 * <li>r : Roo.data.Record[]</li>
11283 * <li>options: Options object from the load call</li>
11284 * <li>success: Boolean success indicator</li></ul></li>
11285 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11286 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11289 load : function(options){
11290 options = options || {};
11291 if(this.fireEvent("beforeload", this, options) !== false){
11292 this.storeOptions(options);
11293 var p = Roo.apply(options.params || {}, this.baseParams);
11294 // if meta was not loaded from remote source.. try requesting it.
11295 if (!this.reader.metaFromRemote) {
11296 p._requestMeta = 1;
11298 if(this.sortInfo && this.remoteSort){
11299 var pn = this.paramNames;
11300 p[pn["sort"]] = this.sortInfo.field;
11301 p[pn["dir"]] = this.sortInfo.direction;
11303 if (this.multiSort) {
11304 var pn = this.paramNames;
11305 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11308 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11313 * Reloads the Record cache from the configured Proxy using the configured Reader and
11314 * the options from the last load operation performed.
11315 * @param {Object} options (optional) An object containing properties which may override the options
11316 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11317 * the most recently used options are reused).
11319 reload : function(options){
11320 this.load(Roo.applyIf(options||{}, this.lastOptions));
11324 // Called as a callback by the Reader during a load operation.
11325 loadRecords : function(o, options, success){
11326 if(!o || success === false){
11327 if(success !== false){
11328 this.fireEvent("load", this, [], options, o);
11330 if(options.callback){
11331 options.callback.call(options.scope || this, [], options, false);
11335 // if data returned failure - throw an exception.
11336 if (o.success === false) {
11337 // show a message if no listener is registered.
11338 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11339 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11341 // loadmask wil be hooked into this..
11342 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11345 var r = o.records, t = o.totalRecords || r.length;
11347 this.fireEvent("beforeloadadd", this, r, options, o);
11349 if(!options || options.add !== true){
11350 if(this.pruneModifiedRecords){
11351 this.modified = [];
11353 for(var i = 0, len = r.length; i < len; i++){
11357 this.data = this.snapshot;
11358 delete this.snapshot;
11361 this.data.addAll(r);
11362 this.totalLength = t;
11364 this.fireEvent("datachanged", this);
11366 this.totalLength = Math.max(t, this.data.length+r.length);
11370 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11372 var e = new Roo.data.Record({});
11374 e.set(this.parent.displayField, this.parent.emptyTitle);
11375 e.set(this.parent.valueField, '');
11380 this.fireEvent("load", this, r, options, o);
11381 if(options.callback){
11382 options.callback.call(options.scope || this, r, options, true);
11388 * Loads data from a passed data block. A Reader which understands the format of the data
11389 * must have been configured in the constructor.
11390 * @param {Object} data The data block from which to read the Records. The format of the data expected
11391 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11392 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11394 loadData : function(o, append){
11395 var r = this.reader.readRecords(o);
11396 this.loadRecords(r, {add: append}, true);
11400 * Gets the number of cached records.
11402 * <em>If using paging, this may not be the total size of the dataset. If the data object
11403 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11404 * the data set size</em>
11406 getCount : function(){
11407 return this.data.length || 0;
11411 * Gets the total number of records in the dataset as returned by the server.
11413 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11414 * the dataset size</em>
11416 getTotalCount : function(){
11417 return this.totalLength || 0;
11421 * Returns the sort state of the Store as an object with two properties:
11423 field {String} The name of the field by which the Records are sorted
11424 direction {String} The sort order, "ASC" or "DESC"
11427 getSortState : function(){
11428 return this.sortInfo;
11432 applySort : function(){
11433 if(this.sortInfo && !this.remoteSort){
11434 var s = this.sortInfo, f = s.field;
11435 var st = this.fields.get(f).sortType;
11436 var fn = function(r1, r2){
11437 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11438 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11440 this.data.sort(s.direction, fn);
11441 if(this.snapshot && this.snapshot != this.data){
11442 this.snapshot.sort(s.direction, fn);
11448 * Sets the default sort column and order to be used by the next load operation.
11449 * @param {String} fieldName The name of the field to sort by.
11450 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11452 setDefaultSort : function(field, dir){
11453 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11457 * Sort the Records.
11458 * If remote sorting is used, the sort is performed on the server, and the cache is
11459 * reloaded. If local sorting is used, the cache is sorted internally.
11460 * @param {String} fieldName The name of the field to sort by.
11461 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11463 sort : function(fieldName, dir){
11464 var f = this.fields.get(fieldName);
11466 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11468 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11469 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11474 this.sortToggle[f.name] = dir;
11475 this.sortInfo = {field: f.name, direction: dir};
11476 if(!this.remoteSort){
11478 this.fireEvent("datachanged", this);
11480 this.load(this.lastOptions);
11485 * Calls the specified function for each of the Records in the cache.
11486 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11487 * Returning <em>false</em> aborts and exits the iteration.
11488 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11490 each : function(fn, scope){
11491 this.data.each(fn, scope);
11495 * Gets all records modified since the last commit. Modified records are persisted across load operations
11496 * (e.g., during paging).
11497 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11499 getModifiedRecords : function(){
11500 return this.modified;
11504 createFilterFn : function(property, value, anyMatch){
11505 if(!value.exec){ // not a regex
11506 value = String(value);
11507 if(value.length == 0){
11510 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11512 return function(r){
11513 return value.test(r.data[property]);
11518 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11519 * @param {String} property A field on your records
11520 * @param {Number} start The record index to start at (defaults to 0)
11521 * @param {Number} end The last record index to include (defaults to length - 1)
11522 * @return {Number} The sum
11524 sum : function(property, start, end){
11525 var rs = this.data.items, v = 0;
11526 start = start || 0;
11527 end = (end || end === 0) ? end : rs.length-1;
11529 for(var i = start; i <= end; i++){
11530 v += (rs[i].data[property] || 0);
11536 * Filter the records by a specified property.
11537 * @param {String} field A field on your records
11538 * @param {String/RegExp} value Either a string that the field
11539 * should start with or a RegExp to test against the field
11540 * @param {Boolean} anyMatch True to match any part not just the beginning
11542 filter : function(property, value, anyMatch){
11543 var fn = this.createFilterFn(property, value, anyMatch);
11544 return fn ? this.filterBy(fn) : this.clearFilter();
11548 * Filter by a function. The specified function will be called with each
11549 * record in this data source. If the function returns true the record is included,
11550 * otherwise it is filtered.
11551 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11552 * @param {Object} scope (optional) The scope of the function (defaults to this)
11554 filterBy : function(fn, scope){
11555 this.snapshot = this.snapshot || this.data;
11556 this.data = this.queryBy(fn, scope||this);
11557 this.fireEvent("datachanged", this);
11561 * Query the records by a specified property.
11562 * @param {String} field A field on your records
11563 * @param {String/RegExp} value Either a string that the field
11564 * should start with or a RegExp to test against the field
11565 * @param {Boolean} anyMatch True to match any part not just the beginning
11566 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11568 query : function(property, value, anyMatch){
11569 var fn = this.createFilterFn(property, value, anyMatch);
11570 return fn ? this.queryBy(fn) : this.data.clone();
11574 * Query by a function. The specified function will be called with each
11575 * record in this data source. If the function returns true the record is included
11577 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11578 * @param {Object} scope (optional) The scope of the function (defaults to this)
11579 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11581 queryBy : function(fn, scope){
11582 var data = this.snapshot || this.data;
11583 return data.filterBy(fn, scope||this);
11587 * Collects unique values for a particular dataIndex from this store.
11588 * @param {String} dataIndex The property to collect
11589 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11590 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11591 * @return {Array} An array of the unique values
11593 collect : function(dataIndex, allowNull, bypassFilter){
11594 var d = (bypassFilter === true && this.snapshot) ?
11595 this.snapshot.items : this.data.items;
11596 var v, sv, r = [], l = {};
11597 for(var i = 0, len = d.length; i < len; i++){
11598 v = d[i].data[dataIndex];
11600 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11609 * Revert to a view of the Record cache with no filtering applied.
11610 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11612 clearFilter : function(suppressEvent){
11613 if(this.snapshot && this.snapshot != this.data){
11614 this.data = this.snapshot;
11615 delete this.snapshot;
11616 if(suppressEvent !== true){
11617 this.fireEvent("datachanged", this);
11623 afterEdit : function(record){
11624 if(this.modified.indexOf(record) == -1){
11625 this.modified.push(record);
11627 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11631 afterReject : function(record){
11632 this.modified.remove(record);
11633 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11637 afterCommit : function(record){
11638 this.modified.remove(record);
11639 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11643 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11644 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11646 commitChanges : function(){
11647 var m = this.modified.slice(0);
11648 this.modified = [];
11649 for(var i = 0, len = m.length; i < len; i++){
11655 * Cancel outstanding changes on all changed records.
11657 rejectChanges : function(){
11658 var m = this.modified.slice(0);
11659 this.modified = [];
11660 for(var i = 0, len = m.length; i < len; i++){
11665 onMetaChange : function(meta, rtype, o){
11666 this.recordType = rtype;
11667 this.fields = rtype.prototype.fields;
11668 delete this.snapshot;
11669 this.sortInfo = meta.sortInfo || this.sortInfo;
11670 this.modified = [];
11671 this.fireEvent('metachange', this, this.reader.meta);
11674 moveIndex : function(data, type)
11676 var index = this.indexOf(data);
11678 var newIndex = index + type;
11682 this.insert(newIndex, data);
11687 * Ext JS Library 1.1.1
11688 * Copyright(c) 2006-2007, Ext JS, LLC.
11690 * Originally Released Under LGPL - original licence link has changed is not relivant.
11693 * <script type="text/javascript">
11697 * @class Roo.data.SimpleStore
11698 * @extends Roo.data.Store
11699 * Small helper class to make creating Stores from Array data easier.
11700 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11701 * @cfg {Array} fields An array of field definition objects, or field name strings.
11702 * @cfg {Array} data The multi-dimensional array of data
11704 * @param {Object} config
11706 Roo.data.SimpleStore = function(config){
11707 Roo.data.SimpleStore.superclass.constructor.call(this, {
11709 reader: new Roo.data.ArrayReader({
11712 Roo.data.Record.create(config.fields)
11714 proxy : new Roo.data.MemoryProxy(config.data)
11718 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11720 * Ext JS Library 1.1.1
11721 * Copyright(c) 2006-2007, Ext JS, LLC.
11723 * Originally Released Under LGPL - original licence link has changed is not relivant.
11726 * <script type="text/javascript">
11731 * @extends Roo.data.Store
11732 * @class Roo.data.JsonStore
11733 * Small helper class to make creating Stores for JSON data easier. <br/>
11735 var store = new Roo.data.JsonStore({
11736 url: 'get-images.php',
11738 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11741 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11742 * JsonReader and HttpProxy (unless inline data is provided).</b>
11743 * @cfg {Array} fields An array of field definition objects, or field name strings.
11745 * @param {Object} config
11747 Roo.data.JsonStore = function(c){
11748 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11749 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11750 reader: new Roo.data.JsonReader(c, c.fields)
11753 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11755 * Ext JS Library 1.1.1
11756 * Copyright(c) 2006-2007, Ext JS, LLC.
11758 * Originally Released Under LGPL - original licence link has changed is not relivant.
11761 * <script type="text/javascript">
11765 Roo.data.Field = function(config){
11766 if(typeof config == "string"){
11767 config = {name: config};
11769 Roo.apply(this, config);
11772 this.type = "auto";
11775 var st = Roo.data.SortTypes;
11776 // named sortTypes are supported, here we look them up
11777 if(typeof this.sortType == "string"){
11778 this.sortType = st[this.sortType];
11781 // set default sortType for strings and dates
11782 if(!this.sortType){
11785 this.sortType = st.asUCString;
11788 this.sortType = st.asDate;
11791 this.sortType = st.none;
11796 var stripRe = /[\$,%]/g;
11798 // prebuilt conversion function for this field, instead of
11799 // switching every time we're reading a value
11801 var cv, dateFormat = this.dateFormat;
11806 cv = function(v){ return v; };
11809 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11813 return v !== undefined && v !== null && v !== '' ?
11814 parseInt(String(v).replace(stripRe, ""), 10) : '';
11819 return v !== undefined && v !== null && v !== '' ?
11820 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11825 cv = function(v){ return v === true || v === "true" || v == 1; };
11832 if(v instanceof Date){
11836 if(dateFormat == "timestamp"){
11837 return new Date(v*1000);
11839 return Date.parseDate(v, dateFormat);
11841 var parsed = Date.parse(v);
11842 return parsed ? new Date(parsed) : null;
11851 Roo.data.Field.prototype = {
11859 * Ext JS Library 1.1.1
11860 * Copyright(c) 2006-2007, Ext JS, LLC.
11862 * Originally Released Under LGPL - original licence link has changed is not relivant.
11865 * <script type="text/javascript">
11868 // Base class for reading structured data from a data source. This class is intended to be
11869 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11872 * @class Roo.data.DataReader
11873 * Base class for reading structured data from a data source. This class is intended to be
11874 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11877 Roo.data.DataReader = function(meta, recordType){
11881 this.recordType = recordType instanceof Array ?
11882 Roo.data.Record.create(recordType) : recordType;
11885 Roo.data.DataReader.prototype = {
11887 * Create an empty record
11888 * @param {Object} data (optional) - overlay some values
11889 * @return {Roo.data.Record} record created.
11891 newRow : function(d) {
11893 this.recordType.prototype.fields.each(function(c) {
11895 case 'int' : da[c.name] = 0; break;
11896 case 'date' : da[c.name] = new Date(); break;
11897 case 'float' : da[c.name] = 0.0; break;
11898 case 'boolean' : da[c.name] = false; break;
11899 default : da[c.name] = ""; break;
11903 return new this.recordType(Roo.apply(da, d));
11908 * Ext JS Library 1.1.1
11909 * Copyright(c) 2006-2007, Ext JS, LLC.
11911 * Originally Released Under LGPL - original licence link has changed is not relivant.
11914 * <script type="text/javascript">
11918 * @class Roo.data.DataProxy
11919 * @extends Roo.data.Observable
11920 * This class is an abstract base class for implementations which provide retrieval of
11921 * unformatted data objects.<br>
11923 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11924 * (of the appropriate type which knows how to parse the data object) to provide a block of
11925 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11927 * Custom implementations must implement the load method as described in
11928 * {@link Roo.data.HttpProxy#load}.
11930 Roo.data.DataProxy = function(){
11933 * @event beforeload
11934 * Fires before a network request is made to retrieve a data object.
11935 * @param {Object} This DataProxy object.
11936 * @param {Object} params The params parameter to the load function.
11941 * Fires before the load method's callback is called.
11942 * @param {Object} This DataProxy object.
11943 * @param {Object} o The data object.
11944 * @param {Object} arg The callback argument object passed to the load function.
11948 * @event loadexception
11949 * Fires if an Exception occurs during data retrieval.
11950 * @param {Object} This DataProxy object.
11951 * @param {Object} o The data object.
11952 * @param {Object} arg The callback argument object passed to the load function.
11953 * @param {Object} e The Exception.
11955 loadexception : true
11957 Roo.data.DataProxy.superclass.constructor.call(this);
11960 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11963 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11967 * Ext JS Library 1.1.1
11968 * Copyright(c) 2006-2007, Ext JS, LLC.
11970 * Originally Released Under LGPL - original licence link has changed is not relivant.
11973 * <script type="text/javascript">
11976 * @class Roo.data.MemoryProxy
11977 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11978 * to the Reader when its load method is called.
11980 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11982 Roo.data.MemoryProxy = function(data){
11986 Roo.data.MemoryProxy.superclass.constructor.call(this);
11990 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11993 * Load data from the requested source (in this case an in-memory
11994 * data object passed to the constructor), read the data object into
11995 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11996 * process that block using the passed callback.
11997 * @param {Object} params This parameter is not used by the MemoryProxy class.
11998 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11999 * object into a block of Roo.data.Records.
12000 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12001 * The function must be passed <ul>
12002 * <li>The Record block object</li>
12003 * <li>The "arg" argument from the load function</li>
12004 * <li>A boolean success indicator</li>
12006 * @param {Object} scope The scope in which to call the callback
12007 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12009 load : function(params, reader, callback, scope, arg){
12010 params = params || {};
12013 result = reader.readRecords(this.data);
12015 this.fireEvent("loadexception", this, arg, null, e);
12016 callback.call(scope, null, arg, false);
12019 callback.call(scope, result, arg, true);
12023 update : function(params, records){
12028 * Ext JS Library 1.1.1
12029 * Copyright(c) 2006-2007, Ext JS, LLC.
12031 * Originally Released Under LGPL - original licence link has changed is not relivant.
12034 * <script type="text/javascript">
12037 * @class Roo.data.HttpProxy
12038 * @extends Roo.data.DataProxy
12039 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12040 * configured to reference a certain URL.<br><br>
12042 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12043 * from which the running page was served.<br><br>
12045 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12047 * Be aware that to enable the browser to parse an XML document, the server must set
12048 * the Content-Type header in the HTTP response to "text/xml".
12050 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12051 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12052 * will be used to make the request.
12054 Roo.data.HttpProxy = function(conn){
12055 Roo.data.HttpProxy.superclass.constructor.call(this);
12056 // is conn a conn config or a real conn?
12058 this.useAjax = !conn || !conn.events;
12062 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12063 // thse are take from connection...
12066 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12069 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12070 * extra parameters to each request made by this object. (defaults to undefined)
12073 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12074 * to each request made by this object. (defaults to undefined)
12077 * @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)
12080 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12083 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12089 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12093 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12094 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12095 * a finer-grained basis than the DataProxy events.
12097 getConnection : function(){
12098 return this.useAjax ? Roo.Ajax : this.conn;
12102 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12103 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12104 * process that block using the passed callback.
12105 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12106 * for the request to the remote server.
12107 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12108 * object into a block of Roo.data.Records.
12109 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12110 * The function must be passed <ul>
12111 * <li>The Record block object</li>
12112 * <li>The "arg" argument from the load function</li>
12113 * <li>A boolean success indicator</li>
12115 * @param {Object} scope The scope in which to call the callback
12116 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12118 load : function(params, reader, callback, scope, arg){
12119 if(this.fireEvent("beforeload", this, params) !== false){
12121 params : params || {},
12123 callback : callback,
12128 callback : this.loadResponse,
12132 Roo.applyIf(o, this.conn);
12133 if(this.activeRequest){
12134 Roo.Ajax.abort(this.activeRequest);
12136 this.activeRequest = Roo.Ajax.request(o);
12138 this.conn.request(o);
12141 callback.call(scope||this, null, arg, false);
12146 loadResponse : function(o, success, response){
12147 delete this.activeRequest;
12149 this.fireEvent("loadexception", this, o, response);
12150 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12155 result = o.reader.read(response);
12157 this.fireEvent("loadexception", this, o, response, e);
12158 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12162 this.fireEvent("load", this, o, o.request.arg);
12163 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12167 update : function(dataSet){
12172 updateResponse : function(dataSet){
12177 * Ext JS Library 1.1.1
12178 * Copyright(c) 2006-2007, Ext JS, LLC.
12180 * Originally Released Under LGPL - original licence link has changed is not relivant.
12183 * <script type="text/javascript">
12187 * @class Roo.data.ScriptTagProxy
12188 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12189 * other than the originating domain of the running page.<br><br>
12191 * <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
12192 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12194 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12195 * source code that is used as the source inside a <script> tag.<br><br>
12197 * In order for the browser to process the returned data, the server must wrap the data object
12198 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12199 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12200 * depending on whether the callback name was passed:
12203 boolean scriptTag = false;
12204 String cb = request.getParameter("callback");
12207 response.setContentType("text/javascript");
12209 response.setContentType("application/x-json");
12211 Writer out = response.getWriter();
12213 out.write(cb + "(");
12215 out.print(dataBlock.toJsonString());
12222 * @param {Object} config A configuration object.
12224 Roo.data.ScriptTagProxy = function(config){
12225 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12226 Roo.apply(this, config);
12227 this.head = document.getElementsByTagName("head")[0];
12230 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12232 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12234 * @cfg {String} url The URL from which to request the data object.
12237 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12241 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12242 * the server the name of the callback function set up by the load call to process the returned data object.
12243 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12244 * javascript output which calls this named function passing the data object as its only parameter.
12246 callbackParam : "callback",
12248 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12249 * name to the request.
12254 * Load data from the configured URL, read the data object into
12255 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12256 * process that block using the passed callback.
12257 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12258 * for the request to the remote server.
12259 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12260 * object into a block of Roo.data.Records.
12261 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12262 * The function must be passed <ul>
12263 * <li>The Record block object</li>
12264 * <li>The "arg" argument from the load function</li>
12265 * <li>A boolean success indicator</li>
12267 * @param {Object} scope The scope in which to call the callback
12268 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12270 load : function(params, reader, callback, scope, arg){
12271 if(this.fireEvent("beforeload", this, params) !== false){
12273 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12275 var url = this.url;
12276 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12278 url += "&_dc=" + (new Date().getTime());
12280 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12283 cb : "stcCallback"+transId,
12284 scriptId : "stcScript"+transId,
12288 callback : callback,
12294 window[trans.cb] = function(o){
12295 conn.handleResponse(o, trans);
12298 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12300 if(this.autoAbort !== false){
12304 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12306 var script = document.createElement("script");
12307 script.setAttribute("src", url);
12308 script.setAttribute("type", "text/javascript");
12309 script.setAttribute("id", trans.scriptId);
12310 this.head.appendChild(script);
12312 this.trans = trans;
12314 callback.call(scope||this, null, arg, false);
12319 isLoading : function(){
12320 return this.trans ? true : false;
12324 * Abort the current server request.
12326 abort : function(){
12327 if(this.isLoading()){
12328 this.destroyTrans(this.trans);
12333 destroyTrans : function(trans, isLoaded){
12334 this.head.removeChild(document.getElementById(trans.scriptId));
12335 clearTimeout(trans.timeoutId);
12337 window[trans.cb] = undefined;
12339 delete window[trans.cb];
12342 // if hasn't been loaded, wait for load to remove it to prevent script error
12343 window[trans.cb] = function(){
12344 window[trans.cb] = undefined;
12346 delete window[trans.cb];
12353 handleResponse : function(o, trans){
12354 this.trans = false;
12355 this.destroyTrans(trans, true);
12358 result = trans.reader.readRecords(o);
12360 this.fireEvent("loadexception", this, o, trans.arg, e);
12361 trans.callback.call(trans.scope||window, null, trans.arg, false);
12364 this.fireEvent("load", this, o, trans.arg);
12365 trans.callback.call(trans.scope||window, result, trans.arg, true);
12369 handleFailure : function(trans){
12370 this.trans = false;
12371 this.destroyTrans(trans, false);
12372 this.fireEvent("loadexception", this, null, trans.arg);
12373 trans.callback.call(trans.scope||window, null, trans.arg, false);
12377 * Ext JS Library 1.1.1
12378 * Copyright(c) 2006-2007, Ext JS, LLC.
12380 * Originally Released Under LGPL - original licence link has changed is not relivant.
12383 * <script type="text/javascript">
12387 * @class Roo.data.JsonReader
12388 * @extends Roo.data.DataReader
12389 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12390 * based on mappings in a provided Roo.data.Record constructor.
12392 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12393 * in the reply previously.
12398 var RecordDef = Roo.data.Record.create([
12399 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12400 {name: 'occupation'} // This field will use "occupation" as the mapping.
12402 var myReader = new Roo.data.JsonReader({
12403 totalProperty: "results", // The property which contains the total dataset size (optional)
12404 root: "rows", // The property which contains an Array of row objects
12405 id: "id" // The property within each row object that provides an ID for the record (optional)
12409 * This would consume a JSON file like this:
12411 { 'results': 2, 'rows': [
12412 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12413 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12416 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12417 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12418 * paged from the remote server.
12419 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12420 * @cfg {String} root name of the property which contains the Array of row objects.
12421 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12422 * @cfg {Array} fields Array of field definition objects
12424 * Create a new JsonReader
12425 * @param {Object} meta Metadata configuration options
12426 * @param {Object} recordType Either an Array of field definition objects,
12427 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12429 Roo.data.JsonReader = function(meta, recordType){
12432 // set some defaults:
12433 Roo.applyIf(meta, {
12434 totalProperty: 'total',
12435 successProperty : 'success',
12440 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12442 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12445 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12446 * Used by Store query builder to append _requestMeta to params.
12449 metaFromRemote : false,
12451 * This method is only used by a DataProxy which has retrieved data from a remote server.
12452 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12453 * @return {Object} data A data block which is used by an Roo.data.Store object as
12454 * a cache of Roo.data.Records.
12456 read : function(response){
12457 var json = response.responseText;
12459 var o = /* eval:var:o */ eval("("+json+")");
12461 throw {message: "JsonReader.read: Json object not found"};
12467 this.metaFromRemote = true;
12468 this.meta = o.metaData;
12469 this.recordType = Roo.data.Record.create(o.metaData.fields);
12470 this.onMetaChange(this.meta, this.recordType, o);
12472 return this.readRecords(o);
12475 // private function a store will implement
12476 onMetaChange : function(meta, recordType, o){
12483 simpleAccess: function(obj, subsc) {
12490 getJsonAccessor: function(){
12492 return function(expr) {
12494 return(re.test(expr))
12495 ? new Function("obj", "return obj." + expr)
12500 return Roo.emptyFn;
12505 * Create a data block containing Roo.data.Records from an XML document.
12506 * @param {Object} o An object which contains an Array of row objects in the property specified
12507 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12508 * which contains the total size of the dataset.
12509 * @return {Object} data A data block which is used by an Roo.data.Store object as
12510 * a cache of Roo.data.Records.
12512 readRecords : function(o){
12514 * After any data loads, the raw JSON data is available for further custom processing.
12518 var s = this.meta, Record = this.recordType,
12519 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12521 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12523 if(s.totalProperty) {
12524 this.getTotal = this.getJsonAccessor(s.totalProperty);
12526 if(s.successProperty) {
12527 this.getSuccess = this.getJsonAccessor(s.successProperty);
12529 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12531 var g = this.getJsonAccessor(s.id);
12532 this.getId = function(rec) {
12534 return (r === undefined || r === "") ? null : r;
12537 this.getId = function(){return null;};
12540 for(var jj = 0; jj < fl; jj++){
12542 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12543 this.ef[jj] = this.getJsonAccessor(map);
12547 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12548 if(s.totalProperty){
12549 var vt = parseInt(this.getTotal(o), 10);
12554 if(s.successProperty){
12555 var vs = this.getSuccess(o);
12556 if(vs === false || vs === 'false'){
12561 for(var i = 0; i < c; i++){
12564 var id = this.getId(n);
12565 for(var j = 0; j < fl; j++){
12567 var v = this.ef[j](n);
12569 Roo.log('missing convert for ' + f.name);
12573 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12575 var record = new Record(values, id);
12577 records[i] = record;
12583 totalRecords : totalRecords
12588 * Ext JS Library 1.1.1
12589 * Copyright(c) 2006-2007, Ext JS, LLC.
12591 * Originally Released Under LGPL - original licence link has changed is not relivant.
12594 * <script type="text/javascript">
12598 * @class Roo.data.ArrayReader
12599 * @extends Roo.data.DataReader
12600 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12601 * Each element of that Array represents a row of data fields. The
12602 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12603 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12607 var RecordDef = Roo.data.Record.create([
12608 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12609 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12611 var myReader = new Roo.data.ArrayReader({
12612 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12616 * This would consume an Array like this:
12618 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12620 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12622 * Create a new JsonReader
12623 * @param {Object} meta Metadata configuration options.
12624 * @param {Object} recordType Either an Array of field definition objects
12625 * as specified to {@link Roo.data.Record#create},
12626 * or an {@link Roo.data.Record} object
12627 * created using {@link Roo.data.Record#create}.
12629 Roo.data.ArrayReader = function(meta, recordType){
12630 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12633 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12635 * Create a data block containing Roo.data.Records from an XML document.
12636 * @param {Object} o An Array of row objects which represents the dataset.
12637 * @return {Object} data A data block which is used by an Roo.data.Store object as
12638 * a cache of Roo.data.Records.
12640 readRecords : function(o){
12641 var sid = this.meta ? this.meta.id : null;
12642 var recordType = this.recordType, fields = recordType.prototype.fields;
12645 for(var i = 0; i < root.length; i++){
12648 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12649 for(var j = 0, jlen = fields.length; j < jlen; j++){
12650 var f = fields.items[j];
12651 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12652 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12654 values[f.name] = v;
12656 var record = new recordType(values, id);
12658 records[records.length] = record;
12662 totalRecords : records.length
12671 * @class Roo.bootstrap.ComboBox
12672 * @extends Roo.bootstrap.TriggerField
12673 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12674 * @cfg {Boolean} append (true|false) default false
12675 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12676 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12677 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12678 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12679 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12680 * @cfg {Boolean} animate default true
12681 * @cfg {Boolean} emptyResultText only for touch device
12682 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12683 * @cfg {String} emptyTitle default ''
12685 * Create a new ComboBox.
12686 * @param {Object} config Configuration options
12688 Roo.bootstrap.ComboBox = function(config){
12689 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12693 * Fires when the dropdown list is expanded
12694 * @param {Roo.bootstrap.ComboBox} combo This combo box
12699 * Fires when the dropdown list is collapsed
12700 * @param {Roo.bootstrap.ComboBox} combo This combo box
12704 * @event beforeselect
12705 * Fires before a list item is selected. Return false to cancel the selection.
12706 * @param {Roo.bootstrap.ComboBox} combo This combo box
12707 * @param {Roo.data.Record} record The data record returned from the underlying store
12708 * @param {Number} index The index of the selected item in the dropdown list
12710 'beforeselect' : true,
12713 * Fires when a list item is selected
12714 * @param {Roo.bootstrap.ComboBox} combo This combo box
12715 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12716 * @param {Number} index The index of the selected item in the dropdown list
12720 * @event beforequery
12721 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12722 * The event object passed has these properties:
12723 * @param {Roo.bootstrap.ComboBox} combo This combo box
12724 * @param {String} query The query
12725 * @param {Boolean} forceAll true to force "all" query
12726 * @param {Boolean} cancel true to cancel the query
12727 * @param {Object} e The query event object
12729 'beforequery': true,
12732 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12733 * @param {Roo.bootstrap.ComboBox} combo This combo box
12738 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12739 * @param {Roo.bootstrap.ComboBox} combo This combo box
12740 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12745 * Fires when the remove value from the combobox array
12746 * @param {Roo.bootstrap.ComboBox} combo This combo box
12750 * @event afterremove
12751 * Fires when the remove value from the combobox array
12752 * @param {Roo.bootstrap.ComboBox} combo This combo box
12754 'afterremove' : true,
12756 * @event specialfilter
12757 * Fires when specialfilter
12758 * @param {Roo.bootstrap.ComboBox} combo This combo box
12760 'specialfilter' : true,
12763 * Fires when tick the element
12764 * @param {Roo.bootstrap.ComboBox} combo This combo box
12768 * @event touchviewdisplay
12769 * Fires when touch view require special display (default is using displayField)
12770 * @param {Roo.bootstrap.ComboBox} combo This combo box
12771 * @param {Object} cfg set html .
12773 'touchviewdisplay' : true
12778 this.tickItems = [];
12780 this.selectedIndex = -1;
12781 if(this.mode == 'local'){
12782 if(config.queryDelay === undefined){
12783 this.queryDelay = 10;
12785 if(config.minChars === undefined){
12791 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12794 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12795 * rendering into an Roo.Editor, defaults to false)
12798 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12799 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12802 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12805 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12806 * the dropdown list (defaults to undefined, with no header element)
12810 * @cfg {String/Roo.Template} tpl The template to use to render the output
12814 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12816 listWidth: undefined,
12818 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12819 * mode = 'remote' or 'text' if mode = 'local')
12821 displayField: undefined,
12824 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12825 * mode = 'remote' or 'value' if mode = 'local').
12826 * Note: use of a valueField requires the user make a selection
12827 * in order for a value to be mapped.
12829 valueField: undefined,
12831 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12836 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12837 * field's data value (defaults to the underlying DOM element's name)
12839 hiddenName: undefined,
12841 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12845 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12847 selectedClass: 'active',
12850 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12854 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12855 * anchor positions (defaults to 'tl-bl')
12857 listAlign: 'tl-bl?',
12859 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12863 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12864 * query specified by the allQuery config option (defaults to 'query')
12866 triggerAction: 'query',
12868 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12869 * (defaults to 4, does not apply if editable = false)
12873 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12874 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12878 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12879 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12883 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12884 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12888 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12889 * when editable = true (defaults to false)
12891 selectOnFocus:false,
12893 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12895 queryParam: 'query',
12897 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12898 * when mode = 'remote' (defaults to 'Loading...')
12900 loadingText: 'Loading...',
12902 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12906 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12910 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12911 * traditional select (defaults to true)
12915 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12919 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12923 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12924 * listWidth has a higher value)
12928 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12929 * allow the user to set arbitrary text into the field (defaults to false)
12931 forceSelection:false,
12933 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12934 * if typeAhead = true (defaults to 250)
12936 typeAheadDelay : 250,
12938 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12939 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12941 valueNotFoundText : undefined,
12943 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12945 blockFocus : false,
12948 * @cfg {Boolean} disableClear Disable showing of clear button.
12950 disableClear : false,
12952 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12954 alwaysQuery : false,
12957 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12962 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12964 invalidClass : "has-warning",
12967 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12969 validClass : "has-success",
12972 * @cfg {Boolean} specialFilter (true|false) special filter default false
12974 specialFilter : false,
12977 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12979 mobileTouchView : true,
12982 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12984 useNativeIOS : false,
12986 ios_options : false,
12998 btnPosition : 'right',
12999 triggerList : true,
13000 showToggleBtn : true,
13002 emptyResultText: 'Empty',
13003 triggerText : 'Select',
13006 // element that contains real text value.. (when hidden is used..)
13008 getAutoCreate : function()
13013 * Render classic select for iso
13016 if(Roo.isIOS && this.useNativeIOS){
13017 cfg = this.getAutoCreateNativeIOS();
13025 if(Roo.isTouch && this.mobileTouchView){
13026 cfg = this.getAutoCreateTouchView();
13033 if(!this.tickable){
13034 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13039 * ComboBox with tickable selections
13042 var align = this.labelAlign || this.parentLabelAlign();
13045 cls : 'form-group roo-combobox-tickable' //input-group
13048 var btn_text_select = '';
13049 var btn_text_done = '';
13050 var btn_text_cancel = '';
13052 if (this.btn_text_show) {
13053 btn_text_select = 'Select';
13054 btn_text_done = 'Done';
13055 btn_text_cancel = 'Cancel';
13060 cls : 'tickable-buttons',
13065 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13066 //html : this.triggerText
13067 html: btn_text_select
13073 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13075 html: btn_text_done
13081 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13083 html: btn_text_cancel
13089 buttons.cn.unshift({
13091 cls: 'roo-select2-search-field-input'
13097 Roo.each(buttons.cn, function(c){
13099 c.cls += ' btn-' + _this.size;
13102 if (_this.disabled) {
13113 cls: 'form-hidden-field'
13117 cls: 'roo-select2-choices',
13121 cls: 'roo-select2-search-field',
13132 cls: 'roo-select2-container input-group roo-select2-container-multi',
13137 // cls: 'typeahead typeahead-long dropdown-menu',
13138 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13143 if(this.hasFeedback && !this.allowBlank){
13147 cls: 'glyphicon form-control-feedback'
13150 combobox.cn.push(feedback);
13154 if (align ==='left' && this.fieldLabel.length) {
13156 cfg.cls += ' roo-form-group-label-left';
13161 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13162 tooltip : 'This field is required'
13167 cls : 'control-label',
13168 html : this.fieldLabel
13180 var labelCfg = cfg.cn[1];
13181 var contentCfg = cfg.cn[2];
13184 if(this.indicatorpos == 'right'){
13190 cls : 'control-label',
13194 html : this.fieldLabel
13198 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13199 tooltip : 'This field is required'
13214 labelCfg = cfg.cn[0];
13215 contentCfg = cfg.cn[1];
13219 if(this.labelWidth > 12){
13220 labelCfg.style = "width: " + this.labelWidth + 'px';
13223 if(this.labelWidth < 13 && this.labelmd == 0){
13224 this.labelmd = this.labelWidth;
13227 if(this.labellg > 0){
13228 labelCfg.cls += ' col-lg-' + this.labellg;
13229 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13232 if(this.labelmd > 0){
13233 labelCfg.cls += ' col-md-' + this.labelmd;
13234 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13237 if(this.labelsm > 0){
13238 labelCfg.cls += ' col-sm-' + this.labelsm;
13239 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13242 if(this.labelxs > 0){
13243 labelCfg.cls += ' col-xs-' + this.labelxs;
13244 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13248 } else if ( this.fieldLabel.length) {
13249 // Roo.log(" label");
13253 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13254 tooltip : 'This field is required'
13258 //cls : 'input-group-addon',
13259 html : this.fieldLabel
13264 if(this.indicatorpos == 'right'){
13268 //cls : 'input-group-addon',
13269 html : this.fieldLabel
13273 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13274 tooltip : 'This field is required'
13283 // Roo.log(" no label && no align");
13290 ['xs','sm','md','lg'].map(function(size){
13291 if (settings[size]) {
13292 cfg.cls += ' col-' + size + '-' + settings[size];
13300 _initEventsCalled : false,
13303 initEvents: function()
13305 if (this._initEventsCalled) { // as we call render... prevent looping...
13308 this._initEventsCalled = true;
13311 throw "can not find store for combo";
13314 this.indicator = this.indicatorEl();
13316 this.store = Roo.factory(this.store, Roo.data);
13317 this.store.parent = this;
13319 // if we are building from html. then this element is so complex, that we can not really
13320 // use the rendered HTML.
13321 // so we have to trash and replace the previous code.
13322 if (Roo.XComponent.build_from_html) {
13323 // remove this element....
13324 var e = this.el.dom, k=0;
13325 while (e ) { e = e.previousSibling; ++k;}
13330 this.rendered = false;
13332 this.render(this.parent().getChildContainer(true), k);
13335 if(Roo.isIOS && this.useNativeIOS){
13336 this.initIOSView();
13344 if(Roo.isTouch && this.mobileTouchView){
13345 this.initTouchView();
13350 this.initTickableEvents();
13354 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13356 if(this.hiddenName){
13358 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13360 this.hiddenField.dom.value =
13361 this.hiddenValue !== undefined ? this.hiddenValue :
13362 this.value !== undefined ? this.value : '';
13364 // prevent input submission
13365 this.el.dom.removeAttribute('name');
13366 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13371 // this.el.dom.setAttribute('autocomplete', 'off');
13374 var cls = 'x-combo-list';
13376 //this.list = new Roo.Layer({
13377 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13383 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13384 _this.list.setWidth(lw);
13387 this.list.on('mouseover', this.onViewOver, this);
13388 this.list.on('mousemove', this.onViewMove, this);
13389 this.list.on('scroll', this.onViewScroll, this);
13392 this.list.swallowEvent('mousewheel');
13393 this.assetHeight = 0;
13396 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13397 this.assetHeight += this.header.getHeight();
13400 this.innerList = this.list.createChild({cls:cls+'-inner'});
13401 this.innerList.on('mouseover', this.onViewOver, this);
13402 this.innerList.on('mousemove', this.onViewMove, this);
13403 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13405 if(this.allowBlank && !this.pageSize && !this.disableClear){
13406 this.footer = this.list.createChild({cls:cls+'-ft'});
13407 this.pageTb = new Roo.Toolbar(this.footer);
13411 this.footer = this.list.createChild({cls:cls+'-ft'});
13412 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13413 {pageSize: this.pageSize});
13417 if (this.pageTb && this.allowBlank && !this.disableClear) {
13419 this.pageTb.add(new Roo.Toolbar.Fill(), {
13420 cls: 'x-btn-icon x-btn-clear',
13422 handler: function()
13425 _this.clearValue();
13426 _this.onSelect(false, -1);
13431 this.assetHeight += this.footer.getHeight();
13436 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13439 this.view = new Roo.View(this.list, this.tpl, {
13440 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13442 //this.view.wrapEl.setDisplayed(false);
13443 this.view.on('click', this.onViewClick, this);
13446 this.store.on('beforeload', this.onBeforeLoad, this);
13447 this.store.on('load', this.onLoad, this);
13448 this.store.on('loadexception', this.onLoadException, this);
13450 if(this.resizable){
13451 this.resizer = new Roo.Resizable(this.list, {
13452 pinned:true, handles:'se'
13454 this.resizer.on('resize', function(r, w, h){
13455 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13456 this.listWidth = w;
13457 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13458 this.restrictHeight();
13460 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13463 if(!this.editable){
13464 this.editable = true;
13465 this.setEditable(false);
13470 if (typeof(this.events.add.listeners) != 'undefined') {
13472 this.addicon = this.wrap.createChild(
13473 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13475 this.addicon.on('click', function(e) {
13476 this.fireEvent('add', this);
13479 if (typeof(this.events.edit.listeners) != 'undefined') {
13481 this.editicon = this.wrap.createChild(
13482 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13483 if (this.addicon) {
13484 this.editicon.setStyle('margin-left', '40px');
13486 this.editicon.on('click', function(e) {
13488 // we fire even if inothing is selected..
13489 this.fireEvent('edit', this, this.lastData );
13495 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13496 "up" : function(e){
13497 this.inKeyMode = true;
13501 "down" : function(e){
13502 if(!this.isExpanded()){
13503 this.onTriggerClick();
13505 this.inKeyMode = true;
13510 "enter" : function(e){
13511 // this.onViewClick();
13515 if(this.fireEvent("specialkey", this, e)){
13516 this.onViewClick(false);
13522 "esc" : function(e){
13526 "tab" : function(e){
13529 if(this.fireEvent("specialkey", this, e)){
13530 this.onViewClick(false);
13538 doRelay : function(foo, bar, hname){
13539 if(hname == 'down' || this.scope.isExpanded()){
13540 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13549 this.queryDelay = Math.max(this.queryDelay || 10,
13550 this.mode == 'local' ? 10 : 250);
13553 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13555 if(this.typeAhead){
13556 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13558 if(this.editable !== false){
13559 this.inputEl().on("keyup", this.onKeyUp, this);
13561 if(this.forceSelection){
13562 this.inputEl().on('blur', this.doForce, this);
13566 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13567 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13571 initTickableEvents: function()
13575 if(this.hiddenName){
13577 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13579 this.hiddenField.dom.value =
13580 this.hiddenValue !== undefined ? this.hiddenValue :
13581 this.value !== undefined ? this.value : '';
13583 // prevent input submission
13584 this.el.dom.removeAttribute('name');
13585 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13590 // this.list = this.el.select('ul.dropdown-menu',true).first();
13592 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13593 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13594 if(this.triggerList){
13595 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13598 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13599 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13601 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13602 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13604 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13605 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13607 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13608 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13609 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13612 this.cancelBtn.hide();
13617 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13618 _this.list.setWidth(lw);
13621 this.list.on('mouseover', this.onViewOver, this);
13622 this.list.on('mousemove', this.onViewMove, this);
13624 this.list.on('scroll', this.onViewScroll, this);
13627 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13628 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13631 this.view = new Roo.View(this.list, this.tpl, {
13636 selectedClass: this.selectedClass
13639 //this.view.wrapEl.setDisplayed(false);
13640 this.view.on('click', this.onViewClick, this);
13644 this.store.on('beforeload', this.onBeforeLoad, this);
13645 this.store.on('load', this.onLoad, this);
13646 this.store.on('loadexception', this.onLoadException, this);
13649 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13650 "up" : function(e){
13651 this.inKeyMode = true;
13655 "down" : function(e){
13656 this.inKeyMode = true;
13660 "enter" : function(e){
13661 if(this.fireEvent("specialkey", this, e)){
13662 this.onViewClick(false);
13668 "esc" : function(e){
13669 this.onTickableFooterButtonClick(e, false, false);
13672 "tab" : function(e){
13673 this.fireEvent("specialkey", this, e);
13675 this.onTickableFooterButtonClick(e, false, false);
13682 doRelay : function(e, fn, key){
13683 if(this.scope.isExpanded()){
13684 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13693 this.queryDelay = Math.max(this.queryDelay || 10,
13694 this.mode == 'local' ? 10 : 250);
13697 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13699 if(this.typeAhead){
13700 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13703 if(this.editable !== false){
13704 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13707 this.indicator = this.indicatorEl();
13709 if(this.indicator){
13710 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13711 this.indicator.hide();
13716 onDestroy : function(){
13718 this.view.setStore(null);
13719 this.view.el.removeAllListeners();
13720 this.view.el.remove();
13721 this.view.purgeListeners();
13724 this.list.dom.innerHTML = '';
13728 this.store.un('beforeload', this.onBeforeLoad, this);
13729 this.store.un('load', this.onLoad, this);
13730 this.store.un('loadexception', this.onLoadException, this);
13732 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13736 fireKey : function(e){
13737 if(e.isNavKeyPress() && !this.list.isVisible()){
13738 this.fireEvent("specialkey", this, e);
13743 onResize: function(w, h){
13744 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13746 // if(typeof w != 'number'){
13747 // // we do not handle it!?!?
13750 // var tw = this.trigger.getWidth();
13751 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13752 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13754 // this.inputEl().setWidth( this.adjustWidth('input', x));
13756 // //this.trigger.setStyle('left', x+'px');
13758 // if(this.list && this.listWidth === undefined){
13759 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13760 // this.list.setWidth(lw);
13761 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13769 * Allow or prevent the user from directly editing the field text. If false is passed,
13770 * the user will only be able to select from the items defined in the dropdown list. This method
13771 * is the runtime equivalent of setting the 'editable' config option at config time.
13772 * @param {Boolean} value True to allow the user to directly edit the field text
13774 setEditable : function(value){
13775 if(value == this.editable){
13778 this.editable = value;
13780 this.inputEl().dom.setAttribute('readOnly', true);
13781 this.inputEl().on('mousedown', this.onTriggerClick, this);
13782 this.inputEl().addClass('x-combo-noedit');
13784 this.inputEl().dom.setAttribute('readOnly', false);
13785 this.inputEl().un('mousedown', this.onTriggerClick, this);
13786 this.inputEl().removeClass('x-combo-noedit');
13792 onBeforeLoad : function(combo,opts){
13793 if(!this.hasFocus){
13797 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13799 this.restrictHeight();
13800 this.selectedIndex = -1;
13804 onLoad : function(){
13806 this.hasQuery = false;
13808 if(!this.hasFocus){
13812 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13813 this.loading.hide();
13816 if(this.store.getCount() > 0){
13819 this.restrictHeight();
13820 if(this.lastQuery == this.allQuery){
13821 if(this.editable && !this.tickable){
13822 this.inputEl().dom.select();
13826 !this.selectByValue(this.value, true) &&
13829 !this.store.lastOptions ||
13830 typeof(this.store.lastOptions.add) == 'undefined' ||
13831 this.store.lastOptions.add != true
13834 this.select(0, true);
13837 if(this.autoFocus){
13840 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13841 this.taTask.delay(this.typeAheadDelay);
13845 this.onEmptyResults();
13851 onLoadException : function()
13853 this.hasQuery = false;
13855 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13856 this.loading.hide();
13859 if(this.tickable && this.editable){
13864 // only causes errors at present
13865 //Roo.log(this.store.reader.jsonData);
13866 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13868 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13874 onTypeAhead : function(){
13875 if(this.store.getCount() > 0){
13876 var r = this.store.getAt(0);
13877 var newValue = r.data[this.displayField];
13878 var len = newValue.length;
13879 var selStart = this.getRawValue().length;
13881 if(selStart != len){
13882 this.setRawValue(newValue);
13883 this.selectText(selStart, newValue.length);
13889 onSelect : function(record, index){
13891 if(this.fireEvent('beforeselect', this, record, index) !== false){
13893 this.setFromData(index > -1 ? record.data : false);
13896 this.fireEvent('select', this, record, index);
13901 * Returns the currently selected field value or empty string if no value is set.
13902 * @return {String} value The selected value
13904 getValue : function()
13906 if(Roo.isIOS && this.useNativeIOS){
13907 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13911 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13914 if(this.valueField){
13915 return typeof this.value != 'undefined' ? this.value : '';
13917 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13921 getRawValue : function()
13923 if(Roo.isIOS && this.useNativeIOS){
13924 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13927 var v = this.inputEl().getValue();
13933 * Clears any text/value currently set in the field
13935 clearValue : function(){
13937 if(this.hiddenField){
13938 this.hiddenField.dom.value = '';
13941 this.setRawValue('');
13942 this.lastSelectionText = '';
13943 this.lastData = false;
13945 var close = this.closeTriggerEl();
13956 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13957 * will be displayed in the field. If the value does not match the data value of an existing item,
13958 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13959 * Otherwise the field will be blank (although the value will still be set).
13960 * @param {String} value The value to match
13962 setValue : function(v)
13964 if(Roo.isIOS && this.useNativeIOS){
13965 this.setIOSValue(v);
13975 if(this.valueField){
13976 var r = this.findRecord(this.valueField, v);
13978 text = r.data[this.displayField];
13979 }else if(this.valueNotFoundText !== undefined){
13980 text = this.valueNotFoundText;
13983 this.lastSelectionText = text;
13984 if(this.hiddenField){
13985 this.hiddenField.dom.value = v;
13987 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13990 var close = this.closeTriggerEl();
13993 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13999 * @property {Object} the last set data for the element
14004 * Sets the value of the field based on a object which is related to the record format for the store.
14005 * @param {Object} value the value to set as. or false on reset?
14007 setFromData : function(o){
14014 var dv = ''; // display value
14015 var vv = ''; // value value..
14017 if (this.displayField) {
14018 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14020 // this is an error condition!!!
14021 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14024 if(this.valueField){
14025 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14028 var close = this.closeTriggerEl();
14031 if(dv.length || vv * 1 > 0){
14033 this.blockFocus=true;
14039 if(this.hiddenField){
14040 this.hiddenField.dom.value = vv;
14042 this.lastSelectionText = dv;
14043 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14047 // no hidden field.. - we store the value in 'value', but still display
14048 // display field!!!!
14049 this.lastSelectionText = dv;
14050 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14057 reset : function(){
14058 // overridden so that last data is reset..
14065 this.setValue(this.originalValue);
14066 //this.clearInvalid();
14067 this.lastData = false;
14069 this.view.clearSelections();
14075 findRecord : function(prop, value){
14077 if(this.store.getCount() > 0){
14078 this.store.each(function(r){
14079 if(r.data[prop] == value){
14089 getName: function()
14091 // returns hidden if it's set..
14092 if (!this.rendered) {return ''};
14093 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14097 onViewMove : function(e, t){
14098 this.inKeyMode = false;
14102 onViewOver : function(e, t){
14103 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14106 var item = this.view.findItemFromChild(t);
14109 var index = this.view.indexOf(item);
14110 this.select(index, false);
14115 onViewClick : function(view, doFocus, el, e)
14117 var index = this.view.getSelectedIndexes()[0];
14119 var r = this.store.getAt(index);
14123 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14130 Roo.each(this.tickItems, function(v,k){
14132 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14134 _this.tickItems.splice(k, 1);
14136 if(typeof(e) == 'undefined' && view == false){
14137 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14149 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14150 this.tickItems.push(r.data);
14153 if(typeof(e) == 'undefined' && view == false){
14154 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14161 this.onSelect(r, index);
14163 if(doFocus !== false && !this.blockFocus){
14164 this.inputEl().focus();
14169 restrictHeight : function(){
14170 //this.innerList.dom.style.height = '';
14171 //var inner = this.innerList.dom;
14172 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14173 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14174 //this.list.beginUpdate();
14175 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14176 this.list.alignTo(this.inputEl(), this.listAlign);
14177 this.list.alignTo(this.inputEl(), this.listAlign);
14178 //this.list.endUpdate();
14182 onEmptyResults : function(){
14184 if(this.tickable && this.editable){
14185 this.hasFocus = false;
14186 this.restrictHeight();
14194 * Returns true if the dropdown list is expanded, else false.
14196 isExpanded : function(){
14197 return this.list.isVisible();
14201 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14202 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14203 * @param {String} value The data value of the item to select
14204 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14205 * selected item if it is not currently in view (defaults to true)
14206 * @return {Boolean} True if the value matched an item in the list, else false
14208 selectByValue : function(v, scrollIntoView){
14209 if(v !== undefined && v !== null){
14210 var r = this.findRecord(this.valueField || this.displayField, v);
14212 this.select(this.store.indexOf(r), scrollIntoView);
14220 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14221 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14222 * @param {Number} index The zero-based index of the list item to select
14223 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14224 * selected item if it is not currently in view (defaults to true)
14226 select : function(index, scrollIntoView){
14227 this.selectedIndex = index;
14228 this.view.select(index);
14229 if(scrollIntoView !== false){
14230 var el = this.view.getNode(index);
14232 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14235 this.list.scrollChildIntoView(el, false);
14241 selectNext : function(){
14242 var ct = this.store.getCount();
14244 if(this.selectedIndex == -1){
14246 }else if(this.selectedIndex < ct-1){
14247 this.select(this.selectedIndex+1);
14253 selectPrev : function(){
14254 var ct = this.store.getCount();
14256 if(this.selectedIndex == -1){
14258 }else if(this.selectedIndex != 0){
14259 this.select(this.selectedIndex-1);
14265 onKeyUp : function(e){
14266 if(this.editable !== false && !e.isSpecialKey()){
14267 this.lastKey = e.getKey();
14268 this.dqTask.delay(this.queryDelay);
14273 validateBlur : function(){
14274 return !this.list || !this.list.isVisible();
14278 initQuery : function(){
14280 var v = this.getRawValue();
14282 if(this.tickable && this.editable){
14283 v = this.tickableInputEl().getValue();
14290 doForce : function(){
14291 if(this.inputEl().dom.value.length > 0){
14292 this.inputEl().dom.value =
14293 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14299 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14300 * query allowing the query action to be canceled if needed.
14301 * @param {String} query The SQL query to execute
14302 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14303 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14304 * saved in the current store (defaults to false)
14306 doQuery : function(q, forceAll){
14308 if(q === undefined || q === null){
14313 forceAll: forceAll,
14317 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14322 forceAll = qe.forceAll;
14323 if(forceAll === true || (q.length >= this.minChars)){
14325 this.hasQuery = true;
14327 if(this.lastQuery != q || this.alwaysQuery){
14328 this.lastQuery = q;
14329 if(this.mode == 'local'){
14330 this.selectedIndex = -1;
14332 this.store.clearFilter();
14335 if(this.specialFilter){
14336 this.fireEvent('specialfilter', this);
14341 this.store.filter(this.displayField, q);
14344 this.store.fireEvent("datachanged", this.store);
14351 this.store.baseParams[this.queryParam] = q;
14353 var options = {params : this.getParams(q)};
14356 options.add = true;
14357 options.params.start = this.page * this.pageSize;
14360 this.store.load(options);
14363 * this code will make the page width larger, at the beginning, the list not align correctly,
14364 * we should expand the list on onLoad
14365 * so command out it
14370 this.selectedIndex = -1;
14375 this.loadNext = false;
14379 getParams : function(q){
14381 //p[this.queryParam] = q;
14385 p.limit = this.pageSize;
14391 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14393 collapse : function(){
14394 if(!this.isExpanded()){
14400 this.hasFocus = false;
14404 this.cancelBtn.hide();
14405 this.trigger.show();
14408 this.tickableInputEl().dom.value = '';
14409 this.tickableInputEl().blur();
14414 Roo.get(document).un('mousedown', this.collapseIf, this);
14415 Roo.get(document).un('mousewheel', this.collapseIf, this);
14416 if (!this.editable) {
14417 Roo.get(document).un('keydown', this.listKeyPress, this);
14419 this.fireEvent('collapse', this);
14425 collapseIf : function(e){
14426 var in_combo = e.within(this.el);
14427 var in_list = e.within(this.list);
14428 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14430 if (in_combo || in_list || is_list) {
14431 //e.stopPropagation();
14436 this.onTickableFooterButtonClick(e, false, false);
14444 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14446 expand : function(){
14448 if(this.isExpanded() || !this.hasFocus){
14452 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14453 this.list.setWidth(lw);
14459 this.restrictHeight();
14463 this.tickItems = Roo.apply([], this.item);
14466 this.cancelBtn.show();
14467 this.trigger.hide();
14470 this.tickableInputEl().focus();
14475 Roo.get(document).on('mousedown', this.collapseIf, this);
14476 Roo.get(document).on('mousewheel', this.collapseIf, this);
14477 if (!this.editable) {
14478 Roo.get(document).on('keydown', this.listKeyPress, this);
14481 this.fireEvent('expand', this);
14485 // Implements the default empty TriggerField.onTriggerClick function
14486 onTriggerClick : function(e)
14488 Roo.log('trigger click');
14490 if(this.disabled || !this.triggerList){
14495 this.loadNext = false;
14497 if(this.isExpanded()){
14499 if (!this.blockFocus) {
14500 this.inputEl().focus();
14504 this.hasFocus = true;
14505 if(this.triggerAction == 'all') {
14506 this.doQuery(this.allQuery, true);
14508 this.doQuery(this.getRawValue());
14510 if (!this.blockFocus) {
14511 this.inputEl().focus();
14516 onTickableTriggerClick : function(e)
14523 this.loadNext = false;
14524 this.hasFocus = true;
14526 if(this.triggerAction == 'all') {
14527 this.doQuery(this.allQuery, true);
14529 this.doQuery(this.getRawValue());
14533 onSearchFieldClick : function(e)
14535 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14536 this.onTickableFooterButtonClick(e, false, false);
14540 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14545 this.loadNext = false;
14546 this.hasFocus = true;
14548 if(this.triggerAction == 'all') {
14549 this.doQuery(this.allQuery, true);
14551 this.doQuery(this.getRawValue());
14555 listKeyPress : function(e)
14557 //Roo.log('listkeypress');
14558 // scroll to first matching element based on key pres..
14559 if (e.isSpecialKey()) {
14562 var k = String.fromCharCode(e.getKey()).toUpperCase();
14565 var csel = this.view.getSelectedNodes();
14566 var cselitem = false;
14568 var ix = this.view.indexOf(csel[0]);
14569 cselitem = this.store.getAt(ix);
14570 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14576 this.store.each(function(v) {
14578 // start at existing selection.
14579 if (cselitem.id == v.id) {
14585 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14586 match = this.store.indexOf(v);
14592 if (match === false) {
14593 return true; // no more action?
14596 this.view.select(match);
14597 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14598 sn.scrollIntoView(sn.dom.parentNode, false);
14601 onViewScroll : function(e, t){
14603 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){
14607 this.hasQuery = true;
14609 this.loading = this.list.select('.loading', true).first();
14611 if(this.loading === null){
14612 this.list.createChild({
14614 cls: 'loading roo-select2-more-results roo-select2-active',
14615 html: 'Loading more results...'
14618 this.loading = this.list.select('.loading', true).first();
14620 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14622 this.loading.hide();
14625 this.loading.show();
14630 this.loadNext = true;
14632 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14637 addItem : function(o)
14639 var dv = ''; // display value
14641 if (this.displayField) {
14642 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14644 // this is an error condition!!!
14645 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14652 var choice = this.choices.createChild({
14654 cls: 'roo-select2-search-choice',
14663 cls: 'roo-select2-search-choice-close fa fa-times',
14668 }, this.searchField);
14670 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14672 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14680 this.inputEl().dom.value = '';
14685 onRemoveItem : function(e, _self, o)
14687 e.preventDefault();
14689 this.lastItem = Roo.apply([], this.item);
14691 var index = this.item.indexOf(o.data) * 1;
14694 Roo.log('not this item?!');
14698 this.item.splice(index, 1);
14703 this.fireEvent('remove', this, e);
14709 syncValue : function()
14711 if(!this.item.length){
14718 Roo.each(this.item, function(i){
14719 if(_this.valueField){
14720 value.push(i[_this.valueField]);
14727 this.value = value.join(',');
14729 if(this.hiddenField){
14730 this.hiddenField.dom.value = this.value;
14733 this.store.fireEvent("datachanged", this.store);
14738 clearItem : function()
14740 if(!this.multiple){
14746 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14754 if(this.tickable && !Roo.isTouch){
14755 this.view.refresh();
14759 inputEl: function ()
14761 if(Roo.isIOS && this.useNativeIOS){
14762 return this.el.select('select.roo-ios-select', true).first();
14765 if(Roo.isTouch && this.mobileTouchView){
14766 return this.el.select('input.form-control',true).first();
14770 return this.searchField;
14773 return this.el.select('input.form-control',true).first();
14776 onTickableFooterButtonClick : function(e, btn, el)
14778 e.preventDefault();
14780 this.lastItem = Roo.apply([], this.item);
14782 if(btn && btn.name == 'cancel'){
14783 this.tickItems = Roo.apply([], this.item);
14792 Roo.each(this.tickItems, function(o){
14800 validate : function()
14802 if(this.getVisibilityEl().hasClass('hidden')){
14806 var v = this.getRawValue();
14809 v = this.getValue();
14812 if(this.disabled || this.allowBlank || v.length){
14817 this.markInvalid();
14821 tickableInputEl : function()
14823 if(!this.tickable || !this.editable){
14824 return this.inputEl();
14827 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14831 getAutoCreateTouchView : function()
14836 cls: 'form-group' //input-group
14842 type : this.inputType,
14843 cls : 'form-control x-combo-noedit',
14844 autocomplete: 'new-password',
14845 placeholder : this.placeholder || '',
14850 input.name = this.name;
14854 input.cls += ' input-' + this.size;
14857 if (this.disabled) {
14858 input.disabled = true;
14869 inputblock.cls += ' input-group';
14871 inputblock.cn.unshift({
14873 cls : 'input-group-addon',
14878 if(this.removable && !this.multiple){
14879 inputblock.cls += ' roo-removable';
14881 inputblock.cn.push({
14884 cls : 'roo-combo-removable-btn close'
14888 if(this.hasFeedback && !this.allowBlank){
14890 inputblock.cls += ' has-feedback';
14892 inputblock.cn.push({
14894 cls: 'glyphicon form-control-feedback'
14901 inputblock.cls += (this.before) ? '' : ' input-group';
14903 inputblock.cn.push({
14905 cls : 'input-group-addon',
14916 cls: 'form-hidden-field'
14930 cls: 'form-hidden-field'
14934 cls: 'roo-select2-choices',
14938 cls: 'roo-select2-search-field',
14951 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14957 if(!this.multiple && this.showToggleBtn){
14964 if (this.caret != false) {
14967 cls: 'fa fa-' + this.caret
14974 cls : 'input-group-addon btn dropdown-toggle',
14979 cls: 'combobox-clear',
14993 combobox.cls += ' roo-select2-container-multi';
14996 var align = this.labelAlign || this.parentLabelAlign();
14998 if (align ==='left' && this.fieldLabel.length) {
15003 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15004 tooltip : 'This field is required'
15008 cls : 'control-label',
15009 html : this.fieldLabel
15020 var labelCfg = cfg.cn[1];
15021 var contentCfg = cfg.cn[2];
15024 if(this.indicatorpos == 'right'){
15029 cls : 'control-label',
15033 html : this.fieldLabel
15037 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15038 tooltip : 'This field is required'
15051 labelCfg = cfg.cn[0];
15052 contentCfg = cfg.cn[1];
15057 if(this.labelWidth > 12){
15058 labelCfg.style = "width: " + this.labelWidth + 'px';
15061 if(this.labelWidth < 13 && this.labelmd == 0){
15062 this.labelmd = this.labelWidth;
15065 if(this.labellg > 0){
15066 labelCfg.cls += ' col-lg-' + this.labellg;
15067 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15070 if(this.labelmd > 0){
15071 labelCfg.cls += ' col-md-' + this.labelmd;
15072 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15075 if(this.labelsm > 0){
15076 labelCfg.cls += ' col-sm-' + this.labelsm;
15077 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15080 if(this.labelxs > 0){
15081 labelCfg.cls += ' col-xs-' + this.labelxs;
15082 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15086 } else if ( this.fieldLabel.length) {
15090 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15091 tooltip : 'This field is required'
15095 cls : 'control-label',
15096 html : this.fieldLabel
15107 if(this.indicatorpos == 'right'){
15111 cls : 'control-label',
15112 html : this.fieldLabel,
15116 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15117 tooltip : 'This field is required'
15134 var settings = this;
15136 ['xs','sm','md','lg'].map(function(size){
15137 if (settings[size]) {
15138 cfg.cls += ' col-' + size + '-' + settings[size];
15145 initTouchView : function()
15147 this.renderTouchView();
15149 this.touchViewEl.on('scroll', function(){
15150 this.el.dom.scrollTop = 0;
15153 this.originalValue = this.getValue();
15155 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15157 this.inputEl().on("click", this.showTouchView, this);
15158 if (this.triggerEl) {
15159 this.triggerEl.on("click", this.showTouchView, this);
15163 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15164 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15166 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15168 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15169 this.store.on('load', this.onTouchViewLoad, this);
15170 this.store.on('loadexception', this.onTouchViewLoadException, this);
15172 if(this.hiddenName){
15174 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15176 this.hiddenField.dom.value =
15177 this.hiddenValue !== undefined ? this.hiddenValue :
15178 this.value !== undefined ? this.value : '';
15180 this.el.dom.removeAttribute('name');
15181 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15185 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15186 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15189 if(this.removable && !this.multiple){
15190 var close = this.closeTriggerEl();
15192 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15193 close.on('click', this.removeBtnClick, this, close);
15197 * fix the bug in Safari iOS8
15199 this.inputEl().on("focus", function(e){
15200 document.activeElement.blur();
15208 renderTouchView : function()
15210 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15211 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15213 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15214 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15216 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15217 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15218 this.touchViewBodyEl.setStyle('overflow', 'auto');
15220 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15221 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15223 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15224 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15228 showTouchView : function()
15234 this.touchViewHeaderEl.hide();
15236 if(this.modalTitle.length){
15237 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15238 this.touchViewHeaderEl.show();
15241 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15242 this.touchViewEl.show();
15244 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15246 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15247 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15249 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15251 if(this.modalTitle.length){
15252 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15255 this.touchViewBodyEl.setHeight(bodyHeight);
15259 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15261 this.touchViewEl.addClass('in');
15264 this.doTouchViewQuery();
15268 hideTouchView : function()
15270 this.touchViewEl.removeClass('in');
15274 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15276 this.touchViewEl.setStyle('display', 'none');
15281 setTouchViewValue : function()
15288 Roo.each(this.tickItems, function(o){
15293 this.hideTouchView();
15296 doTouchViewQuery : function()
15305 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15309 if(!this.alwaysQuery || this.mode == 'local'){
15310 this.onTouchViewLoad();
15317 onTouchViewBeforeLoad : function(combo,opts)
15323 onTouchViewLoad : function()
15325 if(this.store.getCount() < 1){
15326 this.onTouchViewEmptyResults();
15330 this.clearTouchView();
15332 var rawValue = this.getRawValue();
15334 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15336 this.tickItems = [];
15338 this.store.data.each(function(d, rowIndex){
15339 var row = this.touchViewListGroup.createChild(template);
15341 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15342 row.addClass(d.data.cls);
15345 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15348 html : d.data[this.displayField]
15351 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15352 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15355 row.removeClass('selected');
15356 if(!this.multiple && this.valueField &&
15357 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15360 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15361 row.addClass('selected');
15364 if(this.multiple && this.valueField &&
15365 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15369 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15370 this.tickItems.push(d.data);
15373 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15377 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15379 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15381 if(this.modalTitle.length){
15382 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15385 var listHeight = this.touchViewListGroup.getHeight();
15389 if(firstChecked && listHeight > bodyHeight){
15390 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15395 onTouchViewLoadException : function()
15397 this.hideTouchView();
15400 onTouchViewEmptyResults : function()
15402 this.clearTouchView();
15404 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15406 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15410 clearTouchView : function()
15412 this.touchViewListGroup.dom.innerHTML = '';
15415 onTouchViewClick : function(e, el, o)
15417 e.preventDefault();
15420 var rowIndex = o.rowIndex;
15422 var r = this.store.getAt(rowIndex);
15424 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15426 if(!this.multiple){
15427 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15428 c.dom.removeAttribute('checked');
15431 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15433 this.setFromData(r.data);
15435 var close = this.closeTriggerEl();
15441 this.hideTouchView();
15443 this.fireEvent('select', this, r, rowIndex);
15448 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15449 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15450 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15454 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15455 this.addItem(r.data);
15456 this.tickItems.push(r.data);
15460 getAutoCreateNativeIOS : function()
15463 cls: 'form-group' //input-group,
15468 cls : 'roo-ios-select'
15472 combobox.name = this.name;
15475 if (this.disabled) {
15476 combobox.disabled = true;
15479 var settings = this;
15481 ['xs','sm','md','lg'].map(function(size){
15482 if (settings[size]) {
15483 cfg.cls += ' col-' + size + '-' + settings[size];
15493 initIOSView : function()
15495 this.store.on('load', this.onIOSViewLoad, this);
15500 onIOSViewLoad : function()
15502 if(this.store.getCount() < 1){
15506 this.clearIOSView();
15508 if(this.allowBlank) {
15510 var default_text = '-- SELECT --';
15512 if(this.placeholder.length){
15513 default_text = this.placeholder;
15516 if(this.emptyTitle.length){
15517 default_text += ' - ' + this.emptyTitle + ' -';
15520 var opt = this.inputEl().createChild({
15523 html : default_text
15527 o[this.valueField] = 0;
15528 o[this.displayField] = default_text;
15530 this.ios_options.push({
15537 this.store.data.each(function(d, rowIndex){
15541 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15542 html = d.data[this.displayField];
15547 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15548 value = d.data[this.valueField];
15557 if(this.value == d.data[this.valueField]){
15558 option['selected'] = true;
15561 var opt = this.inputEl().createChild(option);
15563 this.ios_options.push({
15570 this.inputEl().on('change', function(){
15571 this.fireEvent('select', this);
15576 clearIOSView: function()
15578 this.inputEl().dom.innerHTML = '';
15580 this.ios_options = [];
15583 setIOSValue: function(v)
15587 if(!this.ios_options){
15591 Roo.each(this.ios_options, function(opts){
15593 opts.el.dom.removeAttribute('selected');
15595 if(opts.data[this.valueField] != v){
15599 opts.el.dom.setAttribute('selected', true);
15605 * @cfg {Boolean} grow
15609 * @cfg {Number} growMin
15613 * @cfg {Number} growMax
15622 Roo.apply(Roo.bootstrap.ComboBox, {
15626 cls: 'modal-header',
15648 cls: 'list-group-item',
15652 cls: 'roo-combobox-list-group-item-value'
15656 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15670 listItemCheckbox : {
15672 cls: 'list-group-item',
15676 cls: 'roo-combobox-list-group-item-value'
15680 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15696 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15701 cls: 'modal-footer',
15709 cls: 'col-xs-6 text-left',
15712 cls: 'btn btn-danger roo-touch-view-cancel',
15718 cls: 'col-xs-6 text-right',
15721 cls: 'btn btn-success roo-touch-view-ok',
15732 Roo.apply(Roo.bootstrap.ComboBox, {
15734 touchViewTemplate : {
15736 cls: 'modal fade roo-combobox-touch-view',
15740 cls: 'modal-dialog',
15741 style : 'position:fixed', // we have to fix position....
15745 cls: 'modal-content',
15747 Roo.bootstrap.ComboBox.header,
15748 Roo.bootstrap.ComboBox.body,
15749 Roo.bootstrap.ComboBox.footer
15758 * Ext JS Library 1.1.1
15759 * Copyright(c) 2006-2007, Ext JS, LLC.
15761 * Originally Released Under LGPL - original licence link has changed is not relivant.
15764 * <script type="text/javascript">
15769 * @extends Roo.util.Observable
15770 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15771 * This class also supports single and multi selection modes. <br>
15772 * Create a data model bound view:
15774 var store = new Roo.data.Store(...);
15776 var view = new Roo.View({
15778 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15780 singleSelect: true,
15781 selectedClass: "ydataview-selected",
15785 // listen for node click?
15786 view.on("click", function(vw, index, node, e){
15787 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15791 dataModel.load("foobar.xml");
15793 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15795 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15796 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15798 * Note: old style constructor is still suported (container, template, config)
15801 * Create a new View
15802 * @param {Object} config The config object
15805 Roo.View = function(config, depreciated_tpl, depreciated_config){
15807 this.parent = false;
15809 if (typeof(depreciated_tpl) == 'undefined') {
15810 // new way.. - universal constructor.
15811 Roo.apply(this, config);
15812 this.el = Roo.get(this.el);
15815 this.el = Roo.get(config);
15816 this.tpl = depreciated_tpl;
15817 Roo.apply(this, depreciated_config);
15819 this.wrapEl = this.el.wrap().wrap();
15820 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15823 if(typeof(this.tpl) == "string"){
15824 this.tpl = new Roo.Template(this.tpl);
15826 // support xtype ctors..
15827 this.tpl = new Roo.factory(this.tpl, Roo);
15831 this.tpl.compile();
15836 * @event beforeclick
15837 * Fires before a click is processed. Returns false to cancel the default action.
15838 * @param {Roo.View} this
15839 * @param {Number} index The index of the target node
15840 * @param {HTMLElement} node The target node
15841 * @param {Roo.EventObject} e The raw event object
15843 "beforeclick" : true,
15846 * Fires when a template node is clicked.
15847 * @param {Roo.View} this
15848 * @param {Number} index The index of the target node
15849 * @param {HTMLElement} node The target node
15850 * @param {Roo.EventObject} e The raw event object
15855 * Fires when a template node is double clicked.
15856 * @param {Roo.View} this
15857 * @param {Number} index The index of the target node
15858 * @param {HTMLElement} node The target node
15859 * @param {Roo.EventObject} e The raw event object
15863 * @event contextmenu
15864 * Fires when a template node is right clicked.
15865 * @param {Roo.View} this
15866 * @param {Number} index The index of the target node
15867 * @param {HTMLElement} node The target node
15868 * @param {Roo.EventObject} e The raw event object
15870 "contextmenu" : true,
15872 * @event selectionchange
15873 * Fires when the selected nodes change.
15874 * @param {Roo.View} this
15875 * @param {Array} selections Array of the selected nodes
15877 "selectionchange" : true,
15880 * @event beforeselect
15881 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15882 * @param {Roo.View} this
15883 * @param {HTMLElement} node The node to be selected
15884 * @param {Array} selections Array of currently selected nodes
15886 "beforeselect" : true,
15888 * @event preparedata
15889 * Fires on every row to render, to allow you to change the data.
15890 * @param {Roo.View} this
15891 * @param {Object} data to be rendered (change this)
15893 "preparedata" : true
15901 "click": this.onClick,
15902 "dblclick": this.onDblClick,
15903 "contextmenu": this.onContextMenu,
15907 this.selections = [];
15909 this.cmp = new Roo.CompositeElementLite([]);
15911 this.store = Roo.factory(this.store, Roo.data);
15912 this.setStore(this.store, true);
15915 if ( this.footer && this.footer.xtype) {
15917 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15919 this.footer.dataSource = this.store;
15920 this.footer.container = fctr;
15921 this.footer = Roo.factory(this.footer, Roo);
15922 fctr.insertFirst(this.el);
15924 // this is a bit insane - as the paging toolbar seems to detach the el..
15925 // dom.parentNode.parentNode.parentNode
15926 // they get detached?
15930 Roo.View.superclass.constructor.call(this);
15935 Roo.extend(Roo.View, Roo.util.Observable, {
15938 * @cfg {Roo.data.Store} store Data store to load data from.
15943 * @cfg {String|Roo.Element} el The container element.
15948 * @cfg {String|Roo.Template} tpl The template used by this View
15952 * @cfg {String} dataName the named area of the template to use as the data area
15953 * Works with domtemplates roo-name="name"
15957 * @cfg {String} selectedClass The css class to add to selected nodes
15959 selectedClass : "x-view-selected",
15961 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15966 * @cfg {String} text to display on mask (default Loading)
15970 * @cfg {Boolean} multiSelect Allow multiple selection
15972 multiSelect : false,
15974 * @cfg {Boolean} singleSelect Allow single selection
15976 singleSelect: false,
15979 * @cfg {Boolean} toggleSelect - selecting
15981 toggleSelect : false,
15984 * @cfg {Boolean} tickable - selecting
15989 * Returns the element this view is bound to.
15990 * @return {Roo.Element}
15992 getEl : function(){
15993 return this.wrapEl;
15999 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16001 refresh : function(){
16002 //Roo.log('refresh');
16005 // if we are using something like 'domtemplate', then
16006 // the what gets used is:
16007 // t.applySubtemplate(NAME, data, wrapping data..)
16008 // the outer template then get' applied with
16009 // the store 'extra data'
16010 // and the body get's added to the
16011 // roo-name="data" node?
16012 // <span class='roo-tpl-{name}'></span> ?????
16016 this.clearSelections();
16017 this.el.update("");
16019 var records = this.store.getRange();
16020 if(records.length < 1) {
16022 // is this valid?? = should it render a template??
16024 this.el.update(this.emptyText);
16028 if (this.dataName) {
16029 this.el.update(t.apply(this.store.meta)); //????
16030 el = this.el.child('.roo-tpl-' + this.dataName);
16033 for(var i = 0, len = records.length; i < len; i++){
16034 var data = this.prepareData(records[i].data, i, records[i]);
16035 this.fireEvent("preparedata", this, data, i, records[i]);
16037 var d = Roo.apply({}, data);
16040 Roo.apply(d, {'roo-id' : Roo.id()});
16044 Roo.each(this.parent.item, function(item){
16045 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16048 Roo.apply(d, {'roo-data-checked' : 'checked'});
16052 html[html.length] = Roo.util.Format.trim(
16054 t.applySubtemplate(this.dataName, d, this.store.meta) :
16061 el.update(html.join(""));
16062 this.nodes = el.dom.childNodes;
16063 this.updateIndexes(0);
16068 * Function to override to reformat the data that is sent to
16069 * the template for each node.
16070 * DEPRICATED - use the preparedata event handler.
16071 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16072 * a JSON object for an UpdateManager bound view).
16074 prepareData : function(data, index, record)
16076 this.fireEvent("preparedata", this, data, index, record);
16080 onUpdate : function(ds, record){
16081 // Roo.log('on update');
16082 this.clearSelections();
16083 var index = this.store.indexOf(record);
16084 var n = this.nodes[index];
16085 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16086 n.parentNode.removeChild(n);
16087 this.updateIndexes(index, index);
16093 onAdd : function(ds, records, index)
16095 //Roo.log(['on Add', ds, records, index] );
16096 this.clearSelections();
16097 if(this.nodes.length == 0){
16101 var n = this.nodes[index];
16102 for(var i = 0, len = records.length; i < len; i++){
16103 var d = this.prepareData(records[i].data, i, records[i]);
16105 this.tpl.insertBefore(n, d);
16108 this.tpl.append(this.el, d);
16111 this.updateIndexes(index);
16114 onRemove : function(ds, record, index){
16115 // Roo.log('onRemove');
16116 this.clearSelections();
16117 var el = this.dataName ?
16118 this.el.child('.roo-tpl-' + this.dataName) :
16121 el.dom.removeChild(this.nodes[index]);
16122 this.updateIndexes(index);
16126 * Refresh an individual node.
16127 * @param {Number} index
16129 refreshNode : function(index){
16130 this.onUpdate(this.store, this.store.getAt(index));
16133 updateIndexes : function(startIndex, endIndex){
16134 var ns = this.nodes;
16135 startIndex = startIndex || 0;
16136 endIndex = endIndex || ns.length - 1;
16137 for(var i = startIndex; i <= endIndex; i++){
16138 ns[i].nodeIndex = i;
16143 * Changes the data store this view uses and refresh the view.
16144 * @param {Store} store
16146 setStore : function(store, initial){
16147 if(!initial && this.store){
16148 this.store.un("datachanged", this.refresh);
16149 this.store.un("add", this.onAdd);
16150 this.store.un("remove", this.onRemove);
16151 this.store.un("update", this.onUpdate);
16152 this.store.un("clear", this.refresh);
16153 this.store.un("beforeload", this.onBeforeLoad);
16154 this.store.un("load", this.onLoad);
16155 this.store.un("loadexception", this.onLoad);
16159 store.on("datachanged", this.refresh, this);
16160 store.on("add", this.onAdd, this);
16161 store.on("remove", this.onRemove, this);
16162 store.on("update", this.onUpdate, this);
16163 store.on("clear", this.refresh, this);
16164 store.on("beforeload", this.onBeforeLoad, this);
16165 store.on("load", this.onLoad, this);
16166 store.on("loadexception", this.onLoad, this);
16174 * onbeforeLoad - masks the loading area.
16177 onBeforeLoad : function(store,opts)
16179 //Roo.log('onBeforeLoad');
16181 this.el.update("");
16183 this.el.mask(this.mask ? this.mask : "Loading" );
16185 onLoad : function ()
16192 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16193 * @param {HTMLElement} node
16194 * @return {HTMLElement} The template node
16196 findItemFromChild : function(node){
16197 var el = this.dataName ?
16198 this.el.child('.roo-tpl-' + this.dataName,true) :
16201 if(!node || node.parentNode == el){
16204 var p = node.parentNode;
16205 while(p && p != el){
16206 if(p.parentNode == el){
16215 onClick : function(e){
16216 var item = this.findItemFromChild(e.getTarget());
16218 var index = this.indexOf(item);
16219 if(this.onItemClick(item, index, e) !== false){
16220 this.fireEvent("click", this, index, item, e);
16223 this.clearSelections();
16228 onContextMenu : function(e){
16229 var item = this.findItemFromChild(e.getTarget());
16231 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16236 onDblClick : function(e){
16237 var item = this.findItemFromChild(e.getTarget());
16239 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16243 onItemClick : function(item, index, e)
16245 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16248 if (this.toggleSelect) {
16249 var m = this.isSelected(item) ? 'unselect' : 'select';
16252 _t[m](item, true, false);
16255 if(this.multiSelect || this.singleSelect){
16256 if(this.multiSelect && e.shiftKey && this.lastSelection){
16257 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16259 this.select(item, this.multiSelect && e.ctrlKey);
16260 this.lastSelection = item;
16263 if(!this.tickable){
16264 e.preventDefault();
16272 * Get the number of selected nodes.
16275 getSelectionCount : function(){
16276 return this.selections.length;
16280 * Get the currently selected nodes.
16281 * @return {Array} An array of HTMLElements
16283 getSelectedNodes : function(){
16284 return this.selections;
16288 * Get the indexes of the selected nodes.
16291 getSelectedIndexes : function(){
16292 var indexes = [], s = this.selections;
16293 for(var i = 0, len = s.length; i < len; i++){
16294 indexes.push(s[i].nodeIndex);
16300 * Clear all selections
16301 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16303 clearSelections : function(suppressEvent){
16304 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16305 this.cmp.elements = this.selections;
16306 this.cmp.removeClass(this.selectedClass);
16307 this.selections = [];
16308 if(!suppressEvent){
16309 this.fireEvent("selectionchange", this, this.selections);
16315 * Returns true if the passed node is selected
16316 * @param {HTMLElement/Number} node The node or node index
16317 * @return {Boolean}
16319 isSelected : function(node){
16320 var s = this.selections;
16324 node = this.getNode(node);
16325 return s.indexOf(node) !== -1;
16330 * @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
16331 * @param {Boolean} keepExisting (optional) true to keep existing selections
16332 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16334 select : function(nodeInfo, keepExisting, suppressEvent){
16335 if(nodeInfo instanceof Array){
16337 this.clearSelections(true);
16339 for(var i = 0, len = nodeInfo.length; i < len; i++){
16340 this.select(nodeInfo[i], true, true);
16344 var node = this.getNode(nodeInfo);
16345 if(!node || this.isSelected(node)){
16346 return; // already selected.
16349 this.clearSelections(true);
16352 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16353 Roo.fly(node).addClass(this.selectedClass);
16354 this.selections.push(node);
16355 if(!suppressEvent){
16356 this.fireEvent("selectionchange", this, this.selections);
16364 * @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
16365 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16366 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16368 unselect : function(nodeInfo, keepExisting, suppressEvent)
16370 if(nodeInfo instanceof Array){
16371 Roo.each(this.selections, function(s) {
16372 this.unselect(s, nodeInfo);
16376 var node = this.getNode(nodeInfo);
16377 if(!node || !this.isSelected(node)){
16378 //Roo.log("not selected");
16379 return; // not selected.
16383 Roo.each(this.selections, function(s) {
16385 Roo.fly(node).removeClass(this.selectedClass);
16392 this.selections= ns;
16393 this.fireEvent("selectionchange", this, this.selections);
16397 * Gets a template node.
16398 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16399 * @return {HTMLElement} The node or null if it wasn't found
16401 getNode : function(nodeInfo){
16402 if(typeof nodeInfo == "string"){
16403 return document.getElementById(nodeInfo);
16404 }else if(typeof nodeInfo == "number"){
16405 return this.nodes[nodeInfo];
16411 * Gets a range template nodes.
16412 * @param {Number} startIndex
16413 * @param {Number} endIndex
16414 * @return {Array} An array of nodes
16416 getNodes : function(start, end){
16417 var ns = this.nodes;
16418 start = start || 0;
16419 end = typeof end == "undefined" ? ns.length - 1 : end;
16422 for(var i = start; i <= end; i++){
16426 for(var i = start; i >= end; i--){
16434 * Finds the index of the passed node
16435 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16436 * @return {Number} The index of the node or -1
16438 indexOf : function(node){
16439 node = this.getNode(node);
16440 if(typeof node.nodeIndex == "number"){
16441 return node.nodeIndex;
16443 var ns = this.nodes;
16444 for(var i = 0, len = ns.length; i < len; i++){
16455 * based on jquery fullcalendar
16459 Roo.bootstrap = Roo.bootstrap || {};
16461 * @class Roo.bootstrap.Calendar
16462 * @extends Roo.bootstrap.Component
16463 * Bootstrap Calendar class
16464 * @cfg {Boolean} loadMask (true|false) default false
16465 * @cfg {Object} header generate the user specific header of the calendar, default false
16468 * Create a new Container
16469 * @param {Object} config The config object
16474 Roo.bootstrap.Calendar = function(config){
16475 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16479 * Fires when a date is selected
16480 * @param {DatePicker} this
16481 * @param {Date} date The selected date
16485 * @event monthchange
16486 * Fires when the displayed month changes
16487 * @param {DatePicker} this
16488 * @param {Date} date The selected month
16490 'monthchange': true,
16492 * @event evententer
16493 * Fires when mouse over an event
16494 * @param {Calendar} this
16495 * @param {event} Event
16497 'evententer': true,
16499 * @event eventleave
16500 * Fires when the mouse leaves an
16501 * @param {Calendar} this
16504 'eventleave': true,
16506 * @event eventclick
16507 * Fires when the mouse click an
16508 * @param {Calendar} this
16517 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16520 * @cfg {Number} startDay
16521 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16529 getAutoCreate : function(){
16532 var fc_button = function(name, corner, style, content ) {
16533 return Roo.apply({},{
16535 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16537 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16540 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16551 style : 'width:100%',
16558 cls : 'fc-header-left',
16560 fc_button('prev', 'left', 'arrow', '‹' ),
16561 fc_button('next', 'right', 'arrow', '›' ),
16562 { tag: 'span', cls: 'fc-header-space' },
16563 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16571 cls : 'fc-header-center',
16575 cls: 'fc-header-title',
16578 html : 'month / year'
16586 cls : 'fc-header-right',
16588 /* fc_button('month', 'left', '', 'month' ),
16589 fc_button('week', '', '', 'week' ),
16590 fc_button('day', 'right', '', 'day' )
16602 header = this.header;
16605 var cal_heads = function() {
16607 // fixme - handle this.
16609 for (var i =0; i < Date.dayNames.length; i++) {
16610 var d = Date.dayNames[i];
16613 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16614 html : d.substring(0,3)
16618 ret[0].cls += ' fc-first';
16619 ret[6].cls += ' fc-last';
16622 var cal_cell = function(n) {
16625 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16630 cls: 'fc-day-number',
16634 cls: 'fc-day-content',
16638 style: 'position: relative;' // height: 17px;
16650 var cal_rows = function() {
16653 for (var r = 0; r < 6; r++) {
16660 for (var i =0; i < Date.dayNames.length; i++) {
16661 var d = Date.dayNames[i];
16662 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16665 row.cn[0].cls+=' fc-first';
16666 row.cn[0].cn[0].style = 'min-height:90px';
16667 row.cn[6].cls+=' fc-last';
16671 ret[0].cls += ' fc-first';
16672 ret[4].cls += ' fc-prev-last';
16673 ret[5].cls += ' fc-last';
16680 cls: 'fc-border-separate',
16681 style : 'width:100%',
16689 cls : 'fc-first fc-last',
16707 cls : 'fc-content',
16708 style : "position: relative;",
16711 cls : 'fc-view fc-view-month fc-grid',
16712 style : 'position: relative',
16713 unselectable : 'on',
16716 cls : 'fc-event-container',
16717 style : 'position:absolute;z-index:8;top:0;left:0;'
16735 initEvents : function()
16738 throw "can not find store for calendar";
16744 style: "text-align:center",
16748 style: "background-color:white;width:50%;margin:250 auto",
16752 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16763 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16765 var size = this.el.select('.fc-content', true).first().getSize();
16766 this.maskEl.setSize(size.width, size.height);
16767 this.maskEl.enableDisplayMode("block");
16768 if(!this.loadMask){
16769 this.maskEl.hide();
16772 this.store = Roo.factory(this.store, Roo.data);
16773 this.store.on('load', this.onLoad, this);
16774 this.store.on('beforeload', this.onBeforeLoad, this);
16778 this.cells = this.el.select('.fc-day',true);
16779 //Roo.log(this.cells);
16780 this.textNodes = this.el.query('.fc-day-number');
16781 this.cells.addClassOnOver('fc-state-hover');
16783 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16784 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16785 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16786 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16788 this.on('monthchange', this.onMonthChange, this);
16790 this.update(new Date().clearTime());
16793 resize : function() {
16794 var sz = this.el.getSize();
16796 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16797 this.el.select('.fc-day-content div',true).setHeight(34);
16802 showPrevMonth : function(e){
16803 this.update(this.activeDate.add("mo", -1));
16805 showToday : function(e){
16806 this.update(new Date().clearTime());
16809 showNextMonth : function(e){
16810 this.update(this.activeDate.add("mo", 1));
16814 showPrevYear : function(){
16815 this.update(this.activeDate.add("y", -1));
16819 showNextYear : function(){
16820 this.update(this.activeDate.add("y", 1));
16825 update : function(date)
16827 var vd = this.activeDate;
16828 this.activeDate = date;
16829 // if(vd && this.el){
16830 // var t = date.getTime();
16831 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16832 // Roo.log('using add remove');
16834 // this.fireEvent('monthchange', this, date);
16836 // this.cells.removeClass("fc-state-highlight");
16837 // this.cells.each(function(c){
16838 // if(c.dateValue == t){
16839 // c.addClass("fc-state-highlight");
16840 // setTimeout(function(){
16841 // try{c.dom.firstChild.focus();}catch(e){}
16851 var days = date.getDaysInMonth();
16853 var firstOfMonth = date.getFirstDateOfMonth();
16854 var startingPos = firstOfMonth.getDay()-this.startDay;
16856 if(startingPos < this.startDay){
16860 var pm = date.add(Date.MONTH, -1);
16861 var prevStart = pm.getDaysInMonth()-startingPos;
16863 this.cells = this.el.select('.fc-day',true);
16864 this.textNodes = this.el.query('.fc-day-number');
16865 this.cells.addClassOnOver('fc-state-hover');
16867 var cells = this.cells.elements;
16868 var textEls = this.textNodes;
16870 Roo.each(cells, function(cell){
16871 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16874 days += startingPos;
16876 // convert everything to numbers so it's fast
16877 var day = 86400000;
16878 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16881 //Roo.log(prevStart);
16883 var today = new Date().clearTime().getTime();
16884 var sel = date.clearTime().getTime();
16885 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16886 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16887 var ddMatch = this.disabledDatesRE;
16888 var ddText = this.disabledDatesText;
16889 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16890 var ddaysText = this.disabledDaysText;
16891 var format = this.format;
16893 var setCellClass = function(cal, cell){
16897 //Roo.log('set Cell Class');
16899 var t = d.getTime();
16903 cell.dateValue = t;
16905 cell.className += " fc-today";
16906 cell.className += " fc-state-highlight";
16907 cell.title = cal.todayText;
16910 // disable highlight in other month..
16911 //cell.className += " fc-state-highlight";
16916 cell.className = " fc-state-disabled";
16917 cell.title = cal.minText;
16921 cell.className = " fc-state-disabled";
16922 cell.title = cal.maxText;
16926 if(ddays.indexOf(d.getDay()) != -1){
16927 cell.title = ddaysText;
16928 cell.className = " fc-state-disabled";
16931 if(ddMatch && format){
16932 var fvalue = d.dateFormat(format);
16933 if(ddMatch.test(fvalue)){
16934 cell.title = ddText.replace("%0", fvalue);
16935 cell.className = " fc-state-disabled";
16939 if (!cell.initialClassName) {
16940 cell.initialClassName = cell.dom.className;
16943 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16948 for(; i < startingPos; i++) {
16949 textEls[i].innerHTML = (++prevStart);
16950 d.setDate(d.getDate()+1);
16952 cells[i].className = "fc-past fc-other-month";
16953 setCellClass(this, cells[i]);
16958 for(; i < days; i++){
16959 intDay = i - startingPos + 1;
16960 textEls[i].innerHTML = (intDay);
16961 d.setDate(d.getDate()+1);
16963 cells[i].className = ''; // "x-date-active";
16964 setCellClass(this, cells[i]);
16968 for(; i < 42; i++) {
16969 textEls[i].innerHTML = (++extraDays);
16970 d.setDate(d.getDate()+1);
16972 cells[i].className = "fc-future fc-other-month";
16973 setCellClass(this, cells[i]);
16976 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16978 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16980 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16981 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16983 if(totalRows != 6){
16984 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16985 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16988 this.fireEvent('monthchange', this, date);
16992 if(!this.internalRender){
16993 var main = this.el.dom.firstChild;
16994 var w = main.offsetWidth;
16995 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16996 Roo.fly(main).setWidth(w);
16997 this.internalRender = true;
16998 // opera does not respect the auto grow header center column
16999 // then, after it gets a width opera refuses to recalculate
17000 // without a second pass
17001 if(Roo.isOpera && !this.secondPass){
17002 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17003 this.secondPass = true;
17004 this.update.defer(10, this, [date]);
17011 findCell : function(dt) {
17012 dt = dt.clearTime().getTime();
17014 this.cells.each(function(c){
17015 //Roo.log("check " +c.dateValue + '?=' + dt);
17016 if(c.dateValue == dt){
17026 findCells : function(ev) {
17027 var s = ev.start.clone().clearTime().getTime();
17029 var e= ev.end.clone().clearTime().getTime();
17032 this.cells.each(function(c){
17033 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17035 if(c.dateValue > e){
17038 if(c.dateValue < s){
17047 // findBestRow: function(cells)
17051 // for (var i =0 ; i < cells.length;i++) {
17052 // ret = Math.max(cells[i].rows || 0,ret);
17059 addItem : function(ev)
17061 // look for vertical location slot in
17062 var cells = this.findCells(ev);
17064 // ev.row = this.findBestRow(cells);
17066 // work out the location.
17070 for(var i =0; i < cells.length; i++) {
17072 cells[i].row = cells[0].row;
17075 cells[i].row = cells[i].row + 1;
17085 if (crow.start.getY() == cells[i].getY()) {
17087 crow.end = cells[i];
17104 cells[0].events.push(ev);
17106 this.calevents.push(ev);
17109 clearEvents: function() {
17111 if(!this.calevents){
17115 Roo.each(this.cells.elements, function(c){
17121 Roo.each(this.calevents, function(e) {
17122 Roo.each(e.els, function(el) {
17123 el.un('mouseenter' ,this.onEventEnter, this);
17124 el.un('mouseleave' ,this.onEventLeave, this);
17129 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17135 renderEvents: function()
17139 this.cells.each(function(c) {
17148 if(c.row != c.events.length){
17149 r = 4 - (4 - (c.row - c.events.length));
17152 c.events = ev.slice(0, r);
17153 c.more = ev.slice(r);
17155 if(c.more.length && c.more.length == 1){
17156 c.events.push(c.more.pop());
17159 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17163 this.cells.each(function(c) {
17165 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17168 for (var e = 0; e < c.events.length; e++){
17169 var ev = c.events[e];
17170 var rows = ev.rows;
17172 for(var i = 0; i < rows.length; i++) {
17174 // how many rows should it span..
17177 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17178 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17180 unselectable : "on",
17183 cls: 'fc-event-inner',
17187 // cls: 'fc-event-time',
17188 // html : cells.length > 1 ? '' : ev.time
17192 cls: 'fc-event-title',
17193 html : String.format('{0}', ev.title)
17200 cls: 'ui-resizable-handle ui-resizable-e',
17201 html : '  '
17208 cfg.cls += ' fc-event-start';
17210 if ((i+1) == rows.length) {
17211 cfg.cls += ' fc-event-end';
17214 var ctr = _this.el.select('.fc-event-container',true).first();
17215 var cg = ctr.createChild(cfg);
17217 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17218 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17220 var r = (c.more.length) ? 1 : 0;
17221 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17222 cg.setWidth(ebox.right - sbox.x -2);
17224 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17225 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17226 cg.on('click', _this.onEventClick, _this, ev);
17237 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17238 style : 'position: absolute',
17239 unselectable : "on",
17242 cls: 'fc-event-inner',
17246 cls: 'fc-event-title',
17254 cls: 'ui-resizable-handle ui-resizable-e',
17255 html : '  '
17261 var ctr = _this.el.select('.fc-event-container',true).first();
17262 var cg = ctr.createChild(cfg);
17264 var sbox = c.select('.fc-day-content',true).first().getBox();
17265 var ebox = c.select('.fc-day-content',true).first().getBox();
17267 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17268 cg.setWidth(ebox.right - sbox.x -2);
17270 cg.on('click', _this.onMoreEventClick, _this, c.more);
17280 onEventEnter: function (e, el,event,d) {
17281 this.fireEvent('evententer', this, el, event);
17284 onEventLeave: function (e, el,event,d) {
17285 this.fireEvent('eventleave', this, el, event);
17288 onEventClick: function (e, el,event,d) {
17289 this.fireEvent('eventclick', this, el, event);
17292 onMonthChange: function () {
17296 onMoreEventClick: function(e, el, more)
17300 this.calpopover.placement = 'right';
17301 this.calpopover.setTitle('More');
17303 this.calpopover.setContent('');
17305 var ctr = this.calpopover.el.select('.popover-content', true).first();
17307 Roo.each(more, function(m){
17309 cls : 'fc-event-hori fc-event-draggable',
17312 var cg = ctr.createChild(cfg);
17314 cg.on('click', _this.onEventClick, _this, m);
17317 this.calpopover.show(el);
17322 onLoad: function ()
17324 this.calevents = [];
17327 if(this.store.getCount() > 0){
17328 this.store.data.each(function(d){
17331 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17332 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17333 time : d.data.start_time,
17334 title : d.data.title,
17335 description : d.data.description,
17336 venue : d.data.venue
17341 this.renderEvents();
17343 if(this.calevents.length && this.loadMask){
17344 this.maskEl.hide();
17348 onBeforeLoad: function()
17350 this.clearEvents();
17352 this.maskEl.show();
17366 * @class Roo.bootstrap.Popover
17367 * @extends Roo.bootstrap.Component
17368 * Bootstrap Popover class
17369 * @cfg {String} html contents of the popover (or false to use children..)
17370 * @cfg {String} title of popover (or false to hide)
17371 * @cfg {String} placement how it is placed
17372 * @cfg {String} trigger click || hover (or false to trigger manually)
17373 * @cfg {String} over what (parent or false to trigger manually.)
17374 * @cfg {Number} delay - delay before showing
17377 * Create a new Popover
17378 * @param {Object} config The config object
17381 Roo.bootstrap.Popover = function(config){
17382 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17388 * After the popover show
17390 * @param {Roo.bootstrap.Popover} this
17395 * After the popover hide
17397 * @param {Roo.bootstrap.Popover} this
17403 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17405 title: 'Fill in a title',
17408 placement : 'right',
17409 trigger : 'hover', // hover
17415 can_build_overlaid : false,
17417 getChildContainer : function()
17419 return this.el.select('.popover-content',true).first();
17422 getAutoCreate : function(){
17425 cls : 'popover roo-dynamic',
17426 style: 'display:block',
17432 cls : 'popover-inner',
17436 cls: 'popover-title',
17440 cls : 'popover-content',
17451 setTitle: function(str)
17454 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17456 setContent: function(str)
17459 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17461 // as it get's added to the bottom of the page.
17462 onRender : function(ct, position)
17464 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17466 var cfg = Roo.apply({}, this.getAutoCreate());
17470 cfg.cls += ' ' + this.cls;
17473 cfg.style = this.style;
17475 //Roo.log("adding to ");
17476 this.el = Roo.get(document.body).createChild(cfg, position);
17477 // Roo.log(this.el);
17482 initEvents : function()
17484 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17485 this.el.enableDisplayMode('block');
17487 if (this.over === false) {
17490 if (this.triggers === false) {
17493 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17494 var triggers = this.trigger ? this.trigger.split(' ') : [];
17495 Roo.each(triggers, function(trigger) {
17497 if (trigger == 'click') {
17498 on_el.on('click', this.toggle, this);
17499 } else if (trigger != 'manual') {
17500 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17501 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17503 on_el.on(eventIn ,this.enter, this);
17504 on_el.on(eventOut, this.leave, this);
17515 toggle : function () {
17516 this.hoverState == 'in' ? this.leave() : this.enter();
17519 enter : function () {
17521 clearTimeout(this.timeout);
17523 this.hoverState = 'in';
17525 if (!this.delay || !this.delay.show) {
17530 this.timeout = setTimeout(function () {
17531 if (_t.hoverState == 'in') {
17534 }, this.delay.show)
17537 leave : function() {
17538 clearTimeout(this.timeout);
17540 this.hoverState = 'out';
17542 if (!this.delay || !this.delay.hide) {
17547 this.timeout = setTimeout(function () {
17548 if (_t.hoverState == 'out') {
17551 }, this.delay.hide)
17554 show : function (on_el)
17557 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17561 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17562 if (this.html !== false) {
17563 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17565 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17566 if (!this.title.length) {
17567 this.el.select('.popover-title',true).hide();
17570 var placement = typeof this.placement == 'function' ?
17571 this.placement.call(this, this.el, on_el) :
17574 var autoToken = /\s?auto?\s?/i;
17575 var autoPlace = autoToken.test(placement);
17577 placement = placement.replace(autoToken, '') || 'top';
17581 //this.el.setXY([0,0]);
17583 this.el.dom.style.display='block';
17584 this.el.addClass(placement);
17586 //this.el.appendTo(on_el);
17588 var p = this.getPosition();
17589 var box = this.el.getBox();
17594 var align = Roo.bootstrap.Popover.alignment[placement];
17597 this.el.alignTo(on_el, align[0],align[1]);
17598 //var arrow = this.el.select('.arrow',true).first();
17599 //arrow.set(align[2],
17601 this.el.addClass('in');
17604 if (this.el.hasClass('fade')) {
17608 this.hoverState = 'in';
17610 this.fireEvent('show', this);
17615 this.el.setXY([0,0]);
17616 this.el.removeClass('in');
17618 this.hoverState = null;
17620 this.fireEvent('hide', this);
17625 Roo.bootstrap.Popover.alignment = {
17626 'left' : ['r-l', [-10,0], 'right'],
17627 'right' : ['l-r', [10,0], 'left'],
17628 'bottom' : ['t-b', [0,10], 'top'],
17629 'top' : [ 'b-t', [0,-10], 'bottom']
17640 * @class Roo.bootstrap.Progress
17641 * @extends Roo.bootstrap.Component
17642 * Bootstrap Progress class
17643 * @cfg {Boolean} striped striped of the progress bar
17644 * @cfg {Boolean} active animated of the progress bar
17648 * Create a new Progress
17649 * @param {Object} config The config object
17652 Roo.bootstrap.Progress = function(config){
17653 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17656 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17661 getAutoCreate : function(){
17669 cfg.cls += ' progress-striped';
17673 cfg.cls += ' active';
17692 * @class Roo.bootstrap.ProgressBar
17693 * @extends Roo.bootstrap.Component
17694 * Bootstrap ProgressBar class
17695 * @cfg {Number} aria_valuenow aria-value now
17696 * @cfg {Number} aria_valuemin aria-value min
17697 * @cfg {Number} aria_valuemax aria-value max
17698 * @cfg {String} label label for the progress bar
17699 * @cfg {String} panel (success | info | warning | danger )
17700 * @cfg {String} role role of the progress bar
17701 * @cfg {String} sr_only text
17705 * Create a new ProgressBar
17706 * @param {Object} config The config object
17709 Roo.bootstrap.ProgressBar = function(config){
17710 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17713 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17717 aria_valuemax : 100,
17723 getAutoCreate : function()
17728 cls: 'progress-bar',
17729 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17741 cfg.role = this.role;
17744 if(this.aria_valuenow){
17745 cfg['aria-valuenow'] = this.aria_valuenow;
17748 if(this.aria_valuemin){
17749 cfg['aria-valuemin'] = this.aria_valuemin;
17752 if(this.aria_valuemax){
17753 cfg['aria-valuemax'] = this.aria_valuemax;
17756 if(this.label && !this.sr_only){
17757 cfg.html = this.label;
17761 cfg.cls += ' progress-bar-' + this.panel;
17767 update : function(aria_valuenow)
17769 this.aria_valuenow = aria_valuenow;
17771 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17786 * @class Roo.bootstrap.TabGroup
17787 * @extends Roo.bootstrap.Column
17788 * Bootstrap Column class
17789 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17790 * @cfg {Boolean} carousel true to make the group behave like a carousel
17791 * @cfg {Boolean} bullets show bullets for the panels
17792 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17793 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17794 * @cfg {Boolean} showarrow (true|false) show arrow default true
17797 * Create a new TabGroup
17798 * @param {Object} config The config object
17801 Roo.bootstrap.TabGroup = function(config){
17802 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17804 this.navId = Roo.id();
17807 Roo.bootstrap.TabGroup.register(this);
17811 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17814 transition : false,
17819 slideOnTouch : false,
17822 getAutoCreate : function()
17824 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17826 cfg.cls += ' tab-content';
17828 if (this.carousel) {
17829 cfg.cls += ' carousel slide';
17832 cls : 'carousel-inner',
17836 if(this.bullets && !Roo.isTouch){
17839 cls : 'carousel-bullets',
17843 if(this.bullets_cls){
17844 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17851 cfg.cn[0].cn.push(bullets);
17854 if(this.showarrow){
17855 cfg.cn[0].cn.push({
17857 class : 'carousel-arrow',
17861 class : 'carousel-prev',
17865 class : 'fa fa-chevron-left'
17871 class : 'carousel-next',
17875 class : 'fa fa-chevron-right'
17888 initEvents: function()
17890 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17891 // this.el.on("touchstart", this.onTouchStart, this);
17894 if(this.autoslide){
17897 this.slideFn = window.setInterval(function() {
17898 _this.showPanelNext();
17902 if(this.showarrow){
17903 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17904 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17910 // onTouchStart : function(e, el, o)
17912 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17916 // this.showPanelNext();
17920 getChildContainer : function()
17922 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17926 * register a Navigation item
17927 * @param {Roo.bootstrap.NavItem} the navitem to add
17929 register : function(item)
17931 this.tabs.push( item);
17932 item.navId = this.navId; // not really needed..
17937 getActivePanel : function()
17940 Roo.each(this.tabs, function(t) {
17950 getPanelByName : function(n)
17953 Roo.each(this.tabs, function(t) {
17954 if (t.tabId == n) {
17962 indexOfPanel : function(p)
17965 Roo.each(this.tabs, function(t,i) {
17966 if (t.tabId == p.tabId) {
17975 * show a specific panel
17976 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17977 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17979 showPanel : function (pan)
17981 if(this.transition || typeof(pan) == 'undefined'){
17982 Roo.log("waiting for the transitionend");
17986 if (typeof(pan) == 'number') {
17987 pan = this.tabs[pan];
17990 if (typeof(pan) == 'string') {
17991 pan = this.getPanelByName(pan);
17994 var cur = this.getActivePanel();
17997 Roo.log('pan or acitve pan is undefined');
18001 if (pan.tabId == this.getActivePanel().tabId) {
18005 if (false === cur.fireEvent('beforedeactivate')) {
18009 if(this.bullets > 0 && !Roo.isTouch){
18010 this.setActiveBullet(this.indexOfPanel(pan));
18013 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18015 this.transition = true;
18016 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18017 var lr = dir == 'next' ? 'left' : 'right';
18018 pan.el.addClass(dir); // or prev
18019 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18020 cur.el.addClass(lr); // or right
18021 pan.el.addClass(lr);
18024 cur.el.on('transitionend', function() {
18025 Roo.log("trans end?");
18027 pan.el.removeClass([lr,dir]);
18028 pan.setActive(true);
18030 cur.el.removeClass([lr]);
18031 cur.setActive(false);
18033 _this.transition = false;
18035 }, this, { single: true } );
18040 cur.setActive(false);
18041 pan.setActive(true);
18046 showPanelNext : function()
18048 var i = this.indexOfPanel(this.getActivePanel());
18050 if (i >= this.tabs.length - 1 && !this.autoslide) {
18054 if (i >= this.tabs.length - 1 && this.autoslide) {
18058 this.showPanel(this.tabs[i+1]);
18061 showPanelPrev : function()
18063 var i = this.indexOfPanel(this.getActivePanel());
18065 if (i < 1 && !this.autoslide) {
18069 if (i < 1 && this.autoslide) {
18070 i = this.tabs.length;
18073 this.showPanel(this.tabs[i-1]);
18077 addBullet: function()
18079 if(!this.bullets || Roo.isTouch){
18082 var ctr = this.el.select('.carousel-bullets',true).first();
18083 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18084 var bullet = ctr.createChild({
18085 cls : 'bullet bullet-' + i
18086 },ctr.dom.lastChild);
18091 bullet.on('click', (function(e, el, o, ii, t){
18093 e.preventDefault();
18095 this.showPanel(ii);
18097 if(this.autoslide && this.slideFn){
18098 clearInterval(this.slideFn);
18099 this.slideFn = window.setInterval(function() {
18100 _this.showPanelNext();
18104 }).createDelegate(this, [i, bullet], true));
18109 setActiveBullet : function(i)
18115 Roo.each(this.el.select('.bullet', true).elements, function(el){
18116 el.removeClass('selected');
18119 var bullet = this.el.select('.bullet-' + i, true).first();
18125 bullet.addClass('selected');
18136 Roo.apply(Roo.bootstrap.TabGroup, {
18140 * register a Navigation Group
18141 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18143 register : function(navgrp)
18145 this.groups[navgrp.navId] = navgrp;
18149 * fetch a Navigation Group based on the navigation ID
18150 * if one does not exist , it will get created.
18151 * @param {string} the navgroup to add
18152 * @returns {Roo.bootstrap.NavGroup} the navgroup
18154 get: function(navId) {
18155 if (typeof(this.groups[navId]) == 'undefined') {
18156 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18158 return this.groups[navId] ;
18173 * @class Roo.bootstrap.TabPanel
18174 * @extends Roo.bootstrap.Component
18175 * Bootstrap TabPanel class
18176 * @cfg {Boolean} active panel active
18177 * @cfg {String} html panel content
18178 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18179 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18180 * @cfg {String} href click to link..
18184 * Create a new TabPanel
18185 * @param {Object} config The config object
18188 Roo.bootstrap.TabPanel = function(config){
18189 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18193 * Fires when the active status changes
18194 * @param {Roo.bootstrap.TabPanel} this
18195 * @param {Boolean} state the new state
18200 * @event beforedeactivate
18201 * Fires before a tab is de-activated - can be used to do validation on a form.
18202 * @param {Roo.bootstrap.TabPanel} this
18203 * @return {Boolean} false if there is an error
18206 'beforedeactivate': true
18209 this.tabId = this.tabId || Roo.id();
18213 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18221 getAutoCreate : function(){
18224 // item is needed for carousel - not sure if it has any effect otherwise
18225 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18226 html: this.html || ''
18230 cfg.cls += ' active';
18234 cfg.tabId = this.tabId;
18241 initEvents: function()
18243 var p = this.parent();
18245 this.navId = this.navId || p.navId;
18247 if (typeof(this.navId) != 'undefined') {
18248 // not really needed.. but just in case.. parent should be a NavGroup.
18249 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18253 var i = tg.tabs.length - 1;
18255 if(this.active && tg.bullets > 0 && i < tg.bullets){
18256 tg.setActiveBullet(i);
18260 this.el.on('click', this.onClick, this);
18263 this.el.on("touchstart", this.onTouchStart, this);
18264 this.el.on("touchmove", this.onTouchMove, this);
18265 this.el.on("touchend", this.onTouchEnd, this);
18270 onRender : function(ct, position)
18272 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18275 setActive : function(state)
18277 Roo.log("panel - set active " + this.tabId + "=" + state);
18279 this.active = state;
18281 this.el.removeClass('active');
18283 } else if (!this.el.hasClass('active')) {
18284 this.el.addClass('active');
18287 this.fireEvent('changed', this, state);
18290 onClick : function(e)
18292 e.preventDefault();
18294 if(!this.href.length){
18298 window.location.href = this.href;
18307 onTouchStart : function(e)
18309 this.swiping = false;
18311 this.startX = e.browserEvent.touches[0].clientX;
18312 this.startY = e.browserEvent.touches[0].clientY;
18315 onTouchMove : function(e)
18317 this.swiping = true;
18319 this.endX = e.browserEvent.touches[0].clientX;
18320 this.endY = e.browserEvent.touches[0].clientY;
18323 onTouchEnd : function(e)
18330 var tabGroup = this.parent();
18332 if(this.endX > this.startX){ // swiping right
18333 tabGroup.showPanelPrev();
18337 if(this.startX > this.endX){ // swiping left
18338 tabGroup.showPanelNext();
18357 * @class Roo.bootstrap.DateField
18358 * @extends Roo.bootstrap.Input
18359 * Bootstrap DateField class
18360 * @cfg {Number} weekStart default 0
18361 * @cfg {String} viewMode default empty, (months|years)
18362 * @cfg {String} minViewMode default empty, (months|years)
18363 * @cfg {Number} startDate default -Infinity
18364 * @cfg {Number} endDate default Infinity
18365 * @cfg {Boolean} todayHighlight default false
18366 * @cfg {Boolean} todayBtn default false
18367 * @cfg {Boolean} calendarWeeks default false
18368 * @cfg {Object} daysOfWeekDisabled default empty
18369 * @cfg {Boolean} singleMode default false (true | false)
18371 * @cfg {Boolean} keyboardNavigation default true
18372 * @cfg {String} language default en
18375 * Create a new DateField
18376 * @param {Object} config The config object
18379 Roo.bootstrap.DateField = function(config){
18380 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18384 * Fires when this field show.
18385 * @param {Roo.bootstrap.DateField} this
18386 * @param {Mixed} date The date value
18391 * Fires when this field hide.
18392 * @param {Roo.bootstrap.DateField} this
18393 * @param {Mixed} date The date value
18398 * Fires when select a date.
18399 * @param {Roo.bootstrap.DateField} this
18400 * @param {Mixed} date The date value
18404 * @event beforeselect
18405 * Fires when before select a date.
18406 * @param {Roo.bootstrap.DateField} this
18407 * @param {Mixed} date The date value
18409 beforeselect : true
18413 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18416 * @cfg {String} format
18417 * The default date format string which can be overriden for localization support. The format must be
18418 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18422 * @cfg {String} altFormats
18423 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18424 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18426 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18434 todayHighlight : false,
18440 keyboardNavigation: true,
18442 calendarWeeks: false,
18444 startDate: -Infinity,
18448 daysOfWeekDisabled: [],
18452 singleMode : false,
18454 UTCDate: function()
18456 return new Date(Date.UTC.apply(Date, arguments));
18459 UTCToday: function()
18461 var today = new Date();
18462 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18465 getDate: function() {
18466 var d = this.getUTCDate();
18467 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18470 getUTCDate: function() {
18474 setDate: function(d) {
18475 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18478 setUTCDate: function(d) {
18480 this.setValue(this.formatDate(this.date));
18483 onRender: function(ct, position)
18486 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18488 this.language = this.language || 'en';
18489 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18490 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18492 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18493 this.format = this.format || 'm/d/y';
18494 this.isInline = false;
18495 this.isInput = true;
18496 this.component = this.el.select('.add-on', true).first() || false;
18497 this.component = (this.component && this.component.length === 0) ? false : this.component;
18498 this.hasInput = this.component && this.inputEl().length;
18500 if (typeof(this.minViewMode === 'string')) {
18501 switch (this.minViewMode) {
18503 this.minViewMode = 1;
18506 this.minViewMode = 2;
18509 this.minViewMode = 0;
18514 if (typeof(this.viewMode === 'string')) {
18515 switch (this.viewMode) {
18528 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18530 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18532 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18534 this.picker().on('mousedown', this.onMousedown, this);
18535 this.picker().on('click', this.onClick, this);
18537 this.picker().addClass('datepicker-dropdown');
18539 this.startViewMode = this.viewMode;
18541 if(this.singleMode){
18542 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18543 v.setVisibilityMode(Roo.Element.DISPLAY);
18547 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18548 v.setStyle('width', '189px');
18552 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18553 if(!this.calendarWeeks){
18558 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18559 v.attr('colspan', function(i, val){
18560 return parseInt(val) + 1;
18565 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18567 this.setStartDate(this.startDate);
18568 this.setEndDate(this.endDate);
18570 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18577 if(this.isInline) {
18582 picker : function()
18584 return this.pickerEl;
18585 // return this.el.select('.datepicker', true).first();
18588 fillDow: function()
18590 var dowCnt = this.weekStart;
18599 if(this.calendarWeeks){
18607 while (dowCnt < this.weekStart + 7) {
18611 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18615 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18618 fillMonths: function()
18621 var months = this.picker().select('>.datepicker-months td', true).first();
18623 months.dom.innerHTML = '';
18629 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18632 months.createChild(month);
18639 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;
18641 if (this.date < this.startDate) {
18642 this.viewDate = new Date(this.startDate);
18643 } else if (this.date > this.endDate) {
18644 this.viewDate = new Date(this.endDate);
18646 this.viewDate = new Date(this.date);
18654 var d = new Date(this.viewDate),
18655 year = d.getUTCFullYear(),
18656 month = d.getUTCMonth(),
18657 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18658 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18659 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18660 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18661 currentDate = this.date && this.date.valueOf(),
18662 today = this.UTCToday();
18664 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18666 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18668 // this.picker.select('>tfoot th.today').
18669 // .text(dates[this.language].today)
18670 // .toggle(this.todayBtn !== false);
18672 this.updateNavArrows();
18675 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18677 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18679 prevMonth.setUTCDate(day);
18681 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18683 var nextMonth = new Date(prevMonth);
18685 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18687 nextMonth = nextMonth.valueOf();
18689 var fillMonths = false;
18691 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18693 while(prevMonth.valueOf() <= nextMonth) {
18696 if (prevMonth.getUTCDay() === this.weekStart) {
18698 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18706 if(this.calendarWeeks){
18707 // ISO 8601: First week contains first thursday.
18708 // ISO also states week starts on Monday, but we can be more abstract here.
18710 // Start of current week: based on weekstart/current date
18711 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18712 // Thursday of this week
18713 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18714 // First Thursday of year, year from thursday
18715 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18716 // Calendar week: ms between thursdays, div ms per day, div 7 days
18717 calWeek = (th - yth) / 864e5 / 7 + 1;
18719 fillMonths.cn.push({
18727 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18729 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18732 if (this.todayHighlight &&
18733 prevMonth.getUTCFullYear() == today.getFullYear() &&
18734 prevMonth.getUTCMonth() == today.getMonth() &&
18735 prevMonth.getUTCDate() == today.getDate()) {
18736 clsName += ' today';
18739 if (currentDate && prevMonth.valueOf() === currentDate) {
18740 clsName += ' active';
18743 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18744 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18745 clsName += ' disabled';
18748 fillMonths.cn.push({
18750 cls: 'day ' + clsName,
18751 html: prevMonth.getDate()
18754 prevMonth.setDate(prevMonth.getDate()+1);
18757 var currentYear = this.date && this.date.getUTCFullYear();
18758 var currentMonth = this.date && this.date.getUTCMonth();
18760 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18762 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18763 v.removeClass('active');
18765 if(currentYear === year && k === currentMonth){
18766 v.addClass('active');
18769 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18770 v.addClass('disabled');
18776 year = parseInt(year/10, 10) * 10;
18778 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18780 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18783 for (var i = -1; i < 11; i++) {
18784 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18786 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18794 showMode: function(dir)
18797 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18800 Roo.each(this.picker().select('>div',true).elements, function(v){
18801 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18804 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18809 if(this.isInline) {
18813 this.picker().removeClass(['bottom', 'top']);
18815 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18817 * place to the top of element!
18821 this.picker().addClass('top');
18822 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18827 this.picker().addClass('bottom');
18829 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18832 parseDate : function(value)
18834 if(!value || value instanceof Date){
18837 var v = Date.parseDate(value, this.format);
18838 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18839 v = Date.parseDate(value, 'Y-m-d');
18841 if(!v && this.altFormats){
18842 if(!this.altFormatsArray){
18843 this.altFormatsArray = this.altFormats.split("|");
18845 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18846 v = Date.parseDate(value, this.altFormatsArray[i]);
18852 formatDate : function(date, fmt)
18854 return (!date || !(date instanceof Date)) ?
18855 date : date.dateFormat(fmt || this.format);
18858 onFocus : function()
18860 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18864 onBlur : function()
18866 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18868 var d = this.inputEl().getValue();
18875 showPopup : function()
18877 this.picker().show();
18881 this.fireEvent('showpopup', this, this.date);
18884 hidePopup : function()
18886 if(this.isInline) {
18889 this.picker().hide();
18890 this.viewMode = this.startViewMode;
18893 this.fireEvent('hidepopup', this, this.date);
18897 onMousedown: function(e)
18899 e.stopPropagation();
18900 e.preventDefault();
18905 Roo.bootstrap.DateField.superclass.keyup.call(this);
18909 setValue: function(v)
18911 if(this.fireEvent('beforeselect', this, v) !== false){
18912 var d = new Date(this.parseDate(v) ).clearTime();
18914 if(isNaN(d.getTime())){
18915 this.date = this.viewDate = '';
18916 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18920 v = this.formatDate(d);
18922 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18924 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18928 this.fireEvent('select', this, this.date);
18932 getValue: function()
18934 return this.formatDate(this.date);
18937 fireKey: function(e)
18939 if (!this.picker().isVisible()){
18940 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18946 var dateChanged = false,
18948 newDate, newViewDate;
18953 e.preventDefault();
18957 if (!this.keyboardNavigation) {
18960 dir = e.keyCode == 37 ? -1 : 1;
18963 newDate = this.moveYear(this.date, dir);
18964 newViewDate = this.moveYear(this.viewDate, dir);
18965 } else if (e.shiftKey){
18966 newDate = this.moveMonth(this.date, dir);
18967 newViewDate = this.moveMonth(this.viewDate, dir);
18969 newDate = new Date(this.date);
18970 newDate.setUTCDate(this.date.getUTCDate() + dir);
18971 newViewDate = new Date(this.viewDate);
18972 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18974 if (this.dateWithinRange(newDate)){
18975 this.date = newDate;
18976 this.viewDate = newViewDate;
18977 this.setValue(this.formatDate(this.date));
18979 e.preventDefault();
18980 dateChanged = true;
18985 if (!this.keyboardNavigation) {
18988 dir = e.keyCode == 38 ? -1 : 1;
18990 newDate = this.moveYear(this.date, dir);
18991 newViewDate = this.moveYear(this.viewDate, dir);
18992 } else if (e.shiftKey){
18993 newDate = this.moveMonth(this.date, dir);
18994 newViewDate = this.moveMonth(this.viewDate, dir);
18996 newDate = new Date(this.date);
18997 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18998 newViewDate = new Date(this.viewDate);
18999 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19001 if (this.dateWithinRange(newDate)){
19002 this.date = newDate;
19003 this.viewDate = newViewDate;
19004 this.setValue(this.formatDate(this.date));
19006 e.preventDefault();
19007 dateChanged = true;
19011 this.setValue(this.formatDate(this.date));
19013 e.preventDefault();
19016 this.setValue(this.formatDate(this.date));
19030 onClick: function(e)
19032 e.stopPropagation();
19033 e.preventDefault();
19035 var target = e.getTarget();
19037 if(target.nodeName.toLowerCase() === 'i'){
19038 target = Roo.get(target).dom.parentNode;
19041 var nodeName = target.nodeName;
19042 var className = target.className;
19043 var html = target.innerHTML;
19044 //Roo.log(nodeName);
19046 switch(nodeName.toLowerCase()) {
19048 switch(className) {
19054 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19055 switch(this.viewMode){
19057 this.viewDate = this.moveMonth(this.viewDate, dir);
19061 this.viewDate = this.moveYear(this.viewDate, dir);
19067 var date = new Date();
19068 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19070 this.setValue(this.formatDate(this.date));
19077 if (className.indexOf('disabled') < 0) {
19078 this.viewDate.setUTCDate(1);
19079 if (className.indexOf('month') > -1) {
19080 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19082 var year = parseInt(html, 10) || 0;
19083 this.viewDate.setUTCFullYear(year);
19087 if(this.singleMode){
19088 this.setValue(this.formatDate(this.viewDate));
19099 //Roo.log(className);
19100 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19101 var day = parseInt(html, 10) || 1;
19102 var year = this.viewDate.getUTCFullYear(),
19103 month = this.viewDate.getUTCMonth();
19105 if (className.indexOf('old') > -1) {
19112 } else if (className.indexOf('new') > -1) {
19120 //Roo.log([year,month,day]);
19121 this.date = this.UTCDate(year, month, day,0,0,0,0);
19122 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19124 //Roo.log(this.formatDate(this.date));
19125 this.setValue(this.formatDate(this.date));
19132 setStartDate: function(startDate)
19134 this.startDate = startDate || -Infinity;
19135 if (this.startDate !== -Infinity) {
19136 this.startDate = this.parseDate(this.startDate);
19139 this.updateNavArrows();
19142 setEndDate: function(endDate)
19144 this.endDate = endDate || Infinity;
19145 if (this.endDate !== Infinity) {
19146 this.endDate = this.parseDate(this.endDate);
19149 this.updateNavArrows();
19152 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19154 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19155 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19156 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19158 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19159 return parseInt(d, 10);
19162 this.updateNavArrows();
19165 updateNavArrows: function()
19167 if(this.singleMode){
19171 var d = new Date(this.viewDate),
19172 year = d.getUTCFullYear(),
19173 month = d.getUTCMonth();
19175 Roo.each(this.picker().select('.prev', true).elements, function(v){
19177 switch (this.viewMode) {
19180 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19186 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19193 Roo.each(this.picker().select('.next', true).elements, function(v){
19195 switch (this.viewMode) {
19198 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19204 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19212 moveMonth: function(date, dir)
19217 var new_date = new Date(date.valueOf()),
19218 day = new_date.getUTCDate(),
19219 month = new_date.getUTCMonth(),
19220 mag = Math.abs(dir),
19222 dir = dir > 0 ? 1 : -1;
19225 // If going back one month, make sure month is not current month
19226 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19228 return new_date.getUTCMonth() == month;
19230 // If going forward one month, make sure month is as expected
19231 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19233 return new_date.getUTCMonth() != new_month;
19235 new_month = month + dir;
19236 new_date.setUTCMonth(new_month);
19237 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19238 if (new_month < 0 || new_month > 11) {
19239 new_month = (new_month + 12) % 12;
19242 // For magnitudes >1, move one month at a time...
19243 for (var i=0; i<mag; i++) {
19244 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19245 new_date = this.moveMonth(new_date, dir);
19247 // ...then reset the day, keeping it in the new month
19248 new_month = new_date.getUTCMonth();
19249 new_date.setUTCDate(day);
19251 return new_month != new_date.getUTCMonth();
19254 // Common date-resetting loop -- if date is beyond end of month, make it
19257 new_date.setUTCDate(--day);
19258 new_date.setUTCMonth(new_month);
19263 moveYear: function(date, dir)
19265 return this.moveMonth(date, dir*12);
19268 dateWithinRange: function(date)
19270 return date >= this.startDate && date <= this.endDate;
19276 this.picker().remove();
19279 validateValue : function(value)
19281 if(this.getVisibilityEl().hasClass('hidden')){
19285 if(value.length < 1) {
19286 if(this.allowBlank){
19292 if(value.length < this.minLength){
19295 if(value.length > this.maxLength){
19299 var vt = Roo.form.VTypes;
19300 if(!vt[this.vtype](value, this)){
19304 if(typeof this.validator == "function"){
19305 var msg = this.validator(value);
19311 if(this.regex && !this.regex.test(value)){
19315 if(typeof(this.parseDate(value)) == 'undefined'){
19319 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19323 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19333 this.date = this.viewDate = '';
19335 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19340 Roo.apply(Roo.bootstrap.DateField, {
19351 html: '<i class="fa fa-arrow-left"/>'
19361 html: '<i class="fa fa-arrow-right"/>'
19403 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19404 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19405 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19406 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19407 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19420 navFnc: 'FullYear',
19425 navFnc: 'FullYear',
19430 Roo.apply(Roo.bootstrap.DateField, {
19434 cls: 'datepicker dropdown-menu roo-dynamic',
19438 cls: 'datepicker-days',
19442 cls: 'table-condensed',
19444 Roo.bootstrap.DateField.head,
19448 Roo.bootstrap.DateField.footer
19455 cls: 'datepicker-months',
19459 cls: 'table-condensed',
19461 Roo.bootstrap.DateField.head,
19462 Roo.bootstrap.DateField.content,
19463 Roo.bootstrap.DateField.footer
19470 cls: 'datepicker-years',
19474 cls: 'table-condensed',
19476 Roo.bootstrap.DateField.head,
19477 Roo.bootstrap.DateField.content,
19478 Roo.bootstrap.DateField.footer
19497 * @class Roo.bootstrap.TimeField
19498 * @extends Roo.bootstrap.Input
19499 * Bootstrap DateField class
19503 * Create a new TimeField
19504 * @param {Object} config The config object
19507 Roo.bootstrap.TimeField = function(config){
19508 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19512 * Fires when this field show.
19513 * @param {Roo.bootstrap.DateField} thisthis
19514 * @param {Mixed} date The date value
19519 * Fires when this field hide.
19520 * @param {Roo.bootstrap.DateField} this
19521 * @param {Mixed} date The date value
19526 * Fires when select a date.
19527 * @param {Roo.bootstrap.DateField} this
19528 * @param {Mixed} date The date value
19534 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19537 * @cfg {String} format
19538 * The default time format string which can be overriden for localization support. The format must be
19539 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19543 onRender: function(ct, position)
19546 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19548 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19550 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19552 this.pop = this.picker().select('>.datepicker-time',true).first();
19553 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19555 this.picker().on('mousedown', this.onMousedown, this);
19556 this.picker().on('click', this.onClick, this);
19558 this.picker().addClass('datepicker-dropdown');
19563 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19564 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19565 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19566 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19567 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19568 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19572 fireKey: function(e){
19573 if (!this.picker().isVisible()){
19574 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19580 e.preventDefault();
19588 this.onTogglePeriod();
19591 this.onIncrementMinutes();
19594 this.onDecrementMinutes();
19603 onClick: function(e) {
19604 e.stopPropagation();
19605 e.preventDefault();
19608 picker : function()
19610 return this.el.select('.datepicker', true).first();
19613 fillTime: function()
19615 var time = this.pop.select('tbody', true).first();
19617 time.dom.innerHTML = '';
19632 cls: 'hours-up glyphicon glyphicon-chevron-up'
19652 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19673 cls: 'timepicker-hour',
19688 cls: 'timepicker-minute',
19703 cls: 'btn btn-primary period',
19725 cls: 'hours-down glyphicon glyphicon-chevron-down'
19745 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19763 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19770 var hours = this.time.getHours();
19771 var minutes = this.time.getMinutes();
19784 hours = hours - 12;
19788 hours = '0' + hours;
19792 minutes = '0' + minutes;
19795 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19796 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19797 this.pop.select('button', true).first().dom.innerHTML = period;
19803 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19805 var cls = ['bottom'];
19807 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19814 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19819 this.picker().addClass(cls.join('-'));
19823 Roo.each(cls, function(c){
19825 _this.picker().setTop(_this.inputEl().getHeight());
19829 _this.picker().setTop(0 - _this.picker().getHeight());
19834 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19838 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19845 onFocus : function()
19847 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19851 onBlur : function()
19853 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19859 this.picker().show();
19864 this.fireEvent('show', this, this.date);
19869 this.picker().hide();
19872 this.fireEvent('hide', this, this.date);
19875 setTime : function()
19878 this.setValue(this.time.format(this.format));
19880 this.fireEvent('select', this, this.date);
19885 onMousedown: function(e){
19886 e.stopPropagation();
19887 e.preventDefault();
19890 onIncrementHours: function()
19892 Roo.log('onIncrementHours');
19893 this.time = this.time.add(Date.HOUR, 1);
19898 onDecrementHours: function()
19900 Roo.log('onDecrementHours');
19901 this.time = this.time.add(Date.HOUR, -1);
19905 onIncrementMinutes: function()
19907 Roo.log('onIncrementMinutes');
19908 this.time = this.time.add(Date.MINUTE, 1);
19912 onDecrementMinutes: function()
19914 Roo.log('onDecrementMinutes');
19915 this.time = this.time.add(Date.MINUTE, -1);
19919 onTogglePeriod: function()
19921 Roo.log('onTogglePeriod');
19922 this.time = this.time.add(Date.HOUR, 12);
19929 Roo.apply(Roo.bootstrap.TimeField, {
19959 cls: 'btn btn-info ok',
19971 Roo.apply(Roo.bootstrap.TimeField, {
19975 cls: 'datepicker dropdown-menu',
19979 cls: 'datepicker-time',
19983 cls: 'table-condensed',
19985 Roo.bootstrap.TimeField.content,
19986 Roo.bootstrap.TimeField.footer
20005 * @class Roo.bootstrap.MonthField
20006 * @extends Roo.bootstrap.Input
20007 * Bootstrap MonthField class
20009 * @cfg {String} language default en
20012 * Create a new MonthField
20013 * @param {Object} config The config object
20016 Roo.bootstrap.MonthField = function(config){
20017 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20022 * Fires when this field show.
20023 * @param {Roo.bootstrap.MonthField} this
20024 * @param {Mixed} date The date value
20029 * Fires when this field hide.
20030 * @param {Roo.bootstrap.MonthField} this
20031 * @param {Mixed} date The date value
20036 * Fires when select a date.
20037 * @param {Roo.bootstrap.MonthField} this
20038 * @param {String} oldvalue The old value
20039 * @param {String} newvalue The new value
20045 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20047 onRender: function(ct, position)
20050 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20052 this.language = this.language || 'en';
20053 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20054 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20056 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20057 this.isInline = false;
20058 this.isInput = true;
20059 this.component = this.el.select('.add-on', true).first() || false;
20060 this.component = (this.component && this.component.length === 0) ? false : this.component;
20061 this.hasInput = this.component && this.inputEL().length;
20063 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20065 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20067 this.picker().on('mousedown', this.onMousedown, this);
20068 this.picker().on('click', this.onClick, this);
20070 this.picker().addClass('datepicker-dropdown');
20072 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20073 v.setStyle('width', '189px');
20080 if(this.isInline) {
20086 setValue: function(v, suppressEvent)
20088 var o = this.getValue();
20090 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20094 if(suppressEvent !== true){
20095 this.fireEvent('select', this, o, v);
20100 getValue: function()
20105 onClick: function(e)
20107 e.stopPropagation();
20108 e.preventDefault();
20110 var target = e.getTarget();
20112 if(target.nodeName.toLowerCase() === 'i'){
20113 target = Roo.get(target).dom.parentNode;
20116 var nodeName = target.nodeName;
20117 var className = target.className;
20118 var html = target.innerHTML;
20120 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20124 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20126 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20132 picker : function()
20134 return this.pickerEl;
20137 fillMonths: function()
20140 var months = this.picker().select('>.datepicker-months td', true).first();
20142 months.dom.innerHTML = '';
20148 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20151 months.createChild(month);
20160 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20161 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20164 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20165 e.removeClass('active');
20167 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20168 e.addClass('active');
20175 if(this.isInline) {
20179 this.picker().removeClass(['bottom', 'top']);
20181 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20183 * place to the top of element!
20187 this.picker().addClass('top');
20188 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20193 this.picker().addClass('bottom');
20195 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20198 onFocus : function()
20200 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20204 onBlur : function()
20206 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20208 var d = this.inputEl().getValue();
20217 this.picker().show();
20218 this.picker().select('>.datepicker-months', true).first().show();
20222 this.fireEvent('show', this, this.date);
20227 if(this.isInline) {
20230 this.picker().hide();
20231 this.fireEvent('hide', this, this.date);
20235 onMousedown: function(e)
20237 e.stopPropagation();
20238 e.preventDefault();
20243 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20247 fireKey: function(e)
20249 if (!this.picker().isVisible()){
20250 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20261 e.preventDefault();
20265 dir = e.keyCode == 37 ? -1 : 1;
20267 this.vIndex = this.vIndex + dir;
20269 if(this.vIndex < 0){
20273 if(this.vIndex > 11){
20277 if(isNaN(this.vIndex)){
20281 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20287 dir = e.keyCode == 38 ? -1 : 1;
20289 this.vIndex = this.vIndex + dir * 4;
20291 if(this.vIndex < 0){
20295 if(this.vIndex > 11){
20299 if(isNaN(this.vIndex)){
20303 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20308 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20309 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20313 e.preventDefault();
20316 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20317 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20333 this.picker().remove();
20338 Roo.apply(Roo.bootstrap.MonthField, {
20357 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20358 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20363 Roo.apply(Roo.bootstrap.MonthField, {
20367 cls: 'datepicker dropdown-menu roo-dynamic',
20371 cls: 'datepicker-months',
20375 cls: 'table-condensed',
20377 Roo.bootstrap.DateField.content
20397 * @class Roo.bootstrap.CheckBox
20398 * @extends Roo.bootstrap.Input
20399 * Bootstrap CheckBox class
20401 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20402 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20403 * @cfg {String} boxLabel The text that appears beside the checkbox
20404 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20405 * @cfg {Boolean} checked initnal the element
20406 * @cfg {Boolean} inline inline the element (default false)
20407 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20408 * @cfg {String} tooltip label tooltip
20411 * Create a new CheckBox
20412 * @param {Object} config The config object
20415 Roo.bootstrap.CheckBox = function(config){
20416 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20421 * Fires when the element is checked or unchecked.
20422 * @param {Roo.bootstrap.CheckBox} this This input
20423 * @param {Boolean} checked The new checked value
20428 * Fires when the element is click.
20429 * @param {Roo.bootstrap.CheckBox} this This input
20436 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20438 inputType: 'checkbox',
20447 getAutoCreate : function()
20449 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20455 cfg.cls = 'form-group ' + this.inputType; //input-group
20458 cfg.cls += ' ' + this.inputType + '-inline';
20464 type : this.inputType,
20465 value : this.inputValue,
20466 cls : 'roo-' + this.inputType, //'form-box',
20467 placeholder : this.placeholder || ''
20471 if(this.inputType != 'radio'){
20475 cls : 'roo-hidden-value',
20476 value : this.checked ? this.inputValue : this.valueOff
20481 if (this.weight) { // Validity check?
20482 cfg.cls += " " + this.inputType + "-" + this.weight;
20485 if (this.disabled) {
20486 input.disabled=true;
20490 input.checked = this.checked;
20495 input.name = this.name;
20497 if(this.inputType != 'radio'){
20498 hidden.name = this.name;
20499 input.name = '_hidden_' + this.name;
20504 input.cls += ' input-' + this.size;
20509 ['xs','sm','md','lg'].map(function(size){
20510 if (settings[size]) {
20511 cfg.cls += ' col-' + size + '-' + settings[size];
20515 var inputblock = input;
20517 if (this.before || this.after) {
20520 cls : 'input-group',
20525 inputblock.cn.push({
20527 cls : 'input-group-addon',
20532 inputblock.cn.push(input);
20534 if(this.inputType != 'radio'){
20535 inputblock.cn.push(hidden);
20539 inputblock.cn.push({
20541 cls : 'input-group-addon',
20548 if (align ==='left' && this.fieldLabel.length) {
20549 // Roo.log("left and has label");
20554 cls : 'control-label',
20555 html : this.fieldLabel
20565 if(this.labelWidth > 12){
20566 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20569 if(this.labelWidth < 13 && this.labelmd == 0){
20570 this.labelmd = this.labelWidth;
20573 if(this.labellg > 0){
20574 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20575 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20578 if(this.labelmd > 0){
20579 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20580 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20583 if(this.labelsm > 0){
20584 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20585 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20588 if(this.labelxs > 0){
20589 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20590 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20593 } else if ( this.fieldLabel.length) {
20594 // Roo.log(" label");
20598 tag: this.boxLabel ? 'span' : 'label',
20600 cls: 'control-label box-input-label',
20601 //cls : 'input-group-addon',
20602 html : this.fieldLabel
20611 // Roo.log(" no label && no align");
20612 cfg.cn = [ inputblock ] ;
20618 var boxLabelCfg = {
20620 //'for': id, // box label is handled by onclick - so no for...
20622 html: this.boxLabel
20626 boxLabelCfg.tooltip = this.tooltip;
20629 cfg.cn.push(boxLabelCfg);
20632 if(this.inputType != 'radio'){
20633 cfg.cn.push(hidden);
20641 * return the real input element.
20643 inputEl: function ()
20645 return this.el.select('input.roo-' + this.inputType,true).first();
20647 hiddenEl: function ()
20649 return this.el.select('input.roo-hidden-value',true).first();
20652 labelEl: function()
20654 return this.el.select('label.control-label',true).first();
20656 /* depricated... */
20660 return this.labelEl();
20663 boxLabelEl: function()
20665 return this.el.select('label.box-label',true).first();
20668 initEvents : function()
20670 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20672 this.inputEl().on('click', this.onClick, this);
20674 if (this.boxLabel) {
20675 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20678 this.startValue = this.getValue();
20681 Roo.bootstrap.CheckBox.register(this);
20685 onClick : function(e)
20687 if(this.fireEvent('click', this, e) !== false){
20688 this.setChecked(!this.checked);
20693 setChecked : function(state,suppressEvent)
20695 this.startValue = this.getValue();
20697 if(this.inputType == 'radio'){
20699 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20700 e.dom.checked = false;
20703 this.inputEl().dom.checked = true;
20705 this.inputEl().dom.value = this.inputValue;
20707 if(suppressEvent !== true){
20708 this.fireEvent('check', this, true);
20716 this.checked = state;
20718 this.inputEl().dom.checked = state;
20721 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20723 if(suppressEvent !== true){
20724 this.fireEvent('check', this, state);
20730 getValue : function()
20732 if(this.inputType == 'radio'){
20733 return this.getGroupValue();
20736 return this.hiddenEl().dom.value;
20740 getGroupValue : function()
20742 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20746 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20749 setValue : function(v,suppressEvent)
20751 if(this.inputType == 'radio'){
20752 this.setGroupValue(v, suppressEvent);
20756 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20761 setGroupValue : function(v, suppressEvent)
20763 this.startValue = this.getValue();
20765 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20766 e.dom.checked = false;
20768 if(e.dom.value == v){
20769 e.dom.checked = true;
20773 if(suppressEvent !== true){
20774 this.fireEvent('check', this, true);
20782 validate : function()
20784 if(this.getVisibilityEl().hasClass('hidden')){
20790 (this.inputType == 'radio' && this.validateRadio()) ||
20791 (this.inputType == 'checkbox' && this.validateCheckbox())
20797 this.markInvalid();
20801 validateRadio : function()
20803 if(this.getVisibilityEl().hasClass('hidden')){
20807 if(this.allowBlank){
20813 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20814 if(!e.dom.checked){
20826 validateCheckbox : function()
20829 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20830 //return (this.getValue() == this.inputValue) ? true : false;
20833 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20841 for(var i in group){
20842 if(group[i].el.isVisible(true)){
20850 for(var i in group){
20855 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20862 * Mark this field as valid
20864 markValid : function()
20868 this.fireEvent('valid', this);
20870 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20873 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20880 if(this.inputType == 'radio'){
20881 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20882 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20883 e.findParent('.form-group', false, true).addClass(_this.validClass);
20890 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20891 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20895 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20901 for(var i in group){
20902 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20903 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20908 * Mark this field as invalid
20909 * @param {String} msg The validation message
20911 markInvalid : function(msg)
20913 if(this.allowBlank){
20919 this.fireEvent('invalid', this, msg);
20921 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20924 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20928 label.markInvalid();
20931 if(this.inputType == 'radio'){
20932 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20933 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20934 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20941 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20942 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20946 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20952 for(var i in group){
20953 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20954 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20959 clearInvalid : function()
20961 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20963 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20965 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20967 if (label && label.iconEl) {
20968 label.iconEl.removeClass(label.validClass);
20969 label.iconEl.removeClass(label.invalidClass);
20973 disable : function()
20975 if(this.inputType != 'radio'){
20976 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20983 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20984 _this.getActionEl().addClass(this.disabledClass);
20985 e.dom.disabled = true;
20989 this.disabled = true;
20990 this.fireEvent("disable", this);
20994 enable : function()
20996 if(this.inputType != 'radio'){
20997 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21004 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21005 _this.getActionEl().removeClass(this.disabledClass);
21006 e.dom.disabled = false;
21010 this.disabled = false;
21011 this.fireEvent("enable", this);
21015 setBoxLabel : function(v)
21020 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21026 Roo.apply(Roo.bootstrap.CheckBox, {
21031 * register a CheckBox Group
21032 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21034 register : function(checkbox)
21036 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21037 this.groups[checkbox.groupId] = {};
21040 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21044 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21048 * fetch a CheckBox Group based on the group ID
21049 * @param {string} the group ID
21050 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21052 get: function(groupId) {
21053 if (typeof(this.groups[groupId]) == 'undefined') {
21057 return this.groups[groupId] ;
21070 * @class Roo.bootstrap.Radio
21071 * @extends Roo.bootstrap.Component
21072 * Bootstrap Radio class
21073 * @cfg {String} boxLabel - the label associated
21074 * @cfg {String} value - the value of radio
21077 * Create a new Radio
21078 * @param {Object} config The config object
21080 Roo.bootstrap.Radio = function(config){
21081 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21085 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21091 getAutoCreate : function()
21095 cls : 'form-group radio',
21100 html : this.boxLabel
21108 initEvents : function()
21110 this.parent().register(this);
21112 this.el.on('click', this.onClick, this);
21116 onClick : function(e)
21118 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21119 this.setChecked(true);
21123 setChecked : function(state, suppressEvent)
21125 this.parent().setValue(this.value, suppressEvent);
21129 setBoxLabel : function(v)
21134 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21149 * @class Roo.bootstrap.SecurePass
21150 * @extends Roo.bootstrap.Input
21151 * Bootstrap SecurePass class
21155 * Create a new SecurePass
21156 * @param {Object} config The config object
21159 Roo.bootstrap.SecurePass = function (config) {
21160 // these go here, so the translation tool can replace them..
21162 PwdEmpty: "Please type a password, and then retype it to confirm.",
21163 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21164 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21165 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21166 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21167 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21168 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21169 TooWeak: "Your password is Too Weak."
21171 this.meterLabel = "Password strength:";
21172 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21173 this.meterClass = [
21174 "roo-password-meter-tooweak",
21175 "roo-password-meter-weak",
21176 "roo-password-meter-medium",
21177 "roo-password-meter-strong",
21178 "roo-password-meter-grey"
21183 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21186 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21188 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21190 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21191 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21192 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21193 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21194 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21195 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21196 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21206 * @cfg {String/Object} Label for the strength meter (defaults to
21207 * 'Password strength:')
21212 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21213 * ['Weak', 'Medium', 'Strong'])
21216 pwdStrengths: false,
21229 initEvents: function ()
21231 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21233 if (this.el.is('input[type=password]') && Roo.isSafari) {
21234 this.el.on('keydown', this.SafariOnKeyDown, this);
21237 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21240 onRender: function (ct, position)
21242 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21243 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21244 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21246 this.trigger.createChild({
21251 cls: 'roo-password-meter-grey col-xs-12',
21254 //width: this.meterWidth + 'px'
21258 cls: 'roo-password-meter-text'
21264 if (this.hideTrigger) {
21265 this.trigger.setDisplayed(false);
21267 this.setSize(this.width || '', this.height || '');
21270 onDestroy: function ()
21272 if (this.trigger) {
21273 this.trigger.removeAllListeners();
21274 this.trigger.remove();
21277 this.wrap.remove();
21279 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21282 checkStrength: function ()
21284 var pwd = this.inputEl().getValue();
21285 if (pwd == this._lastPwd) {
21290 if (this.ClientSideStrongPassword(pwd)) {
21292 } else if (this.ClientSideMediumPassword(pwd)) {
21294 } else if (this.ClientSideWeakPassword(pwd)) {
21300 Roo.log('strength1: ' + strength);
21302 //var pm = this.trigger.child('div/div/div').dom;
21303 var pm = this.trigger.child('div/div');
21304 pm.removeClass(this.meterClass);
21305 pm.addClass(this.meterClass[strength]);
21308 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21310 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21312 this._lastPwd = pwd;
21316 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21318 this._lastPwd = '';
21320 var pm = this.trigger.child('div/div');
21321 pm.removeClass(this.meterClass);
21322 pm.addClass('roo-password-meter-grey');
21325 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21328 this.inputEl().dom.type='password';
21331 validateValue: function (value)
21334 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21337 if (value.length == 0) {
21338 if (this.allowBlank) {
21339 this.clearInvalid();
21343 this.markInvalid(this.errors.PwdEmpty);
21344 this.errorMsg = this.errors.PwdEmpty;
21352 if ('[\x21-\x7e]*'.match(value)) {
21353 this.markInvalid(this.errors.PwdBadChar);
21354 this.errorMsg = this.errors.PwdBadChar;
21357 if (value.length < 6) {
21358 this.markInvalid(this.errors.PwdShort);
21359 this.errorMsg = this.errors.PwdShort;
21362 if (value.length > 16) {
21363 this.markInvalid(this.errors.PwdLong);
21364 this.errorMsg = this.errors.PwdLong;
21368 if (this.ClientSideStrongPassword(value)) {
21370 } else if (this.ClientSideMediumPassword(value)) {
21372 } else if (this.ClientSideWeakPassword(value)) {
21379 if (strength < 2) {
21380 //this.markInvalid(this.errors.TooWeak);
21381 this.errorMsg = this.errors.TooWeak;
21386 console.log('strength2: ' + strength);
21388 //var pm = this.trigger.child('div/div/div').dom;
21390 var pm = this.trigger.child('div/div');
21391 pm.removeClass(this.meterClass);
21392 pm.addClass(this.meterClass[strength]);
21394 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21396 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21398 this.errorMsg = '';
21402 CharacterSetChecks: function (type)
21405 this.fResult = false;
21408 isctype: function (character, type)
21411 case this.kCapitalLetter:
21412 if (character >= 'A' && character <= 'Z') {
21417 case this.kSmallLetter:
21418 if (character >= 'a' && character <= 'z') {
21424 if (character >= '0' && character <= '9') {
21429 case this.kPunctuation:
21430 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21441 IsLongEnough: function (pwd, size)
21443 return !(pwd == null || isNaN(size) || pwd.length < size);
21446 SpansEnoughCharacterSets: function (word, nb)
21448 if (!this.IsLongEnough(word, nb))
21453 var characterSetChecks = new Array(
21454 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21455 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21458 for (var index = 0; index < word.length; ++index) {
21459 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21460 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21461 characterSetChecks[nCharSet].fResult = true;
21468 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21469 if (characterSetChecks[nCharSet].fResult) {
21474 if (nCharSets < nb) {
21480 ClientSideStrongPassword: function (pwd)
21482 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21485 ClientSideMediumPassword: function (pwd)
21487 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21490 ClientSideWeakPassword: function (pwd)
21492 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21495 })//<script type="text/javascript">
21498 * Based Ext JS Library 1.1.1
21499 * Copyright(c) 2006-2007, Ext JS, LLC.
21505 * @class Roo.HtmlEditorCore
21506 * @extends Roo.Component
21507 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21509 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21512 Roo.HtmlEditorCore = function(config){
21515 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21520 * @event initialize
21521 * Fires when the editor is fully initialized (including the iframe)
21522 * @param {Roo.HtmlEditorCore} this
21527 * Fires when the editor is first receives the focus. Any insertion must wait
21528 * until after this event.
21529 * @param {Roo.HtmlEditorCore} this
21533 * @event beforesync
21534 * Fires before the textarea is updated with content from the editor iframe. Return false
21535 * to cancel the sync.
21536 * @param {Roo.HtmlEditorCore} this
21537 * @param {String} html
21541 * @event beforepush
21542 * Fires before the iframe editor is updated with content from the textarea. Return false
21543 * to cancel the push.
21544 * @param {Roo.HtmlEditorCore} this
21545 * @param {String} html
21550 * Fires when the textarea is updated with content from the editor iframe.
21551 * @param {Roo.HtmlEditorCore} this
21552 * @param {String} html
21557 * Fires when the iframe editor is updated with content from the textarea.
21558 * @param {Roo.HtmlEditorCore} this
21559 * @param {String} html
21564 * @event editorevent
21565 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21566 * @param {Roo.HtmlEditorCore} this
21572 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21574 // defaults : white / black...
21575 this.applyBlacklists();
21582 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21586 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21592 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21597 * @cfg {Number} height (in pixels)
21601 * @cfg {Number} width (in pixels)
21606 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21609 stylesheets: false,
21614 // private properties
21615 validationEvent : false,
21617 initialized : false,
21619 sourceEditMode : false,
21620 onFocus : Roo.emptyFn,
21622 hideMode:'offsets',
21626 // blacklist + whitelisted elements..
21633 * Protected method that will not generally be called directly. It
21634 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21635 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21637 getDocMarkup : function(){
21641 // inherit styels from page...??
21642 if (this.stylesheets === false) {
21644 Roo.get(document.head).select('style').each(function(node) {
21645 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21648 Roo.get(document.head).select('link').each(function(node) {
21649 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21652 } else if (!this.stylesheets.length) {
21654 st = '<style type="text/css">' +
21655 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21658 st = '<style type="text/css">' +
21663 st += '<style type="text/css">' +
21664 'IMG { cursor: pointer } ' +
21667 var cls = 'roo-htmleditor-body';
21669 if(this.bodyCls.length){
21670 cls += ' ' + this.bodyCls;
21673 return '<html><head>' + st +
21674 //<style type="text/css">' +
21675 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21677 ' </head><body class="' + cls + '"></body></html>';
21681 onRender : function(ct, position)
21684 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21685 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21688 this.el.dom.style.border = '0 none';
21689 this.el.dom.setAttribute('tabIndex', -1);
21690 this.el.addClass('x-hidden hide');
21694 if(Roo.isIE){ // fix IE 1px bogus margin
21695 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21699 this.frameId = Roo.id();
21703 var iframe = this.owner.wrap.createChild({
21705 cls: 'form-control', // bootstrap..
21707 name: this.frameId,
21708 frameBorder : 'no',
21709 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21714 this.iframe = iframe.dom;
21716 this.assignDocWin();
21718 this.doc.designMode = 'on';
21721 this.doc.write(this.getDocMarkup());
21725 var task = { // must defer to wait for browser to be ready
21727 //console.log("run task?" + this.doc.readyState);
21728 this.assignDocWin();
21729 if(this.doc.body || this.doc.readyState == 'complete'){
21731 this.doc.designMode="on";
21735 Roo.TaskMgr.stop(task);
21736 this.initEditor.defer(10, this);
21743 Roo.TaskMgr.start(task);
21748 onResize : function(w, h)
21750 Roo.log('resize: ' +w + ',' + h );
21751 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21755 if(typeof w == 'number'){
21757 this.iframe.style.width = w + 'px';
21759 if(typeof h == 'number'){
21761 this.iframe.style.height = h + 'px';
21763 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21770 * Toggles the editor between standard and source edit mode.
21771 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21773 toggleSourceEdit : function(sourceEditMode){
21775 this.sourceEditMode = sourceEditMode === true;
21777 if(this.sourceEditMode){
21779 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21782 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21783 //this.iframe.className = '';
21786 //this.setSize(this.owner.wrap.getSize());
21787 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21794 * Protected method that will not generally be called directly. If you need/want
21795 * custom HTML cleanup, this is the method you should override.
21796 * @param {String} html The HTML to be cleaned
21797 * return {String} The cleaned HTML
21799 cleanHtml : function(html){
21800 html = String(html);
21801 if(html.length > 5){
21802 if(Roo.isSafari){ // strip safari nonsense
21803 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21806 if(html == ' '){
21813 * HTML Editor -> Textarea
21814 * Protected method that will not generally be called directly. Syncs the contents
21815 * of the editor iframe with the textarea.
21817 syncValue : function(){
21818 if(this.initialized){
21819 var bd = (this.doc.body || this.doc.documentElement);
21820 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21821 var html = bd.innerHTML;
21823 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21824 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21826 html = '<div style="'+m[0]+'">' + html + '</div>';
21829 html = this.cleanHtml(html);
21830 // fix up the special chars.. normaly like back quotes in word...
21831 // however we do not want to do this with chinese..
21832 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21833 var cc = b.charCodeAt();
21835 (cc >= 0x4E00 && cc < 0xA000 ) ||
21836 (cc >= 0x3400 && cc < 0x4E00 ) ||
21837 (cc >= 0xf900 && cc < 0xfb00 )
21843 if(this.owner.fireEvent('beforesync', this, html) !== false){
21844 this.el.dom.value = html;
21845 this.owner.fireEvent('sync', this, html);
21851 * Protected method that will not generally be called directly. Pushes the value of the textarea
21852 * into the iframe editor.
21854 pushValue : function(){
21855 if(this.initialized){
21856 var v = this.el.dom.value.trim();
21858 // if(v.length < 1){
21862 if(this.owner.fireEvent('beforepush', this, v) !== false){
21863 var d = (this.doc.body || this.doc.documentElement);
21865 this.cleanUpPaste();
21866 this.el.dom.value = d.innerHTML;
21867 this.owner.fireEvent('push', this, v);
21873 deferFocus : function(){
21874 this.focus.defer(10, this);
21878 focus : function(){
21879 if(this.win && !this.sourceEditMode){
21886 assignDocWin: function()
21888 var iframe = this.iframe;
21891 this.doc = iframe.contentWindow.document;
21892 this.win = iframe.contentWindow;
21894 // if (!Roo.get(this.frameId)) {
21897 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21898 // this.win = Roo.get(this.frameId).dom.contentWindow;
21900 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21904 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21905 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21910 initEditor : function(){
21911 //console.log("INIT EDITOR");
21912 this.assignDocWin();
21916 this.doc.designMode="on";
21918 this.doc.write(this.getDocMarkup());
21921 var dbody = (this.doc.body || this.doc.documentElement);
21922 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21923 // this copies styles from the containing element into thsi one..
21924 // not sure why we need all of this..
21925 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21927 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21928 //ss['background-attachment'] = 'fixed'; // w3c
21929 dbody.bgProperties = 'fixed'; // ie
21930 //Roo.DomHelper.applyStyles(dbody, ss);
21931 Roo.EventManager.on(this.doc, {
21932 //'mousedown': this.onEditorEvent,
21933 'mouseup': this.onEditorEvent,
21934 'dblclick': this.onEditorEvent,
21935 'click': this.onEditorEvent,
21936 'keyup': this.onEditorEvent,
21941 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21943 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21944 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21946 this.initialized = true;
21948 this.owner.fireEvent('initialize', this);
21953 onDestroy : function(){
21959 //for (var i =0; i < this.toolbars.length;i++) {
21960 // // fixme - ask toolbars for heights?
21961 // this.toolbars[i].onDestroy();
21964 //this.wrap.dom.innerHTML = '';
21965 //this.wrap.remove();
21970 onFirstFocus : function(){
21972 this.assignDocWin();
21975 this.activated = true;
21978 if(Roo.isGecko){ // prevent silly gecko errors
21980 var s = this.win.getSelection();
21981 if(!s.focusNode || s.focusNode.nodeType != 3){
21982 var r = s.getRangeAt(0);
21983 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21988 this.execCmd('useCSS', true);
21989 this.execCmd('styleWithCSS', false);
21992 this.owner.fireEvent('activate', this);
21996 adjustFont: function(btn){
21997 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21998 //if(Roo.isSafari){ // safari
22001 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22002 if(Roo.isSafari){ // safari
22003 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22004 v = (v < 10) ? 10 : v;
22005 v = (v > 48) ? 48 : v;
22006 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22011 v = Math.max(1, v+adjust);
22013 this.execCmd('FontSize', v );
22016 onEditorEvent : function(e)
22018 this.owner.fireEvent('editorevent', this, e);
22019 // this.updateToolbar();
22020 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22023 insertTag : function(tg)
22025 // could be a bit smarter... -> wrap the current selected tRoo..
22026 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22028 range = this.createRange(this.getSelection());
22029 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22030 wrappingNode.appendChild(range.extractContents());
22031 range.insertNode(wrappingNode);
22038 this.execCmd("formatblock", tg);
22042 insertText : function(txt)
22046 var range = this.createRange();
22047 range.deleteContents();
22048 //alert(Sender.getAttribute('label'));
22050 range.insertNode(this.doc.createTextNode(txt));
22056 * Executes a Midas editor command on the editor document and performs necessary focus and
22057 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22058 * @param {String} cmd The Midas command
22059 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22061 relayCmd : function(cmd, value){
22063 this.execCmd(cmd, value);
22064 this.owner.fireEvent('editorevent', this);
22065 //this.updateToolbar();
22066 this.owner.deferFocus();
22070 * Executes a Midas editor command directly on the editor document.
22071 * For visual commands, you should use {@link #relayCmd} instead.
22072 * <b>This should only be called after the editor is initialized.</b>
22073 * @param {String} cmd The Midas command
22074 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22076 execCmd : function(cmd, value){
22077 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22084 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22086 * @param {String} text | dom node..
22088 insertAtCursor : function(text)
22091 if(!this.activated){
22097 var r = this.doc.selection.createRange();
22108 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22112 // from jquery ui (MIT licenced)
22114 var win = this.win;
22116 if (win.getSelection && win.getSelection().getRangeAt) {
22117 range = win.getSelection().getRangeAt(0);
22118 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22119 range.insertNode(node);
22120 } else if (win.document.selection && win.document.selection.createRange) {
22121 // no firefox support
22122 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22123 win.document.selection.createRange().pasteHTML(txt);
22125 // no firefox support
22126 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22127 this.execCmd('InsertHTML', txt);
22136 mozKeyPress : function(e){
22138 var c = e.getCharCode(), cmd;
22141 c = String.fromCharCode(c).toLowerCase();
22155 this.cleanUpPaste.defer(100, this);
22163 e.preventDefault();
22171 fixKeys : function(){ // load time branching for fastest keydown performance
22173 return function(e){
22174 var k = e.getKey(), r;
22177 r = this.doc.selection.createRange();
22180 r.pasteHTML('    ');
22187 r = this.doc.selection.createRange();
22189 var target = r.parentElement();
22190 if(!target || target.tagName.toLowerCase() != 'li'){
22192 r.pasteHTML('<br />');
22198 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22199 this.cleanUpPaste.defer(100, this);
22205 }else if(Roo.isOpera){
22206 return function(e){
22207 var k = e.getKey();
22211 this.execCmd('InsertHTML','    ');
22214 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22215 this.cleanUpPaste.defer(100, this);
22220 }else if(Roo.isSafari){
22221 return function(e){
22222 var k = e.getKey();
22226 this.execCmd('InsertText','\t');
22230 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22231 this.cleanUpPaste.defer(100, this);
22239 getAllAncestors: function()
22241 var p = this.getSelectedNode();
22244 a.push(p); // push blank onto stack..
22245 p = this.getParentElement();
22249 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22253 a.push(this.doc.body);
22257 lastSelNode : false,
22260 getSelection : function()
22262 this.assignDocWin();
22263 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22266 getSelectedNode: function()
22268 // this may only work on Gecko!!!
22270 // should we cache this!!!!
22275 var range = this.createRange(this.getSelection()).cloneRange();
22278 var parent = range.parentElement();
22280 var testRange = range.duplicate();
22281 testRange.moveToElementText(parent);
22282 if (testRange.inRange(range)) {
22285 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22288 parent = parent.parentElement;
22293 // is ancestor a text element.
22294 var ac = range.commonAncestorContainer;
22295 if (ac.nodeType == 3) {
22296 ac = ac.parentNode;
22299 var ar = ac.childNodes;
22302 var other_nodes = [];
22303 var has_other_nodes = false;
22304 for (var i=0;i<ar.length;i++) {
22305 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22308 // fullly contained node.
22310 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22315 // probably selected..
22316 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22317 other_nodes.push(ar[i]);
22321 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22326 has_other_nodes = true;
22328 if (!nodes.length && other_nodes.length) {
22329 nodes= other_nodes;
22331 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22337 createRange: function(sel)
22339 // this has strange effects when using with
22340 // top toolbar - not sure if it's a great idea.
22341 //this.editor.contentWindow.focus();
22342 if (typeof sel != "undefined") {
22344 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22346 return this.doc.createRange();
22349 return this.doc.createRange();
22352 getParentElement: function()
22355 this.assignDocWin();
22356 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22358 var range = this.createRange(sel);
22361 var p = range.commonAncestorContainer;
22362 while (p.nodeType == 3) { // text node
22373 * Range intersection.. the hard stuff...
22377 * [ -- selected range --- ]
22381 * if end is before start or hits it. fail.
22382 * if start is after end or hits it fail.
22384 * if either hits (but other is outside. - then it's not
22390 // @see http://www.thismuchiknow.co.uk/?p=64.
22391 rangeIntersectsNode : function(range, node)
22393 var nodeRange = node.ownerDocument.createRange();
22395 nodeRange.selectNode(node);
22397 nodeRange.selectNodeContents(node);
22400 var rangeStartRange = range.cloneRange();
22401 rangeStartRange.collapse(true);
22403 var rangeEndRange = range.cloneRange();
22404 rangeEndRange.collapse(false);
22406 var nodeStartRange = nodeRange.cloneRange();
22407 nodeStartRange.collapse(true);
22409 var nodeEndRange = nodeRange.cloneRange();
22410 nodeEndRange.collapse(false);
22412 return rangeStartRange.compareBoundaryPoints(
22413 Range.START_TO_START, nodeEndRange) == -1 &&
22414 rangeEndRange.compareBoundaryPoints(
22415 Range.START_TO_START, nodeStartRange) == 1;
22419 rangeCompareNode : function(range, node)
22421 var nodeRange = node.ownerDocument.createRange();
22423 nodeRange.selectNode(node);
22425 nodeRange.selectNodeContents(node);
22429 range.collapse(true);
22431 nodeRange.collapse(true);
22433 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22434 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22436 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22438 var nodeIsBefore = ss == 1;
22439 var nodeIsAfter = ee == -1;
22441 if (nodeIsBefore && nodeIsAfter) {
22444 if (!nodeIsBefore && nodeIsAfter) {
22445 return 1; //right trailed.
22448 if (nodeIsBefore && !nodeIsAfter) {
22449 return 2; // left trailed.
22455 // private? - in a new class?
22456 cleanUpPaste : function()
22458 // cleans up the whole document..
22459 Roo.log('cleanuppaste');
22461 this.cleanUpChildren(this.doc.body);
22462 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22463 if (clean != this.doc.body.innerHTML) {
22464 this.doc.body.innerHTML = clean;
22469 cleanWordChars : function(input) {// change the chars to hex code
22470 var he = Roo.HtmlEditorCore;
22472 var output = input;
22473 Roo.each(he.swapCodes, function(sw) {
22474 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22476 output = output.replace(swapper, sw[1]);
22483 cleanUpChildren : function (n)
22485 if (!n.childNodes.length) {
22488 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22489 this.cleanUpChild(n.childNodes[i]);
22496 cleanUpChild : function (node)
22499 //console.log(node);
22500 if (node.nodeName == "#text") {
22501 // clean up silly Windows -- stuff?
22504 if (node.nodeName == "#comment") {
22505 node.parentNode.removeChild(node);
22506 // clean up silly Windows -- stuff?
22509 var lcname = node.tagName.toLowerCase();
22510 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22511 // whitelist of tags..
22513 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22515 node.parentNode.removeChild(node);
22520 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22522 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22523 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22525 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22526 // remove_keep_children = true;
22529 if (remove_keep_children) {
22530 this.cleanUpChildren(node);
22531 // inserts everything just before this node...
22532 while (node.childNodes.length) {
22533 var cn = node.childNodes[0];
22534 node.removeChild(cn);
22535 node.parentNode.insertBefore(cn, node);
22537 node.parentNode.removeChild(node);
22541 if (!node.attributes || !node.attributes.length) {
22542 this.cleanUpChildren(node);
22546 function cleanAttr(n,v)
22549 if (v.match(/^\./) || v.match(/^\//)) {
22552 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22555 if (v.match(/^#/)) {
22558 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22559 node.removeAttribute(n);
22563 var cwhite = this.cwhite;
22564 var cblack = this.cblack;
22566 function cleanStyle(n,v)
22568 if (v.match(/expression/)) { //XSS?? should we even bother..
22569 node.removeAttribute(n);
22573 var parts = v.split(/;/);
22576 Roo.each(parts, function(p) {
22577 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22581 var l = p.split(':').shift().replace(/\s+/g,'');
22582 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22584 if ( cwhite.length && cblack.indexOf(l) > -1) {
22585 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22586 //node.removeAttribute(n);
22590 // only allow 'c whitelisted system attributes'
22591 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22592 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22593 //node.removeAttribute(n);
22603 if (clean.length) {
22604 node.setAttribute(n, clean.join(';'));
22606 node.removeAttribute(n);
22612 for (var i = node.attributes.length-1; i > -1 ; i--) {
22613 var a = node.attributes[i];
22616 if (a.name.toLowerCase().substr(0,2)=='on') {
22617 node.removeAttribute(a.name);
22620 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22621 node.removeAttribute(a.name);
22624 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22625 cleanAttr(a.name,a.value); // fixme..
22628 if (a.name == 'style') {
22629 cleanStyle(a.name,a.value);
22632 /// clean up MS crap..
22633 // tecnically this should be a list of valid class'es..
22636 if (a.name == 'class') {
22637 if (a.value.match(/^Mso/)) {
22638 node.className = '';
22641 if (a.value.match(/^body$/)) {
22642 node.className = '';
22653 this.cleanUpChildren(node);
22659 * Clean up MS wordisms...
22661 cleanWord : function(node)
22666 this.cleanWord(this.doc.body);
22669 if (node.nodeName == "#text") {
22670 // clean up silly Windows -- stuff?
22673 if (node.nodeName == "#comment") {
22674 node.parentNode.removeChild(node);
22675 // clean up silly Windows -- stuff?
22679 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22680 node.parentNode.removeChild(node);
22684 // remove - but keep children..
22685 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22686 while (node.childNodes.length) {
22687 var cn = node.childNodes[0];
22688 node.removeChild(cn);
22689 node.parentNode.insertBefore(cn, node);
22691 node.parentNode.removeChild(node);
22692 this.iterateChildren(node, this.cleanWord);
22696 if (node.className.length) {
22698 var cn = node.className.split(/\W+/);
22700 Roo.each(cn, function(cls) {
22701 if (cls.match(/Mso[a-zA-Z]+/)) {
22706 node.className = cna.length ? cna.join(' ') : '';
22708 node.removeAttribute("class");
22712 if (node.hasAttribute("lang")) {
22713 node.removeAttribute("lang");
22716 if (node.hasAttribute("style")) {
22718 var styles = node.getAttribute("style").split(";");
22720 Roo.each(styles, function(s) {
22721 if (!s.match(/:/)) {
22724 var kv = s.split(":");
22725 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22728 // what ever is left... we allow.
22731 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22732 if (!nstyle.length) {
22733 node.removeAttribute('style');
22736 this.iterateChildren(node, this.cleanWord);
22742 * iterateChildren of a Node, calling fn each time, using this as the scole..
22743 * @param {DomNode} node node to iterate children of.
22744 * @param {Function} fn method of this class to call on each item.
22746 iterateChildren : function(node, fn)
22748 if (!node.childNodes.length) {
22751 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22752 fn.call(this, node.childNodes[i])
22758 * cleanTableWidths.
22760 * Quite often pasting from word etc.. results in tables with column and widths.
22761 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22764 cleanTableWidths : function(node)
22769 this.cleanTableWidths(this.doc.body);
22774 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22777 Roo.log(node.tagName);
22778 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22779 this.iterateChildren(node, this.cleanTableWidths);
22782 if (node.hasAttribute('width')) {
22783 node.removeAttribute('width');
22787 if (node.hasAttribute("style")) {
22790 var styles = node.getAttribute("style").split(";");
22792 Roo.each(styles, function(s) {
22793 if (!s.match(/:/)) {
22796 var kv = s.split(":");
22797 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22800 // what ever is left... we allow.
22803 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22804 if (!nstyle.length) {
22805 node.removeAttribute('style');
22809 this.iterateChildren(node, this.cleanTableWidths);
22817 domToHTML : function(currentElement, depth, nopadtext) {
22819 depth = depth || 0;
22820 nopadtext = nopadtext || false;
22822 if (!currentElement) {
22823 return this.domToHTML(this.doc.body);
22826 //Roo.log(currentElement);
22828 var allText = false;
22829 var nodeName = currentElement.nodeName;
22830 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22832 if (nodeName == '#text') {
22834 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22839 if (nodeName != 'BODY') {
22842 // Prints the node tagName, such as <A>, <IMG>, etc
22845 for(i = 0; i < currentElement.attributes.length;i++) {
22847 var aname = currentElement.attributes.item(i).name;
22848 if (!currentElement.attributes.item(i).value.length) {
22851 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22854 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22863 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22866 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22871 // Traverse the tree
22873 var currentElementChild = currentElement.childNodes.item(i);
22874 var allText = true;
22875 var innerHTML = '';
22877 while (currentElementChild) {
22878 // Formatting code (indent the tree so it looks nice on the screen)
22879 var nopad = nopadtext;
22880 if (lastnode == 'SPAN') {
22884 if (currentElementChild.nodeName == '#text') {
22885 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22886 toadd = nopadtext ? toadd : toadd.trim();
22887 if (!nopad && toadd.length > 80) {
22888 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22890 innerHTML += toadd;
22893 currentElementChild = currentElement.childNodes.item(i);
22899 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22901 // Recursively traverse the tree structure of the child node
22902 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22903 lastnode = currentElementChild.nodeName;
22905 currentElementChild=currentElement.childNodes.item(i);
22911 // The remaining code is mostly for formatting the tree
22912 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22917 ret+= "</"+tagName+">";
22923 applyBlacklists : function()
22925 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22926 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22930 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22931 if (b.indexOf(tag) > -1) {
22934 this.white.push(tag);
22938 Roo.each(w, function(tag) {
22939 if (b.indexOf(tag) > -1) {
22942 if (this.white.indexOf(tag) > -1) {
22945 this.white.push(tag);
22950 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22951 if (w.indexOf(tag) > -1) {
22954 this.black.push(tag);
22958 Roo.each(b, function(tag) {
22959 if (w.indexOf(tag) > -1) {
22962 if (this.black.indexOf(tag) > -1) {
22965 this.black.push(tag);
22970 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22971 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22975 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22976 if (b.indexOf(tag) > -1) {
22979 this.cwhite.push(tag);
22983 Roo.each(w, function(tag) {
22984 if (b.indexOf(tag) > -1) {
22987 if (this.cwhite.indexOf(tag) > -1) {
22990 this.cwhite.push(tag);
22995 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22996 if (w.indexOf(tag) > -1) {
22999 this.cblack.push(tag);
23003 Roo.each(b, function(tag) {
23004 if (w.indexOf(tag) > -1) {
23007 if (this.cblack.indexOf(tag) > -1) {
23010 this.cblack.push(tag);
23015 setStylesheets : function(stylesheets)
23017 if(typeof(stylesheets) == 'string'){
23018 Roo.get(this.iframe.contentDocument.head).createChild({
23020 rel : 'stylesheet',
23029 Roo.each(stylesheets, function(s) {
23034 Roo.get(_this.iframe.contentDocument.head).createChild({
23036 rel : 'stylesheet',
23045 removeStylesheets : function()
23049 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23054 setStyle : function(style)
23056 Roo.get(this.iframe.contentDocument.head).createChild({
23065 // hide stuff that is not compatible
23079 * @event specialkey
23083 * @cfg {String} fieldClass @hide
23086 * @cfg {String} focusClass @hide
23089 * @cfg {String} autoCreate @hide
23092 * @cfg {String} inputType @hide
23095 * @cfg {String} invalidClass @hide
23098 * @cfg {String} invalidText @hide
23101 * @cfg {String} msgFx @hide
23104 * @cfg {String} validateOnBlur @hide
23108 Roo.HtmlEditorCore.white = [
23109 'area', 'br', 'img', 'input', 'hr', 'wbr',
23111 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23112 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23113 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23114 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23115 'table', 'ul', 'xmp',
23117 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23120 'dir', 'menu', 'ol', 'ul', 'dl',
23126 Roo.HtmlEditorCore.black = [
23127 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23129 'base', 'basefont', 'bgsound', 'blink', 'body',
23130 'frame', 'frameset', 'head', 'html', 'ilayer',
23131 'iframe', 'layer', 'link', 'meta', 'object',
23132 'script', 'style' ,'title', 'xml' // clean later..
23134 Roo.HtmlEditorCore.clean = [
23135 'script', 'style', 'title', 'xml'
23137 Roo.HtmlEditorCore.remove = [
23142 Roo.HtmlEditorCore.ablack = [
23146 Roo.HtmlEditorCore.aclean = [
23147 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23151 Roo.HtmlEditorCore.pwhite= [
23152 'http', 'https', 'mailto'
23155 // white listed style attributes.
23156 Roo.HtmlEditorCore.cwhite= [
23157 // 'text-align', /// default is to allow most things..
23163 // black listed style attributes.
23164 Roo.HtmlEditorCore.cblack= [
23165 // 'font-size' -- this can be set by the project
23169 Roo.HtmlEditorCore.swapCodes =[
23188 * @class Roo.bootstrap.HtmlEditor
23189 * @extends Roo.bootstrap.TextArea
23190 * Bootstrap HtmlEditor class
23193 * Create a new HtmlEditor
23194 * @param {Object} config The config object
23197 Roo.bootstrap.HtmlEditor = function(config){
23198 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23199 if (!this.toolbars) {
23200 this.toolbars = [];
23203 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23206 * @event initialize
23207 * Fires when the editor is fully initialized (including the iframe)
23208 * @param {HtmlEditor} this
23213 * Fires when the editor is first receives the focus. Any insertion must wait
23214 * until after this event.
23215 * @param {HtmlEditor} this
23219 * @event beforesync
23220 * Fires before the textarea is updated with content from the editor iframe. Return false
23221 * to cancel the sync.
23222 * @param {HtmlEditor} this
23223 * @param {String} html
23227 * @event beforepush
23228 * Fires before the iframe editor is updated with content from the textarea. Return false
23229 * to cancel the push.
23230 * @param {HtmlEditor} this
23231 * @param {String} html
23236 * Fires when the textarea is updated with content from the editor iframe.
23237 * @param {HtmlEditor} this
23238 * @param {String} html
23243 * Fires when the iframe editor is updated with content from the textarea.
23244 * @param {HtmlEditor} this
23245 * @param {String} html
23249 * @event editmodechange
23250 * Fires when the editor switches edit modes
23251 * @param {HtmlEditor} this
23252 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23254 editmodechange: true,
23256 * @event editorevent
23257 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23258 * @param {HtmlEditor} this
23262 * @event firstfocus
23263 * Fires when on first focus - needed by toolbars..
23264 * @param {HtmlEditor} this
23269 * Auto save the htmlEditor value as a file into Events
23270 * @param {HtmlEditor} this
23274 * @event savedpreview
23275 * preview the saved version of htmlEditor
23276 * @param {HtmlEditor} this
23283 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23287 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23292 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23297 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23302 * @cfg {Number} height (in pixels)
23306 * @cfg {Number} width (in pixels)
23311 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23314 stylesheets: false,
23319 // private properties
23320 validationEvent : false,
23322 initialized : false,
23325 onFocus : Roo.emptyFn,
23327 hideMode:'offsets',
23329 tbContainer : false,
23333 toolbarContainer :function() {
23334 return this.wrap.select('.x-html-editor-tb',true).first();
23338 * Protected method that will not generally be called directly. It
23339 * is called when the editor creates its toolbar. Override this method if you need to
23340 * add custom toolbar buttons.
23341 * @param {HtmlEditor} editor
23343 createToolbar : function(){
23344 Roo.log('renewing');
23345 Roo.log("create toolbars");
23347 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23348 this.toolbars[0].render(this.toolbarContainer());
23352 // if (!editor.toolbars || !editor.toolbars.length) {
23353 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23356 // for (var i =0 ; i < editor.toolbars.length;i++) {
23357 // editor.toolbars[i] = Roo.factory(
23358 // typeof(editor.toolbars[i]) == 'string' ?
23359 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23360 // Roo.bootstrap.HtmlEditor);
23361 // editor.toolbars[i].init(editor);
23367 onRender : function(ct, position)
23369 // Roo.log("Call onRender: " + this.xtype);
23371 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23373 this.wrap = this.inputEl().wrap({
23374 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23377 this.editorcore.onRender(ct, position);
23379 if (this.resizable) {
23380 this.resizeEl = new Roo.Resizable(this.wrap, {
23384 minHeight : this.height,
23385 height: this.height,
23386 handles : this.resizable,
23389 resize : function(r, w, h) {
23390 _t.onResize(w,h); // -something
23396 this.createToolbar(this);
23399 if(!this.width && this.resizable){
23400 this.setSize(this.wrap.getSize());
23402 if (this.resizeEl) {
23403 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23404 // should trigger onReize..
23410 onResize : function(w, h)
23412 Roo.log('resize: ' +w + ',' + h );
23413 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23417 if(this.inputEl() ){
23418 if(typeof w == 'number'){
23419 var aw = w - this.wrap.getFrameWidth('lr');
23420 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23423 if(typeof h == 'number'){
23424 var tbh = -11; // fixme it needs to tool bar size!
23425 for (var i =0; i < this.toolbars.length;i++) {
23426 // fixme - ask toolbars for heights?
23427 tbh += this.toolbars[i].el.getHeight();
23428 //if (this.toolbars[i].footer) {
23429 // tbh += this.toolbars[i].footer.el.getHeight();
23437 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23438 ah -= 5; // knock a few pixes off for look..
23439 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23443 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23444 this.editorcore.onResize(ew,eh);
23449 * Toggles the editor between standard and source edit mode.
23450 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23452 toggleSourceEdit : function(sourceEditMode)
23454 this.editorcore.toggleSourceEdit(sourceEditMode);
23456 if(this.editorcore.sourceEditMode){
23457 Roo.log('editor - showing textarea');
23460 // Roo.log(this.syncValue());
23462 this.inputEl().removeClass(['hide', 'x-hidden']);
23463 this.inputEl().dom.removeAttribute('tabIndex');
23464 this.inputEl().focus();
23466 Roo.log('editor - hiding textarea');
23468 // Roo.log(this.pushValue());
23471 this.inputEl().addClass(['hide', 'x-hidden']);
23472 this.inputEl().dom.setAttribute('tabIndex', -1);
23473 //this.deferFocus();
23476 if(this.resizable){
23477 this.setSize(this.wrap.getSize());
23480 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23483 // private (for BoxComponent)
23484 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23486 // private (for BoxComponent)
23487 getResizeEl : function(){
23491 // private (for BoxComponent)
23492 getPositionEl : function(){
23497 initEvents : function(){
23498 this.originalValue = this.getValue();
23502 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23505 // markInvalid : Roo.emptyFn,
23507 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23510 // clearInvalid : Roo.emptyFn,
23512 setValue : function(v){
23513 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23514 this.editorcore.pushValue();
23519 deferFocus : function(){
23520 this.focus.defer(10, this);
23524 focus : function(){
23525 this.editorcore.focus();
23531 onDestroy : function(){
23537 for (var i =0; i < this.toolbars.length;i++) {
23538 // fixme - ask toolbars for heights?
23539 this.toolbars[i].onDestroy();
23542 this.wrap.dom.innerHTML = '';
23543 this.wrap.remove();
23548 onFirstFocus : function(){
23549 //Roo.log("onFirstFocus");
23550 this.editorcore.onFirstFocus();
23551 for (var i =0; i < this.toolbars.length;i++) {
23552 this.toolbars[i].onFirstFocus();
23558 syncValue : function()
23560 this.editorcore.syncValue();
23563 pushValue : function()
23565 this.editorcore.pushValue();
23569 // hide stuff that is not compatible
23583 * @event specialkey
23587 * @cfg {String} fieldClass @hide
23590 * @cfg {String} focusClass @hide
23593 * @cfg {String} autoCreate @hide
23596 * @cfg {String} inputType @hide
23599 * @cfg {String} invalidClass @hide
23602 * @cfg {String} invalidText @hide
23605 * @cfg {String} msgFx @hide
23608 * @cfg {String} validateOnBlur @hide
23617 Roo.namespace('Roo.bootstrap.htmleditor');
23619 * @class Roo.bootstrap.HtmlEditorToolbar1
23624 new Roo.bootstrap.HtmlEditor({
23627 new Roo.bootstrap.HtmlEditorToolbar1({
23628 disable : { fonts: 1 , format: 1, ..., ... , ...],
23634 * @cfg {Object} disable List of elements to disable..
23635 * @cfg {Array} btns List of additional buttons.
23639 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23642 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23645 Roo.apply(this, config);
23647 // default disabled, based on 'good practice'..
23648 this.disable = this.disable || {};
23649 Roo.applyIf(this.disable, {
23652 specialElements : true
23654 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23656 this.editor = config.editor;
23657 this.editorcore = config.editor.editorcore;
23659 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23661 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23662 // dont call parent... till later.
23664 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23669 editorcore : false,
23674 "h1","h2","h3","h4","h5","h6",
23676 "abbr", "acronym", "address", "cite", "samp", "var",
23680 onRender : function(ct, position)
23682 // Roo.log("Call onRender: " + this.xtype);
23684 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23686 this.el.dom.style.marginBottom = '0';
23688 var editorcore = this.editorcore;
23689 var editor= this.editor;
23692 var btn = function(id,cmd , toggle, handler, html){
23694 var event = toggle ? 'toggle' : 'click';
23699 xns: Roo.bootstrap,
23702 enableToggle:toggle !== false,
23704 pressed : toggle ? false : null,
23707 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23708 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23714 // var cb_box = function...
23719 xns: Roo.bootstrap,
23720 glyphicon : 'font',
23724 xns: Roo.bootstrap,
23728 Roo.each(this.formats, function(f) {
23729 style.menu.items.push({
23731 xns: Roo.bootstrap,
23732 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23737 editorcore.insertTag(this.tagname);
23744 children.push(style);
23746 btn('bold',false,true);
23747 btn('italic',false,true);
23748 btn('align-left', 'justifyleft',true);
23749 btn('align-center', 'justifycenter',true);
23750 btn('align-right' , 'justifyright',true);
23751 btn('link', false, false, function(btn) {
23752 //Roo.log("create link?");
23753 var url = prompt(this.createLinkText, this.defaultLinkValue);
23754 if(url && url != 'http:/'+'/'){
23755 this.editorcore.relayCmd('createlink', url);
23758 btn('list','insertunorderedlist',true);
23759 btn('pencil', false,true, function(btn){
23761 this.toggleSourceEdit(btn.pressed);
23764 if (this.editor.btns.length > 0) {
23765 for (var i = 0; i<this.editor.btns.length; i++) {
23766 children.push(this.editor.btns[i]);
23774 xns: Roo.bootstrap,
23779 xns: Roo.bootstrap,
23784 cog.menu.items.push({
23786 xns: Roo.bootstrap,
23787 html : Clean styles,
23792 editorcore.insertTag(this.tagname);
23801 this.xtype = 'NavSimplebar';
23803 for(var i=0;i< children.length;i++) {
23805 this.buttons.add(this.addxtypeChild(children[i]));
23809 editor.on('editorevent', this.updateToolbar, this);
23811 onBtnClick : function(id)
23813 this.editorcore.relayCmd(id);
23814 this.editorcore.focus();
23818 * Protected method that will not generally be called directly. It triggers
23819 * a toolbar update by reading the markup state of the current selection in the editor.
23821 updateToolbar: function(){
23823 if(!this.editorcore.activated){
23824 this.editor.onFirstFocus(); // is this neeed?
23828 var btns = this.buttons;
23829 var doc = this.editorcore.doc;
23830 btns.get('bold').setActive(doc.queryCommandState('bold'));
23831 btns.get('italic').setActive(doc.queryCommandState('italic'));
23832 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23834 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23835 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23836 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23838 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23839 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23842 var ans = this.editorcore.getAllAncestors();
23843 if (this.formatCombo) {
23846 var store = this.formatCombo.store;
23847 this.formatCombo.setValue("");
23848 for (var i =0; i < ans.length;i++) {
23849 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23851 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23859 // hides menus... - so this cant be on a menu...
23860 Roo.bootstrap.MenuMgr.hideAll();
23862 Roo.bootstrap.MenuMgr.hideAll();
23863 //this.editorsyncValue();
23865 onFirstFocus: function() {
23866 this.buttons.each(function(item){
23870 toggleSourceEdit : function(sourceEditMode){
23873 if(sourceEditMode){
23874 Roo.log("disabling buttons");
23875 this.buttons.each( function(item){
23876 if(item.cmd != 'pencil'){
23882 Roo.log("enabling buttons");
23883 if(this.editorcore.initialized){
23884 this.buttons.each( function(item){
23890 Roo.log("calling toggole on editor");
23891 // tell the editor that it's been pressed..
23892 this.editor.toggleSourceEdit(sourceEditMode);
23902 * @class Roo.bootstrap.Table.AbstractSelectionModel
23903 * @extends Roo.util.Observable
23904 * Abstract base class for grid SelectionModels. It provides the interface that should be
23905 * implemented by descendant classes. This class should not be directly instantiated.
23908 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23909 this.locked = false;
23910 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23914 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23915 /** @ignore Called by the grid automatically. Do not call directly. */
23916 init : function(grid){
23922 * Locks the selections.
23925 this.locked = true;
23929 * Unlocks the selections.
23931 unlock : function(){
23932 this.locked = false;
23936 * Returns true if the selections are locked.
23937 * @return {Boolean}
23939 isLocked : function(){
23940 return this.locked;
23944 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23945 * @class Roo.bootstrap.Table.RowSelectionModel
23946 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23947 * It supports multiple selections and keyboard selection/navigation.
23949 * @param {Object} config
23952 Roo.bootstrap.Table.RowSelectionModel = function(config){
23953 Roo.apply(this, config);
23954 this.selections = new Roo.util.MixedCollection(false, function(o){
23959 this.lastActive = false;
23963 * @event selectionchange
23964 * Fires when the selection changes
23965 * @param {SelectionModel} this
23967 "selectionchange" : true,
23969 * @event afterselectionchange
23970 * Fires after the selection changes (eg. by key press or clicking)
23971 * @param {SelectionModel} this
23973 "afterselectionchange" : true,
23975 * @event beforerowselect
23976 * Fires when a row is selected being selected, return false to cancel.
23977 * @param {SelectionModel} this
23978 * @param {Number} rowIndex The selected index
23979 * @param {Boolean} keepExisting False if other selections will be cleared
23981 "beforerowselect" : true,
23984 * Fires when a row is selected.
23985 * @param {SelectionModel} this
23986 * @param {Number} rowIndex The selected index
23987 * @param {Roo.data.Record} r The record
23989 "rowselect" : true,
23991 * @event rowdeselect
23992 * Fires when a row is deselected.
23993 * @param {SelectionModel} this
23994 * @param {Number} rowIndex The selected index
23996 "rowdeselect" : true
23998 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23999 this.locked = false;
24002 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24004 * @cfg {Boolean} singleSelect
24005 * True to allow selection of only one row at a time (defaults to false)
24007 singleSelect : false,
24010 initEvents : function()
24013 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24014 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24015 //}else{ // allow click to work like normal
24016 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24018 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24019 this.grid.on("rowclick", this.handleMouseDown, this);
24021 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24022 "up" : function(e){
24024 this.selectPrevious(e.shiftKey);
24025 }else if(this.last !== false && this.lastActive !== false){
24026 var last = this.last;
24027 this.selectRange(this.last, this.lastActive-1);
24028 this.grid.getView().focusRow(this.lastActive);
24029 if(last !== false){
24033 this.selectFirstRow();
24035 this.fireEvent("afterselectionchange", this);
24037 "down" : function(e){
24039 this.selectNext(e.shiftKey);
24040 }else if(this.last !== false && this.lastActive !== false){
24041 var last = this.last;
24042 this.selectRange(this.last, this.lastActive+1);
24043 this.grid.getView().focusRow(this.lastActive);
24044 if(last !== false){
24048 this.selectFirstRow();
24050 this.fireEvent("afterselectionchange", this);
24054 this.grid.store.on('load', function(){
24055 this.selections.clear();
24058 var view = this.grid.view;
24059 view.on("refresh", this.onRefresh, this);
24060 view.on("rowupdated", this.onRowUpdated, this);
24061 view.on("rowremoved", this.onRemove, this);
24066 onRefresh : function()
24068 var ds = this.grid.store, i, v = this.grid.view;
24069 var s = this.selections;
24070 s.each(function(r){
24071 if((i = ds.indexOfId(r.id)) != -1){
24080 onRemove : function(v, index, r){
24081 this.selections.remove(r);
24085 onRowUpdated : function(v, index, r){
24086 if(this.isSelected(r)){
24087 v.onRowSelect(index);
24093 * @param {Array} records The records to select
24094 * @param {Boolean} keepExisting (optional) True to keep existing selections
24096 selectRecords : function(records, keepExisting)
24099 this.clearSelections();
24101 var ds = this.grid.store;
24102 for(var i = 0, len = records.length; i < len; i++){
24103 this.selectRow(ds.indexOf(records[i]), true);
24108 * Gets the number of selected rows.
24111 getCount : function(){
24112 return this.selections.length;
24116 * Selects the first row in the grid.
24118 selectFirstRow : function(){
24123 * Select the last row.
24124 * @param {Boolean} keepExisting (optional) True to keep existing selections
24126 selectLastRow : function(keepExisting){
24127 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24128 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24132 * Selects the row immediately following the last selected row.
24133 * @param {Boolean} keepExisting (optional) True to keep existing selections
24135 selectNext : function(keepExisting)
24137 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24138 this.selectRow(this.last+1, keepExisting);
24139 this.grid.getView().focusRow(this.last);
24144 * Selects the row that precedes the last selected row.
24145 * @param {Boolean} keepExisting (optional) True to keep existing selections
24147 selectPrevious : function(keepExisting){
24149 this.selectRow(this.last-1, keepExisting);
24150 this.grid.getView().focusRow(this.last);
24155 * Returns the selected records
24156 * @return {Array} Array of selected records
24158 getSelections : function(){
24159 return [].concat(this.selections.items);
24163 * Returns the first selected record.
24166 getSelected : function(){
24167 return this.selections.itemAt(0);
24172 * Clears all selections.
24174 clearSelections : function(fast)
24180 var ds = this.grid.store;
24181 var s = this.selections;
24182 s.each(function(r){
24183 this.deselectRow(ds.indexOfId(r.id));
24187 this.selections.clear();
24194 * Selects all rows.
24196 selectAll : function(){
24200 this.selections.clear();
24201 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24202 this.selectRow(i, true);
24207 * Returns True if there is a selection.
24208 * @return {Boolean}
24210 hasSelection : function(){
24211 return this.selections.length > 0;
24215 * Returns True if the specified row is selected.
24216 * @param {Number/Record} record The record or index of the record to check
24217 * @return {Boolean}
24219 isSelected : function(index){
24220 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24221 return (r && this.selections.key(r.id) ? true : false);
24225 * Returns True if the specified record id is selected.
24226 * @param {String} id The id of record to check
24227 * @return {Boolean}
24229 isIdSelected : function(id){
24230 return (this.selections.key(id) ? true : false);
24235 handleMouseDBClick : function(e, t){
24239 handleMouseDown : function(e, t)
24241 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24242 if(this.isLocked() || rowIndex < 0 ){
24245 if(e.shiftKey && this.last !== false){
24246 var last = this.last;
24247 this.selectRange(last, rowIndex, e.ctrlKey);
24248 this.last = last; // reset the last
24252 var isSelected = this.isSelected(rowIndex);
24253 //Roo.log("select row:" + rowIndex);
24255 this.deselectRow(rowIndex);
24257 this.selectRow(rowIndex, true);
24261 if(e.button !== 0 && isSelected){
24262 alert('rowIndex 2: ' + rowIndex);
24263 view.focusRow(rowIndex);
24264 }else if(e.ctrlKey && isSelected){
24265 this.deselectRow(rowIndex);
24266 }else if(!isSelected){
24267 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24268 view.focusRow(rowIndex);
24272 this.fireEvent("afterselectionchange", this);
24275 handleDragableRowClick : function(grid, rowIndex, e)
24277 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24278 this.selectRow(rowIndex, false);
24279 grid.view.focusRow(rowIndex);
24280 this.fireEvent("afterselectionchange", this);
24285 * Selects multiple rows.
24286 * @param {Array} rows Array of the indexes of the row to select
24287 * @param {Boolean} keepExisting (optional) True to keep existing selections
24289 selectRows : function(rows, keepExisting){
24291 this.clearSelections();
24293 for(var i = 0, len = rows.length; i < len; i++){
24294 this.selectRow(rows[i], true);
24299 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24300 * @param {Number} startRow The index of the first row in the range
24301 * @param {Number} endRow The index of the last row in the range
24302 * @param {Boolean} keepExisting (optional) True to retain existing selections
24304 selectRange : function(startRow, endRow, keepExisting){
24309 this.clearSelections();
24311 if(startRow <= endRow){
24312 for(var i = startRow; i <= endRow; i++){
24313 this.selectRow(i, true);
24316 for(var i = startRow; i >= endRow; i--){
24317 this.selectRow(i, true);
24323 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24324 * @param {Number} startRow The index of the first row in the range
24325 * @param {Number} endRow The index of the last row in the range
24327 deselectRange : function(startRow, endRow, preventViewNotify){
24331 for(var i = startRow; i <= endRow; i++){
24332 this.deselectRow(i, preventViewNotify);
24338 * @param {Number} row The index of the row to select
24339 * @param {Boolean} keepExisting (optional) True to keep existing selections
24341 selectRow : function(index, keepExisting, preventViewNotify)
24343 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24346 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24347 if(!keepExisting || this.singleSelect){
24348 this.clearSelections();
24351 var r = this.grid.store.getAt(index);
24352 //console.log('selectRow - record id :' + r.id);
24354 this.selections.add(r);
24355 this.last = this.lastActive = index;
24356 if(!preventViewNotify){
24357 var proxy = new Roo.Element(
24358 this.grid.getRowDom(index)
24360 proxy.addClass('bg-info info');
24362 this.fireEvent("rowselect", this, index, r);
24363 this.fireEvent("selectionchange", this);
24369 * @param {Number} row The index of the row to deselect
24371 deselectRow : function(index, preventViewNotify)
24376 if(this.last == index){
24379 if(this.lastActive == index){
24380 this.lastActive = false;
24383 var r = this.grid.store.getAt(index);
24388 this.selections.remove(r);
24389 //.console.log('deselectRow - record id :' + r.id);
24390 if(!preventViewNotify){
24392 var proxy = new Roo.Element(
24393 this.grid.getRowDom(index)
24395 proxy.removeClass('bg-info info');
24397 this.fireEvent("rowdeselect", this, index);
24398 this.fireEvent("selectionchange", this);
24402 restoreLast : function(){
24404 this.last = this._last;
24409 acceptsNav : function(row, col, cm){
24410 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24414 onEditorKey : function(field, e){
24415 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24420 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24422 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24424 }else if(k == e.ENTER && !e.ctrlKey){
24428 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24430 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24432 }else if(k == e.ESC){
24436 g.startEditing(newCell[0], newCell[1]);
24442 * Ext JS Library 1.1.1
24443 * Copyright(c) 2006-2007, Ext JS, LLC.
24445 * Originally Released Under LGPL - original licence link has changed is not relivant.
24448 * <script type="text/javascript">
24452 * @class Roo.bootstrap.PagingToolbar
24453 * @extends Roo.bootstrap.NavSimplebar
24454 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24456 * Create a new PagingToolbar
24457 * @param {Object} config The config object
24458 * @param {Roo.data.Store} store
24460 Roo.bootstrap.PagingToolbar = function(config)
24462 // old args format still supported... - xtype is prefered..
24463 // created from xtype...
24465 this.ds = config.dataSource;
24467 if (config.store && !this.ds) {
24468 this.store= Roo.factory(config.store, Roo.data);
24469 this.ds = this.store;
24470 this.ds.xmodule = this.xmodule || false;
24473 this.toolbarItems = [];
24474 if (config.items) {
24475 this.toolbarItems = config.items;
24478 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24483 this.bind(this.ds);
24486 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24490 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24492 * @cfg {Roo.data.Store} dataSource
24493 * The underlying data store providing the paged data
24496 * @cfg {String/HTMLElement/Element} container
24497 * container The id or element that will contain the toolbar
24500 * @cfg {Boolean} displayInfo
24501 * True to display the displayMsg (defaults to false)
24504 * @cfg {Number} pageSize
24505 * The number of records to display per page (defaults to 20)
24509 * @cfg {String} displayMsg
24510 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24512 displayMsg : 'Displaying {0} - {1} of {2}',
24514 * @cfg {String} emptyMsg
24515 * The message to display when no records are found (defaults to "No data to display")
24517 emptyMsg : 'No data to display',
24519 * Customizable piece of the default paging text (defaults to "Page")
24522 beforePageText : "Page",
24524 * Customizable piece of the default paging text (defaults to "of %0")
24527 afterPageText : "of {0}",
24529 * Customizable piece of the default paging text (defaults to "First Page")
24532 firstText : "First Page",
24534 * Customizable piece of the default paging text (defaults to "Previous Page")
24537 prevText : "Previous Page",
24539 * Customizable piece of the default paging text (defaults to "Next Page")
24542 nextText : "Next Page",
24544 * Customizable piece of the default paging text (defaults to "Last Page")
24547 lastText : "Last Page",
24549 * Customizable piece of the default paging text (defaults to "Refresh")
24552 refreshText : "Refresh",
24556 onRender : function(ct, position)
24558 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24559 this.navgroup.parentId = this.id;
24560 this.navgroup.onRender(this.el, null);
24561 // add the buttons to the navgroup
24563 if(this.displayInfo){
24564 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24565 this.displayEl = this.el.select('.x-paging-info', true).first();
24566 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24567 // this.displayEl = navel.el.select('span',true).first();
24573 Roo.each(_this.buttons, function(e){ // this might need to use render????
24574 Roo.factory(e).render(_this.el);
24578 Roo.each(_this.toolbarItems, function(e) {
24579 _this.navgroup.addItem(e);
24583 this.first = this.navgroup.addItem({
24584 tooltip: this.firstText,
24586 icon : 'fa fa-backward',
24588 preventDefault: true,
24589 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24592 this.prev = this.navgroup.addItem({
24593 tooltip: this.prevText,
24595 icon : 'fa fa-step-backward',
24597 preventDefault: true,
24598 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24600 //this.addSeparator();
24603 var field = this.navgroup.addItem( {
24605 cls : 'x-paging-position',
24607 html : this.beforePageText +
24608 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24609 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24612 this.field = field.el.select('input', true).first();
24613 this.field.on("keydown", this.onPagingKeydown, this);
24614 this.field.on("focus", function(){this.dom.select();});
24617 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24618 //this.field.setHeight(18);
24619 //this.addSeparator();
24620 this.next = this.navgroup.addItem({
24621 tooltip: this.nextText,
24623 html : ' <i class="fa fa-step-forward">',
24625 preventDefault: true,
24626 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24628 this.last = this.navgroup.addItem({
24629 tooltip: this.lastText,
24630 icon : 'fa fa-forward',
24633 preventDefault: true,
24634 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24636 //this.addSeparator();
24637 this.loading = this.navgroup.addItem({
24638 tooltip: this.refreshText,
24639 icon: 'fa fa-refresh',
24640 preventDefault: true,
24641 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24647 updateInfo : function(){
24648 if(this.displayEl){
24649 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24650 var msg = count == 0 ?
24654 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24656 this.displayEl.update(msg);
24661 onLoad : function(ds, r, o)
24663 this.cursor = o.params.start ? o.params.start : 0;
24665 var d = this.getPageData(),
24670 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24671 this.field.dom.value = ap;
24672 this.first.setDisabled(ap == 1);
24673 this.prev.setDisabled(ap == 1);
24674 this.next.setDisabled(ap == ps);
24675 this.last.setDisabled(ap == ps);
24676 this.loading.enable();
24681 getPageData : function(){
24682 var total = this.ds.getTotalCount();
24685 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24686 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24691 onLoadError : function(){
24692 this.loading.enable();
24696 onPagingKeydown : function(e){
24697 var k = e.getKey();
24698 var d = this.getPageData();
24700 var v = this.field.dom.value, pageNum;
24701 if(!v || isNaN(pageNum = parseInt(v, 10))){
24702 this.field.dom.value = d.activePage;
24705 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24706 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24709 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))
24711 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24712 this.field.dom.value = pageNum;
24713 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24716 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24718 var v = this.field.dom.value, pageNum;
24719 var increment = (e.shiftKey) ? 10 : 1;
24720 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24723 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24724 this.field.dom.value = d.activePage;
24727 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24729 this.field.dom.value = parseInt(v, 10) + increment;
24730 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24731 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24738 beforeLoad : function(){
24740 this.loading.disable();
24745 onClick : function(which){
24754 ds.load({params:{start: 0, limit: this.pageSize}});
24757 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24760 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24763 var total = ds.getTotalCount();
24764 var extra = total % this.pageSize;
24765 var lastStart = extra ? (total - extra) : total-this.pageSize;
24766 ds.load({params:{start: lastStart, limit: this.pageSize}});
24769 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24775 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24776 * @param {Roo.data.Store} store The data store to unbind
24778 unbind : function(ds){
24779 ds.un("beforeload", this.beforeLoad, this);
24780 ds.un("load", this.onLoad, this);
24781 ds.un("loadexception", this.onLoadError, this);
24782 ds.un("remove", this.updateInfo, this);
24783 ds.un("add", this.updateInfo, this);
24784 this.ds = undefined;
24788 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24789 * @param {Roo.data.Store} store The data store to bind
24791 bind : function(ds){
24792 ds.on("beforeload", this.beforeLoad, this);
24793 ds.on("load", this.onLoad, this);
24794 ds.on("loadexception", this.onLoadError, this);
24795 ds.on("remove", this.updateInfo, this);
24796 ds.on("add", this.updateInfo, this);
24807 * @class Roo.bootstrap.MessageBar
24808 * @extends Roo.bootstrap.Component
24809 * Bootstrap MessageBar class
24810 * @cfg {String} html contents of the MessageBar
24811 * @cfg {String} weight (info | success | warning | danger) default info
24812 * @cfg {String} beforeClass insert the bar before the given class
24813 * @cfg {Boolean} closable (true | false) default false
24814 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24817 * Create a new Element
24818 * @param {Object} config The config object
24821 Roo.bootstrap.MessageBar = function(config){
24822 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24825 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24831 beforeClass: 'bootstrap-sticky-wrap',
24833 getAutoCreate : function(){
24837 cls: 'alert alert-dismissable alert-' + this.weight,
24842 html: this.html || ''
24848 cfg.cls += ' alert-messages-fixed';
24862 onRender : function(ct, position)
24864 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24867 var cfg = Roo.apply({}, this.getAutoCreate());
24871 cfg.cls += ' ' + this.cls;
24874 cfg.style = this.style;
24876 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24878 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24881 this.el.select('>button.close').on('click', this.hide, this);
24887 if (!this.rendered) {
24893 this.fireEvent('show', this);
24899 if (!this.rendered) {
24905 this.fireEvent('hide', this);
24908 update : function()
24910 // var e = this.el.dom.firstChild;
24912 // if(this.closable){
24913 // e = e.nextSibling;
24916 // e.data = this.html || '';
24918 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24934 * @class Roo.bootstrap.Graph
24935 * @extends Roo.bootstrap.Component
24936 * Bootstrap Graph class
24940 @cfg {String} graphtype bar | vbar | pie
24941 @cfg {number} g_x coodinator | centre x (pie)
24942 @cfg {number} g_y coodinator | centre y (pie)
24943 @cfg {number} g_r radius (pie)
24944 @cfg {number} g_height height of the chart (respected by all elements in the set)
24945 @cfg {number} g_width width of the chart (respected by all elements in the set)
24946 @cfg {Object} title The title of the chart
24949 -opts (object) options for the chart
24951 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24952 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24954 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.
24955 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24957 o stretch (boolean)
24959 -opts (object) options for the pie
24962 o startAngle (number)
24963 o endAngle (number)
24967 * Create a new Input
24968 * @param {Object} config The config object
24971 Roo.bootstrap.Graph = function(config){
24972 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24978 * The img click event for the img.
24979 * @param {Roo.EventObject} e
24985 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24996 //g_colors: this.colors,
25003 getAutoCreate : function(){
25014 onRender : function(ct,position){
25017 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25019 if (typeof(Raphael) == 'undefined') {
25020 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25024 this.raphael = Raphael(this.el.dom);
25026 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25027 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25028 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25029 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25031 r.text(160, 10, "Single Series Chart").attr(txtattr);
25032 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25033 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25034 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25036 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25037 r.barchart(330, 10, 300, 220, data1);
25038 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25039 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25042 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25043 // r.barchart(30, 30, 560, 250, xdata, {
25044 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25045 // axis : "0 0 1 1",
25046 // axisxlabels : xdata
25047 // //yvalues : cols,
25050 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25052 // this.load(null,xdata,{
25053 // axis : "0 0 1 1",
25054 // axisxlabels : xdata
25059 load : function(graphtype,xdata,opts)
25061 this.raphael.clear();
25063 graphtype = this.graphtype;
25068 var r = this.raphael,
25069 fin = function () {
25070 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25072 fout = function () {
25073 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25075 pfin = function() {
25076 this.sector.stop();
25077 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25080 this.label[0].stop();
25081 this.label[0].attr({ r: 7.5 });
25082 this.label[1].attr({ "font-weight": 800 });
25085 pfout = function() {
25086 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25089 this.label[0].animate({ r: 5 }, 500, "bounce");
25090 this.label[1].attr({ "font-weight": 400 });
25096 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25099 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25102 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25103 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25105 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25112 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25117 setTitle: function(o)
25122 initEvents: function() {
25125 this.el.on('click', this.onClick, this);
25129 onClick : function(e)
25131 Roo.log('img onclick');
25132 this.fireEvent('click', this, e);
25144 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25147 * @class Roo.bootstrap.dash.NumberBox
25148 * @extends Roo.bootstrap.Component
25149 * Bootstrap NumberBox class
25150 * @cfg {String} headline Box headline
25151 * @cfg {String} content Box content
25152 * @cfg {String} icon Box icon
25153 * @cfg {String} footer Footer text
25154 * @cfg {String} fhref Footer href
25157 * Create a new NumberBox
25158 * @param {Object} config The config object
25162 Roo.bootstrap.dash.NumberBox = function(config){
25163 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25167 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25176 getAutoCreate : function(){
25180 cls : 'small-box ',
25188 cls : 'roo-headline',
25189 html : this.headline
25193 cls : 'roo-content',
25194 html : this.content
25208 cls : 'ion ' + this.icon
25217 cls : 'small-box-footer',
25218 href : this.fhref || '#',
25222 cfg.cn.push(footer);
25229 onRender : function(ct,position){
25230 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25237 setHeadline: function (value)
25239 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25242 setFooter: function (value, href)
25244 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25247 this.el.select('a.small-box-footer',true).first().attr('href', href);
25252 setContent: function (value)
25254 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25257 initEvents: function()
25271 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25274 * @class Roo.bootstrap.dash.TabBox
25275 * @extends Roo.bootstrap.Component
25276 * Bootstrap TabBox class
25277 * @cfg {String} title Title of the TabBox
25278 * @cfg {String} icon Icon of the TabBox
25279 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25280 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25283 * Create a new TabBox
25284 * @param {Object} config The config object
25288 Roo.bootstrap.dash.TabBox = function(config){
25289 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25294 * When a pane is added
25295 * @param {Roo.bootstrap.dash.TabPane} pane
25299 * @event activatepane
25300 * When a pane is activated
25301 * @param {Roo.bootstrap.dash.TabPane} pane
25303 "activatepane" : true
25311 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25316 tabScrollable : false,
25318 getChildContainer : function()
25320 return this.el.select('.tab-content', true).first();
25323 getAutoCreate : function(){
25327 cls: 'pull-left header',
25335 cls: 'fa ' + this.icon
25341 cls: 'nav nav-tabs pull-right',
25347 if(this.tabScrollable){
25354 cls: 'nav nav-tabs pull-right',
25365 cls: 'nav-tabs-custom',
25370 cls: 'tab-content no-padding',
25378 initEvents : function()
25380 //Roo.log('add add pane handler');
25381 this.on('addpane', this.onAddPane, this);
25384 * Updates the box title
25385 * @param {String} html to set the title to.
25387 setTitle : function(value)
25389 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25391 onAddPane : function(pane)
25393 this.panes.push(pane);
25394 //Roo.log('addpane');
25396 // tabs are rendere left to right..
25397 if(!this.showtabs){
25401 var ctr = this.el.select('.nav-tabs', true).first();
25404 var existing = ctr.select('.nav-tab',true);
25405 var qty = existing.getCount();;
25408 var tab = ctr.createChild({
25410 cls : 'nav-tab' + (qty ? '' : ' active'),
25418 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25421 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25423 pane.el.addClass('active');
25428 onTabClick : function(ev,un,ob,pane)
25430 //Roo.log('tab - prev default');
25431 ev.preventDefault();
25434 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25435 pane.tab.addClass('active');
25436 //Roo.log(pane.title);
25437 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25438 // technically we should have a deactivate event.. but maybe add later.
25439 // and it should not de-activate the selected tab...
25440 this.fireEvent('activatepane', pane);
25441 pane.el.addClass('active');
25442 pane.fireEvent('activate');
25447 getActivePane : function()
25450 Roo.each(this.panes, function(p) {
25451 if(p.el.hasClass('active')){
25472 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25474 * @class Roo.bootstrap.TabPane
25475 * @extends Roo.bootstrap.Component
25476 * Bootstrap TabPane class
25477 * @cfg {Boolean} active (false | true) Default false
25478 * @cfg {String} title title of panel
25482 * Create a new TabPane
25483 * @param {Object} config The config object
25486 Roo.bootstrap.dash.TabPane = function(config){
25487 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25493 * When a pane is activated
25494 * @param {Roo.bootstrap.dash.TabPane} pane
25501 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25506 // the tabBox that this is attached to.
25509 getAutoCreate : function()
25517 cfg.cls += ' active';
25522 initEvents : function()
25524 //Roo.log('trigger add pane handler');
25525 this.parent().fireEvent('addpane', this)
25529 * Updates the tab title
25530 * @param {String} html to set the title to.
25532 setTitle: function(str)
25538 this.tab.select('a', true).first().dom.innerHTML = str;
25555 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25558 * @class Roo.bootstrap.menu.Menu
25559 * @extends Roo.bootstrap.Component
25560 * Bootstrap Menu class - container for Menu
25561 * @cfg {String} html Text of the menu
25562 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25563 * @cfg {String} icon Font awesome icon
25564 * @cfg {String} pos Menu align to (top | bottom) default bottom
25568 * Create a new Menu
25569 * @param {Object} config The config object
25573 Roo.bootstrap.menu.Menu = function(config){
25574 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25578 * @event beforeshow
25579 * Fires before this menu is displayed
25580 * @param {Roo.bootstrap.menu.Menu} this
25584 * @event beforehide
25585 * Fires before this menu is hidden
25586 * @param {Roo.bootstrap.menu.Menu} this
25591 * Fires after this menu is displayed
25592 * @param {Roo.bootstrap.menu.Menu} this
25597 * Fires after this menu is hidden
25598 * @param {Roo.bootstrap.menu.Menu} this
25603 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25604 * @param {Roo.bootstrap.menu.Menu} this
25605 * @param {Roo.EventObject} e
25612 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25616 weight : 'default',
25621 getChildContainer : function() {
25622 if(this.isSubMenu){
25626 return this.el.select('ul.dropdown-menu', true).first();
25629 getAutoCreate : function()
25634 cls : 'roo-menu-text',
25642 cls : 'fa ' + this.icon
25653 cls : 'dropdown-button btn btn-' + this.weight,
25658 cls : 'dropdown-toggle btn btn-' + this.weight,
25668 cls : 'dropdown-menu'
25674 if(this.pos == 'top'){
25675 cfg.cls += ' dropup';
25678 if(this.isSubMenu){
25681 cls : 'dropdown-menu'
25688 onRender : function(ct, position)
25690 this.isSubMenu = ct.hasClass('dropdown-submenu');
25692 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25695 initEvents : function()
25697 if(this.isSubMenu){
25701 this.hidden = true;
25703 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25704 this.triggerEl.on('click', this.onTriggerPress, this);
25706 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25707 this.buttonEl.on('click', this.onClick, this);
25713 if(this.isSubMenu){
25717 return this.el.select('ul.dropdown-menu', true).first();
25720 onClick : function(e)
25722 this.fireEvent("click", this, e);
25725 onTriggerPress : function(e)
25727 if (this.isVisible()) {
25734 isVisible : function(){
25735 return !this.hidden;
25740 this.fireEvent("beforeshow", this);
25742 this.hidden = false;
25743 this.el.addClass('open');
25745 Roo.get(document).on("mouseup", this.onMouseUp, this);
25747 this.fireEvent("show", this);
25754 this.fireEvent("beforehide", this);
25756 this.hidden = true;
25757 this.el.removeClass('open');
25759 Roo.get(document).un("mouseup", this.onMouseUp);
25761 this.fireEvent("hide", this);
25764 onMouseUp : function()
25778 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25781 * @class Roo.bootstrap.menu.Item
25782 * @extends Roo.bootstrap.Component
25783 * Bootstrap MenuItem class
25784 * @cfg {Boolean} submenu (true | false) default false
25785 * @cfg {String} html text of the item
25786 * @cfg {String} href the link
25787 * @cfg {Boolean} disable (true | false) default false
25788 * @cfg {Boolean} preventDefault (true | false) default true
25789 * @cfg {String} icon Font awesome icon
25790 * @cfg {String} pos Submenu align to (left | right) default right
25794 * Create a new Item
25795 * @param {Object} config The config object
25799 Roo.bootstrap.menu.Item = function(config){
25800 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25804 * Fires when the mouse is hovering over this menu
25805 * @param {Roo.bootstrap.menu.Item} this
25806 * @param {Roo.EventObject} e
25811 * Fires when the mouse exits this menu
25812 * @param {Roo.bootstrap.menu.Item} this
25813 * @param {Roo.EventObject} e
25819 * The raw click event for the entire grid.
25820 * @param {Roo.EventObject} e
25826 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25831 preventDefault: true,
25836 getAutoCreate : function()
25841 cls : 'roo-menu-item-text',
25849 cls : 'fa ' + this.icon
25858 href : this.href || '#',
25865 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25869 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25871 if(this.pos == 'left'){
25872 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25879 initEvents : function()
25881 this.el.on('mouseover', this.onMouseOver, this);
25882 this.el.on('mouseout', this.onMouseOut, this);
25884 this.el.select('a', true).first().on('click', this.onClick, this);
25888 onClick : function(e)
25890 if(this.preventDefault){
25891 e.preventDefault();
25894 this.fireEvent("click", this, e);
25897 onMouseOver : function(e)
25899 if(this.submenu && this.pos == 'left'){
25900 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25903 this.fireEvent("mouseover", this, e);
25906 onMouseOut : function(e)
25908 this.fireEvent("mouseout", this, e);
25920 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25923 * @class Roo.bootstrap.menu.Separator
25924 * @extends Roo.bootstrap.Component
25925 * Bootstrap Separator class
25928 * Create a new Separator
25929 * @param {Object} config The config object
25933 Roo.bootstrap.menu.Separator = function(config){
25934 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25937 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25939 getAutoCreate : function(){
25960 * @class Roo.bootstrap.Tooltip
25961 * Bootstrap Tooltip class
25962 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25963 * to determine which dom element triggers the tooltip.
25965 * It needs to add support for additional attributes like tooltip-position
25968 * Create a new Toolti
25969 * @param {Object} config The config object
25972 Roo.bootstrap.Tooltip = function(config){
25973 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25975 this.alignment = Roo.bootstrap.Tooltip.alignment;
25977 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25978 this.alignment = config.alignment;
25983 Roo.apply(Roo.bootstrap.Tooltip, {
25985 * @function init initialize tooltip monitoring.
25989 currentTip : false,
25990 currentRegion : false,
25996 Roo.get(document).on('mouseover', this.enter ,this);
25997 Roo.get(document).on('mouseout', this.leave, this);
26000 this.currentTip = new Roo.bootstrap.Tooltip();
26003 enter : function(ev)
26005 var dom = ev.getTarget();
26007 //Roo.log(['enter',dom]);
26008 var el = Roo.fly(dom);
26009 if (this.currentEl) {
26011 //Roo.log(this.currentEl);
26012 //Roo.log(this.currentEl.contains(dom));
26013 if (this.currentEl == el) {
26016 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26022 if (this.currentTip.el) {
26023 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26027 if(!el || el.dom == document){
26033 // you can not look for children, as if el is the body.. then everythign is the child..
26034 if (!el.attr('tooltip')) { //
26035 if (!el.select("[tooltip]").elements.length) {
26038 // is the mouse over this child...?
26039 bindEl = el.select("[tooltip]").first();
26040 var xy = ev.getXY();
26041 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26042 //Roo.log("not in region.");
26045 //Roo.log("child element over..");
26048 this.currentEl = bindEl;
26049 this.currentTip.bind(bindEl);
26050 this.currentRegion = Roo.lib.Region.getRegion(dom);
26051 this.currentTip.enter();
26054 leave : function(ev)
26056 var dom = ev.getTarget();
26057 //Roo.log(['leave',dom]);
26058 if (!this.currentEl) {
26063 if (dom != this.currentEl.dom) {
26066 var xy = ev.getXY();
26067 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26070 // only activate leave if mouse cursor is outside... bounding box..
26075 if (this.currentTip) {
26076 this.currentTip.leave();
26078 //Roo.log('clear currentEl');
26079 this.currentEl = false;
26084 'left' : ['r-l', [-2,0], 'right'],
26085 'right' : ['l-r', [2,0], 'left'],
26086 'bottom' : ['t-b', [0,2], 'top'],
26087 'top' : [ 'b-t', [0,-2], 'bottom']
26093 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26098 delay : null, // can be { show : 300 , hide: 500}
26102 hoverState : null, //???
26104 placement : 'bottom',
26108 getAutoCreate : function(){
26115 cls : 'tooltip-arrow'
26118 cls : 'tooltip-inner'
26125 bind : function(el)
26131 enter : function () {
26133 if (this.timeout != null) {
26134 clearTimeout(this.timeout);
26137 this.hoverState = 'in';
26138 //Roo.log("enter - show");
26139 if (!this.delay || !this.delay.show) {
26144 this.timeout = setTimeout(function () {
26145 if (_t.hoverState == 'in') {
26148 }, this.delay.show);
26152 clearTimeout(this.timeout);
26154 this.hoverState = 'out';
26155 if (!this.delay || !this.delay.hide) {
26161 this.timeout = setTimeout(function () {
26162 //Roo.log("leave - timeout");
26164 if (_t.hoverState == 'out') {
26166 Roo.bootstrap.Tooltip.currentEl = false;
26171 show : function (msg)
26174 this.render(document.body);
26177 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26179 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26181 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26183 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26185 var placement = typeof this.placement == 'function' ?
26186 this.placement.call(this, this.el, on_el) :
26189 var autoToken = /\s?auto?\s?/i;
26190 var autoPlace = autoToken.test(placement);
26192 placement = placement.replace(autoToken, '') || 'top';
26196 //this.el.setXY([0,0]);
26198 //this.el.dom.style.display='block';
26200 //this.el.appendTo(on_el);
26202 var p = this.getPosition();
26203 var box = this.el.getBox();
26209 var align = this.alignment[placement];
26211 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26213 if(placement == 'top' || placement == 'bottom'){
26215 placement = 'right';
26218 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26219 placement = 'left';
26222 var scroll = Roo.select('body', true).first().getScroll();
26224 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26228 align = this.alignment[placement];
26231 this.el.alignTo(this.bindEl, align[0],align[1]);
26232 //var arrow = this.el.select('.arrow',true).first();
26233 //arrow.set(align[2],
26235 this.el.addClass(placement);
26237 this.el.addClass('in fade');
26239 this.hoverState = null;
26241 if (this.el.hasClass('fade')) {
26252 //this.el.setXY([0,0]);
26253 this.el.removeClass('in');
26269 * @class Roo.bootstrap.LocationPicker
26270 * @extends Roo.bootstrap.Component
26271 * Bootstrap LocationPicker class
26272 * @cfg {Number} latitude Position when init default 0
26273 * @cfg {Number} longitude Position when init default 0
26274 * @cfg {Number} zoom default 15
26275 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26276 * @cfg {Boolean} mapTypeControl default false
26277 * @cfg {Boolean} disableDoubleClickZoom default false
26278 * @cfg {Boolean} scrollwheel default true
26279 * @cfg {Boolean} streetViewControl default false
26280 * @cfg {Number} radius default 0
26281 * @cfg {String} locationName
26282 * @cfg {Boolean} draggable default true
26283 * @cfg {Boolean} enableAutocomplete default false
26284 * @cfg {Boolean} enableReverseGeocode default true
26285 * @cfg {String} markerTitle
26288 * Create a new LocationPicker
26289 * @param {Object} config The config object
26293 Roo.bootstrap.LocationPicker = function(config){
26295 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26300 * Fires when the picker initialized.
26301 * @param {Roo.bootstrap.LocationPicker} this
26302 * @param {Google Location} location
26306 * @event positionchanged
26307 * Fires when the picker position changed.
26308 * @param {Roo.bootstrap.LocationPicker} this
26309 * @param {Google Location} location
26311 positionchanged : true,
26314 * Fires when the map resize.
26315 * @param {Roo.bootstrap.LocationPicker} this
26320 * Fires when the map show.
26321 * @param {Roo.bootstrap.LocationPicker} this
26326 * Fires when the map hide.
26327 * @param {Roo.bootstrap.LocationPicker} this
26332 * Fires when click the map.
26333 * @param {Roo.bootstrap.LocationPicker} this
26334 * @param {Map event} e
26338 * @event mapRightClick
26339 * Fires when right click the map.
26340 * @param {Roo.bootstrap.LocationPicker} this
26341 * @param {Map event} e
26343 mapRightClick : true,
26345 * @event markerClick
26346 * Fires when click the marker.
26347 * @param {Roo.bootstrap.LocationPicker} this
26348 * @param {Map event} e
26350 markerClick : true,
26352 * @event markerRightClick
26353 * Fires when right click the marker.
26354 * @param {Roo.bootstrap.LocationPicker} this
26355 * @param {Map event} e
26357 markerRightClick : true,
26359 * @event OverlayViewDraw
26360 * Fires when OverlayView Draw
26361 * @param {Roo.bootstrap.LocationPicker} this
26363 OverlayViewDraw : true,
26365 * @event OverlayViewOnAdd
26366 * Fires when OverlayView Draw
26367 * @param {Roo.bootstrap.LocationPicker} this
26369 OverlayViewOnAdd : true,
26371 * @event OverlayViewOnRemove
26372 * Fires when OverlayView Draw
26373 * @param {Roo.bootstrap.LocationPicker} this
26375 OverlayViewOnRemove : true,
26377 * @event OverlayViewShow
26378 * Fires when OverlayView Draw
26379 * @param {Roo.bootstrap.LocationPicker} this
26380 * @param {Pixel} cpx
26382 OverlayViewShow : true,
26384 * @event OverlayViewHide
26385 * Fires when OverlayView Draw
26386 * @param {Roo.bootstrap.LocationPicker} this
26388 OverlayViewHide : true,
26390 * @event loadexception
26391 * Fires when load google lib failed.
26392 * @param {Roo.bootstrap.LocationPicker} this
26394 loadexception : true
26399 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26401 gMapContext: false,
26407 mapTypeControl: false,
26408 disableDoubleClickZoom: false,
26410 streetViewControl: false,
26414 enableAutocomplete: false,
26415 enableReverseGeocode: true,
26418 getAutoCreate: function()
26423 cls: 'roo-location-picker'
26429 initEvents: function(ct, position)
26431 if(!this.el.getWidth() || this.isApplied()){
26435 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26440 initial: function()
26442 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26443 this.fireEvent('loadexception', this);
26447 if(!this.mapTypeId){
26448 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26451 this.gMapContext = this.GMapContext();
26453 this.initOverlayView();
26455 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26459 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26460 _this.setPosition(_this.gMapContext.marker.position);
26463 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26464 _this.fireEvent('mapClick', this, event);
26468 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26469 _this.fireEvent('mapRightClick', this, event);
26473 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26474 _this.fireEvent('markerClick', this, event);
26478 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26479 _this.fireEvent('markerRightClick', this, event);
26483 this.setPosition(this.gMapContext.location);
26485 this.fireEvent('initial', this, this.gMapContext.location);
26488 initOverlayView: function()
26492 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26496 _this.fireEvent('OverlayViewDraw', _this);
26501 _this.fireEvent('OverlayViewOnAdd', _this);
26504 onRemove: function()
26506 _this.fireEvent('OverlayViewOnRemove', _this);
26509 show: function(cpx)
26511 _this.fireEvent('OverlayViewShow', _this, cpx);
26516 _this.fireEvent('OverlayViewHide', _this);
26522 fromLatLngToContainerPixel: function(event)
26524 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26527 isApplied: function()
26529 return this.getGmapContext() == false ? false : true;
26532 getGmapContext: function()
26534 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26537 GMapContext: function()
26539 var position = new google.maps.LatLng(this.latitude, this.longitude);
26541 var _map = new google.maps.Map(this.el.dom, {
26544 mapTypeId: this.mapTypeId,
26545 mapTypeControl: this.mapTypeControl,
26546 disableDoubleClickZoom: this.disableDoubleClickZoom,
26547 scrollwheel: this.scrollwheel,
26548 streetViewControl: this.streetViewControl,
26549 locationName: this.locationName,
26550 draggable: this.draggable,
26551 enableAutocomplete: this.enableAutocomplete,
26552 enableReverseGeocode: this.enableReverseGeocode
26555 var _marker = new google.maps.Marker({
26556 position: position,
26558 title: this.markerTitle,
26559 draggable: this.draggable
26566 location: position,
26567 radius: this.radius,
26568 locationName: this.locationName,
26569 addressComponents: {
26570 formatted_address: null,
26571 addressLine1: null,
26572 addressLine2: null,
26574 streetNumber: null,
26578 stateOrProvince: null
26581 domContainer: this.el.dom,
26582 geodecoder: new google.maps.Geocoder()
26586 drawCircle: function(center, radius, options)
26588 if (this.gMapContext.circle != null) {
26589 this.gMapContext.circle.setMap(null);
26593 options = Roo.apply({}, options, {
26594 strokeColor: "#0000FF",
26595 strokeOpacity: .35,
26597 fillColor: "#0000FF",
26601 options.map = this.gMapContext.map;
26602 options.radius = radius;
26603 options.center = center;
26604 this.gMapContext.circle = new google.maps.Circle(options);
26605 return this.gMapContext.circle;
26611 setPosition: function(location)
26613 this.gMapContext.location = location;
26614 this.gMapContext.marker.setPosition(location);
26615 this.gMapContext.map.panTo(location);
26616 this.drawCircle(location, this.gMapContext.radius, {});
26620 if (this.gMapContext.settings.enableReverseGeocode) {
26621 this.gMapContext.geodecoder.geocode({
26622 latLng: this.gMapContext.location
26623 }, function(results, status) {
26625 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26626 _this.gMapContext.locationName = results[0].formatted_address;
26627 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26629 _this.fireEvent('positionchanged', this, location);
26636 this.fireEvent('positionchanged', this, location);
26641 google.maps.event.trigger(this.gMapContext.map, "resize");
26643 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26645 this.fireEvent('resize', this);
26648 setPositionByLatLng: function(latitude, longitude)
26650 this.setPosition(new google.maps.LatLng(latitude, longitude));
26653 getCurrentPosition: function()
26656 latitude: this.gMapContext.location.lat(),
26657 longitude: this.gMapContext.location.lng()
26661 getAddressName: function()
26663 return this.gMapContext.locationName;
26666 getAddressComponents: function()
26668 return this.gMapContext.addressComponents;
26671 address_component_from_google_geocode: function(address_components)
26675 for (var i = 0; i < address_components.length; i++) {
26676 var component = address_components[i];
26677 if (component.types.indexOf("postal_code") >= 0) {
26678 result.postalCode = component.short_name;
26679 } else if (component.types.indexOf("street_number") >= 0) {
26680 result.streetNumber = component.short_name;
26681 } else if (component.types.indexOf("route") >= 0) {
26682 result.streetName = component.short_name;
26683 } else if (component.types.indexOf("neighborhood") >= 0) {
26684 result.city = component.short_name;
26685 } else if (component.types.indexOf("locality") >= 0) {
26686 result.city = component.short_name;
26687 } else if (component.types.indexOf("sublocality") >= 0) {
26688 result.district = component.short_name;
26689 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26690 result.stateOrProvince = component.short_name;
26691 } else if (component.types.indexOf("country") >= 0) {
26692 result.country = component.short_name;
26696 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26697 result.addressLine2 = "";
26701 setZoomLevel: function(zoom)
26703 this.gMapContext.map.setZoom(zoom);
26716 this.fireEvent('show', this);
26727 this.fireEvent('hide', this);
26732 Roo.apply(Roo.bootstrap.LocationPicker, {
26734 OverlayView : function(map, options)
26736 options = options || {};
26750 * @class Roo.bootstrap.Alert
26751 * @extends Roo.bootstrap.Component
26752 * Bootstrap Alert class
26753 * @cfg {String} title The title of alert
26754 * @cfg {String} html The content of alert
26755 * @cfg {String} weight ( success | info | warning | danger )
26756 * @cfg {String} faicon font-awesomeicon
26759 * Create a new alert
26760 * @param {Object} config The config object
26764 Roo.bootstrap.Alert = function(config){
26765 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26769 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26776 getAutoCreate : function()
26785 cls : 'roo-alert-icon'
26790 cls : 'roo-alert-title',
26795 cls : 'roo-alert-text',
26802 cfg.cn[0].cls += ' fa ' + this.faicon;
26806 cfg.cls += ' alert-' + this.weight;
26812 initEvents: function()
26814 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26817 setTitle : function(str)
26819 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26822 setText : function(str)
26824 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26827 setWeight : function(weight)
26830 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26833 this.weight = weight;
26835 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26838 setIcon : function(icon)
26841 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26844 this.faicon = icon;
26846 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26867 * @class Roo.bootstrap.UploadCropbox
26868 * @extends Roo.bootstrap.Component
26869 * Bootstrap UploadCropbox class
26870 * @cfg {String} emptyText show when image has been loaded
26871 * @cfg {String} rotateNotify show when image too small to rotate
26872 * @cfg {Number} errorTimeout default 3000
26873 * @cfg {Number} minWidth default 300
26874 * @cfg {Number} minHeight default 300
26875 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26876 * @cfg {Boolean} isDocument (true|false) default false
26877 * @cfg {String} url action url
26878 * @cfg {String} paramName default 'imageUpload'
26879 * @cfg {String} method default POST
26880 * @cfg {Boolean} loadMask (true|false) default true
26881 * @cfg {Boolean} loadingText default 'Loading...'
26884 * Create a new UploadCropbox
26885 * @param {Object} config The config object
26888 Roo.bootstrap.UploadCropbox = function(config){
26889 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26893 * @event beforeselectfile
26894 * Fire before select file
26895 * @param {Roo.bootstrap.UploadCropbox} this
26897 "beforeselectfile" : true,
26900 * Fire after initEvent
26901 * @param {Roo.bootstrap.UploadCropbox} this
26906 * Fire after initEvent
26907 * @param {Roo.bootstrap.UploadCropbox} this
26908 * @param {String} data
26913 * Fire when preparing the file data
26914 * @param {Roo.bootstrap.UploadCropbox} this
26915 * @param {Object} file
26920 * Fire when get exception
26921 * @param {Roo.bootstrap.UploadCropbox} this
26922 * @param {XMLHttpRequest} xhr
26924 "exception" : true,
26926 * @event beforeloadcanvas
26927 * Fire before load the canvas
26928 * @param {Roo.bootstrap.UploadCropbox} this
26929 * @param {String} src
26931 "beforeloadcanvas" : true,
26934 * Fire when trash image
26935 * @param {Roo.bootstrap.UploadCropbox} this
26940 * Fire when download the image
26941 * @param {Roo.bootstrap.UploadCropbox} this
26945 * @event footerbuttonclick
26946 * Fire when footerbuttonclick
26947 * @param {Roo.bootstrap.UploadCropbox} this
26948 * @param {String} type
26950 "footerbuttonclick" : true,
26954 * @param {Roo.bootstrap.UploadCropbox} this
26959 * Fire when rotate the image
26960 * @param {Roo.bootstrap.UploadCropbox} this
26961 * @param {String} pos
26966 * Fire when inspect the file
26967 * @param {Roo.bootstrap.UploadCropbox} this
26968 * @param {Object} file
26973 * Fire when xhr upload the file
26974 * @param {Roo.bootstrap.UploadCropbox} this
26975 * @param {Object} data
26980 * Fire when arrange the file data
26981 * @param {Roo.bootstrap.UploadCropbox} this
26982 * @param {Object} formData
26987 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26990 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26992 emptyText : 'Click to upload image',
26993 rotateNotify : 'Image is too small to rotate',
26994 errorTimeout : 3000,
27008 cropType : 'image/jpeg',
27010 canvasLoaded : false,
27011 isDocument : false,
27013 paramName : 'imageUpload',
27015 loadingText : 'Loading...',
27018 getAutoCreate : function()
27022 cls : 'roo-upload-cropbox',
27026 cls : 'roo-upload-cropbox-selector',
27031 cls : 'roo-upload-cropbox-body',
27032 style : 'cursor:pointer',
27036 cls : 'roo-upload-cropbox-preview'
27040 cls : 'roo-upload-cropbox-thumb'
27044 cls : 'roo-upload-cropbox-empty-notify',
27045 html : this.emptyText
27049 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27050 html : this.rotateNotify
27056 cls : 'roo-upload-cropbox-footer',
27059 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27069 onRender : function(ct, position)
27071 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27073 if (this.buttons.length) {
27075 Roo.each(this.buttons, function(bb) {
27077 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27079 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27085 this.maskEl = this.el;
27089 initEvents : function()
27091 this.urlAPI = (window.createObjectURL && window) ||
27092 (window.URL && URL.revokeObjectURL && URL) ||
27093 (window.webkitURL && webkitURL);
27095 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27096 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27098 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27099 this.selectorEl.hide();
27101 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27102 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27104 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27105 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27106 this.thumbEl.hide();
27108 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27109 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27111 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27112 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27113 this.errorEl.hide();
27115 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27116 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27117 this.footerEl.hide();
27119 this.setThumbBoxSize();
27125 this.fireEvent('initial', this);
27132 window.addEventListener("resize", function() { _this.resize(); } );
27134 this.bodyEl.on('click', this.beforeSelectFile, this);
27137 this.bodyEl.on('touchstart', this.onTouchStart, this);
27138 this.bodyEl.on('touchmove', this.onTouchMove, this);
27139 this.bodyEl.on('touchend', this.onTouchEnd, this);
27143 this.bodyEl.on('mousedown', this.onMouseDown, this);
27144 this.bodyEl.on('mousemove', this.onMouseMove, this);
27145 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27146 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27147 Roo.get(document).on('mouseup', this.onMouseUp, this);
27150 this.selectorEl.on('change', this.onFileSelected, this);
27156 this.baseScale = 1;
27158 this.baseRotate = 1;
27159 this.dragable = false;
27160 this.pinching = false;
27163 this.cropData = false;
27164 this.notifyEl.dom.innerHTML = this.emptyText;
27166 this.selectorEl.dom.value = '';
27170 resize : function()
27172 if(this.fireEvent('resize', this) != false){
27173 this.setThumbBoxPosition();
27174 this.setCanvasPosition();
27178 onFooterButtonClick : function(e, el, o, type)
27181 case 'rotate-left' :
27182 this.onRotateLeft(e);
27184 case 'rotate-right' :
27185 this.onRotateRight(e);
27188 this.beforeSelectFile(e);
27203 this.fireEvent('footerbuttonclick', this, type);
27206 beforeSelectFile : function(e)
27208 e.preventDefault();
27210 if(this.fireEvent('beforeselectfile', this) != false){
27211 this.selectorEl.dom.click();
27215 onFileSelected : function(e)
27217 e.preventDefault();
27219 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27223 var file = this.selectorEl.dom.files[0];
27225 if(this.fireEvent('inspect', this, file) != false){
27226 this.prepare(file);
27231 trash : function(e)
27233 this.fireEvent('trash', this);
27236 download : function(e)
27238 this.fireEvent('download', this);
27241 loadCanvas : function(src)
27243 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27247 this.imageEl = document.createElement('img');
27251 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27253 this.imageEl.src = src;
27257 onLoadCanvas : function()
27259 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27260 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27262 this.bodyEl.un('click', this.beforeSelectFile, this);
27264 this.notifyEl.hide();
27265 this.thumbEl.show();
27266 this.footerEl.show();
27268 this.baseRotateLevel();
27270 if(this.isDocument){
27271 this.setThumbBoxSize();
27274 this.setThumbBoxPosition();
27276 this.baseScaleLevel();
27282 this.canvasLoaded = true;
27285 this.maskEl.unmask();
27290 setCanvasPosition : function()
27292 if(!this.canvasEl){
27296 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27297 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27299 this.previewEl.setLeft(pw);
27300 this.previewEl.setTop(ph);
27304 onMouseDown : function(e)
27308 this.dragable = true;
27309 this.pinching = false;
27311 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27312 this.dragable = false;
27316 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27317 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27321 onMouseMove : function(e)
27325 if(!this.canvasLoaded){
27329 if (!this.dragable){
27333 var minX = Math.ceil(this.thumbEl.getLeft(true));
27334 var minY = Math.ceil(this.thumbEl.getTop(true));
27336 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27337 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27339 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27340 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27342 x = x - this.mouseX;
27343 y = y - this.mouseY;
27345 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27346 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27348 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27349 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27351 this.previewEl.setLeft(bgX);
27352 this.previewEl.setTop(bgY);
27354 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27355 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27358 onMouseUp : function(e)
27362 this.dragable = false;
27365 onMouseWheel : function(e)
27369 this.startScale = this.scale;
27371 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27373 if(!this.zoomable()){
27374 this.scale = this.startScale;
27383 zoomable : function()
27385 var minScale = this.thumbEl.getWidth() / this.minWidth;
27387 if(this.minWidth < this.minHeight){
27388 minScale = this.thumbEl.getHeight() / this.minHeight;
27391 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27392 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27396 (this.rotate == 0 || this.rotate == 180) &&
27398 width > this.imageEl.OriginWidth ||
27399 height > this.imageEl.OriginHeight ||
27400 (width < this.minWidth && height < this.minHeight)
27408 (this.rotate == 90 || this.rotate == 270) &&
27410 width > this.imageEl.OriginWidth ||
27411 height > this.imageEl.OriginHeight ||
27412 (width < this.minHeight && height < this.minWidth)
27419 !this.isDocument &&
27420 (this.rotate == 0 || this.rotate == 180) &&
27422 width < this.minWidth ||
27423 width > this.imageEl.OriginWidth ||
27424 height < this.minHeight ||
27425 height > this.imageEl.OriginHeight
27432 !this.isDocument &&
27433 (this.rotate == 90 || this.rotate == 270) &&
27435 width < this.minHeight ||
27436 width > this.imageEl.OriginWidth ||
27437 height < this.minWidth ||
27438 height > this.imageEl.OriginHeight
27448 onRotateLeft : function(e)
27450 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27452 var minScale = this.thumbEl.getWidth() / this.minWidth;
27454 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27455 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27457 this.startScale = this.scale;
27459 while (this.getScaleLevel() < minScale){
27461 this.scale = this.scale + 1;
27463 if(!this.zoomable()){
27468 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27469 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27474 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27481 this.scale = this.startScale;
27483 this.onRotateFail();
27488 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27490 if(this.isDocument){
27491 this.setThumbBoxSize();
27492 this.setThumbBoxPosition();
27493 this.setCanvasPosition();
27498 this.fireEvent('rotate', this, 'left');
27502 onRotateRight : function(e)
27504 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27506 var minScale = this.thumbEl.getWidth() / this.minWidth;
27508 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27509 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27511 this.startScale = this.scale;
27513 while (this.getScaleLevel() < minScale){
27515 this.scale = this.scale + 1;
27517 if(!this.zoomable()){
27522 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27523 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27528 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27535 this.scale = this.startScale;
27537 this.onRotateFail();
27542 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27544 if(this.isDocument){
27545 this.setThumbBoxSize();
27546 this.setThumbBoxPosition();
27547 this.setCanvasPosition();
27552 this.fireEvent('rotate', this, 'right');
27555 onRotateFail : function()
27557 this.errorEl.show(true);
27561 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27566 this.previewEl.dom.innerHTML = '';
27568 var canvasEl = document.createElement("canvas");
27570 var contextEl = canvasEl.getContext("2d");
27572 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27573 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27574 var center = this.imageEl.OriginWidth / 2;
27576 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27577 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27578 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27579 center = this.imageEl.OriginHeight / 2;
27582 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27584 contextEl.translate(center, center);
27585 contextEl.rotate(this.rotate * Math.PI / 180);
27587 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27589 this.canvasEl = document.createElement("canvas");
27591 this.contextEl = this.canvasEl.getContext("2d");
27593 switch (this.rotate) {
27596 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27597 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27599 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27604 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27605 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27607 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27608 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);
27612 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27617 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27618 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27620 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27621 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);
27625 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27630 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27631 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27633 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27634 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27638 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);
27645 this.previewEl.appendChild(this.canvasEl);
27647 this.setCanvasPosition();
27652 if(!this.canvasLoaded){
27656 var imageCanvas = document.createElement("canvas");
27658 var imageContext = imageCanvas.getContext("2d");
27660 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27661 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27663 var center = imageCanvas.width / 2;
27665 imageContext.translate(center, center);
27667 imageContext.rotate(this.rotate * Math.PI / 180);
27669 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27671 var canvas = document.createElement("canvas");
27673 var context = canvas.getContext("2d");
27675 canvas.width = this.minWidth;
27676 canvas.height = this.minHeight;
27678 switch (this.rotate) {
27681 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27682 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27684 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27685 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27687 var targetWidth = this.minWidth - 2 * x;
27688 var targetHeight = this.minHeight - 2 * y;
27692 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27693 scale = targetWidth / width;
27696 if(x > 0 && y == 0){
27697 scale = targetHeight / height;
27700 if(x > 0 && y > 0){
27701 scale = targetWidth / width;
27703 if(width < height){
27704 scale = targetHeight / height;
27708 context.scale(scale, scale);
27710 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27711 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27713 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27714 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27716 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27721 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27722 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27724 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27725 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27727 var targetWidth = this.minWidth - 2 * x;
27728 var targetHeight = this.minHeight - 2 * y;
27732 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27733 scale = targetWidth / width;
27736 if(x > 0 && y == 0){
27737 scale = targetHeight / height;
27740 if(x > 0 && y > 0){
27741 scale = targetWidth / width;
27743 if(width < height){
27744 scale = targetHeight / height;
27748 context.scale(scale, scale);
27750 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27751 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27753 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27754 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27756 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27758 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27763 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27764 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27766 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27767 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27769 var targetWidth = this.minWidth - 2 * x;
27770 var targetHeight = this.minHeight - 2 * y;
27774 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27775 scale = targetWidth / width;
27778 if(x > 0 && y == 0){
27779 scale = targetHeight / height;
27782 if(x > 0 && y > 0){
27783 scale = targetWidth / width;
27785 if(width < height){
27786 scale = targetHeight / height;
27790 context.scale(scale, scale);
27792 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27793 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27795 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27796 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27798 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27799 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27801 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27806 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27807 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27809 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27810 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27812 var targetWidth = this.minWidth - 2 * x;
27813 var targetHeight = this.minHeight - 2 * y;
27817 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27818 scale = targetWidth / width;
27821 if(x > 0 && y == 0){
27822 scale = targetHeight / height;
27825 if(x > 0 && y > 0){
27826 scale = targetWidth / width;
27828 if(width < height){
27829 scale = targetHeight / height;
27833 context.scale(scale, scale);
27835 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27836 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27838 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27839 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27841 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27843 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27850 this.cropData = canvas.toDataURL(this.cropType);
27852 if(this.fireEvent('crop', this, this.cropData) !== false){
27853 this.process(this.file, this.cropData);
27860 setThumbBoxSize : function()
27864 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27865 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27866 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27868 this.minWidth = width;
27869 this.minHeight = height;
27871 if(this.rotate == 90 || this.rotate == 270){
27872 this.minWidth = height;
27873 this.minHeight = width;
27878 width = Math.ceil(this.minWidth * height / this.minHeight);
27880 if(this.minWidth > this.minHeight){
27882 height = Math.ceil(this.minHeight * width / this.minWidth);
27885 this.thumbEl.setStyle({
27886 width : width + 'px',
27887 height : height + 'px'
27894 setThumbBoxPosition : function()
27896 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27897 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27899 this.thumbEl.setLeft(x);
27900 this.thumbEl.setTop(y);
27904 baseRotateLevel : function()
27906 this.baseRotate = 1;
27909 typeof(this.exif) != 'undefined' &&
27910 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27911 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27913 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27916 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27920 baseScaleLevel : function()
27924 if(this.isDocument){
27926 if(this.baseRotate == 6 || this.baseRotate == 8){
27928 height = this.thumbEl.getHeight();
27929 this.baseScale = height / this.imageEl.OriginWidth;
27931 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27932 width = this.thumbEl.getWidth();
27933 this.baseScale = width / this.imageEl.OriginHeight;
27939 height = this.thumbEl.getHeight();
27940 this.baseScale = height / this.imageEl.OriginHeight;
27942 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27943 width = this.thumbEl.getWidth();
27944 this.baseScale = width / this.imageEl.OriginWidth;
27950 if(this.baseRotate == 6 || this.baseRotate == 8){
27952 width = this.thumbEl.getHeight();
27953 this.baseScale = width / this.imageEl.OriginHeight;
27955 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27956 height = this.thumbEl.getWidth();
27957 this.baseScale = height / this.imageEl.OriginHeight;
27960 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27961 height = this.thumbEl.getWidth();
27962 this.baseScale = height / this.imageEl.OriginHeight;
27964 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27965 width = this.thumbEl.getHeight();
27966 this.baseScale = width / this.imageEl.OriginWidth;
27973 width = this.thumbEl.getWidth();
27974 this.baseScale = width / this.imageEl.OriginWidth;
27976 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27977 height = this.thumbEl.getHeight();
27978 this.baseScale = height / this.imageEl.OriginHeight;
27981 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27983 height = this.thumbEl.getHeight();
27984 this.baseScale = height / this.imageEl.OriginHeight;
27986 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27987 width = this.thumbEl.getWidth();
27988 this.baseScale = width / this.imageEl.OriginWidth;
27996 getScaleLevel : function()
27998 return this.baseScale * Math.pow(1.1, this.scale);
28001 onTouchStart : function(e)
28003 if(!this.canvasLoaded){
28004 this.beforeSelectFile(e);
28008 var touches = e.browserEvent.touches;
28014 if(touches.length == 1){
28015 this.onMouseDown(e);
28019 if(touches.length != 2){
28025 for(var i = 0, finger; finger = touches[i]; i++){
28026 coords.push(finger.pageX, finger.pageY);
28029 var x = Math.pow(coords[0] - coords[2], 2);
28030 var y = Math.pow(coords[1] - coords[3], 2);
28032 this.startDistance = Math.sqrt(x + y);
28034 this.startScale = this.scale;
28036 this.pinching = true;
28037 this.dragable = false;
28041 onTouchMove : function(e)
28043 if(!this.pinching && !this.dragable){
28047 var touches = e.browserEvent.touches;
28054 this.onMouseMove(e);
28060 for(var i = 0, finger; finger = touches[i]; i++){
28061 coords.push(finger.pageX, finger.pageY);
28064 var x = Math.pow(coords[0] - coords[2], 2);
28065 var y = Math.pow(coords[1] - coords[3], 2);
28067 this.endDistance = Math.sqrt(x + y);
28069 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28071 if(!this.zoomable()){
28072 this.scale = this.startScale;
28080 onTouchEnd : function(e)
28082 this.pinching = false;
28083 this.dragable = false;
28087 process : function(file, crop)
28090 this.maskEl.mask(this.loadingText);
28093 this.xhr = new XMLHttpRequest();
28095 file.xhr = this.xhr;
28097 this.xhr.open(this.method, this.url, true);
28100 "Accept": "application/json",
28101 "Cache-Control": "no-cache",
28102 "X-Requested-With": "XMLHttpRequest"
28105 for (var headerName in headers) {
28106 var headerValue = headers[headerName];
28108 this.xhr.setRequestHeader(headerName, headerValue);
28114 this.xhr.onload = function()
28116 _this.xhrOnLoad(_this.xhr);
28119 this.xhr.onerror = function()
28121 _this.xhrOnError(_this.xhr);
28124 var formData = new FormData();
28126 formData.append('returnHTML', 'NO');
28129 formData.append('crop', crop);
28132 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28133 formData.append(this.paramName, file, file.name);
28136 if(typeof(file.filename) != 'undefined'){
28137 formData.append('filename', file.filename);
28140 if(typeof(file.mimetype) != 'undefined'){
28141 formData.append('mimetype', file.mimetype);
28144 if(this.fireEvent('arrange', this, formData) != false){
28145 this.xhr.send(formData);
28149 xhrOnLoad : function(xhr)
28152 this.maskEl.unmask();
28155 if (xhr.readyState !== 4) {
28156 this.fireEvent('exception', this, xhr);
28160 var response = Roo.decode(xhr.responseText);
28162 if(!response.success){
28163 this.fireEvent('exception', this, xhr);
28167 var response = Roo.decode(xhr.responseText);
28169 this.fireEvent('upload', this, response);
28173 xhrOnError : function()
28176 this.maskEl.unmask();
28179 Roo.log('xhr on error');
28181 var response = Roo.decode(xhr.responseText);
28187 prepare : function(file)
28190 this.maskEl.mask(this.loadingText);
28196 if(typeof(file) === 'string'){
28197 this.loadCanvas(file);
28201 if(!file || !this.urlAPI){
28206 this.cropType = file.type;
28210 if(this.fireEvent('prepare', this, this.file) != false){
28212 var reader = new FileReader();
28214 reader.onload = function (e) {
28215 if (e.target.error) {
28216 Roo.log(e.target.error);
28220 var buffer = e.target.result,
28221 dataView = new DataView(buffer),
28223 maxOffset = dataView.byteLength - 4,
28227 if (dataView.getUint16(0) === 0xffd8) {
28228 while (offset < maxOffset) {
28229 markerBytes = dataView.getUint16(offset);
28231 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28232 markerLength = dataView.getUint16(offset + 2) + 2;
28233 if (offset + markerLength > dataView.byteLength) {
28234 Roo.log('Invalid meta data: Invalid segment size.');
28238 if(markerBytes == 0xffe1){
28239 _this.parseExifData(
28246 offset += markerLength;
28256 var url = _this.urlAPI.createObjectURL(_this.file);
28258 _this.loadCanvas(url);
28263 reader.readAsArrayBuffer(this.file);
28269 parseExifData : function(dataView, offset, length)
28271 var tiffOffset = offset + 10,
28275 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28276 // No Exif data, might be XMP data instead
28280 // Check for the ASCII code for "Exif" (0x45786966):
28281 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28282 // No Exif data, might be XMP data instead
28285 if (tiffOffset + 8 > dataView.byteLength) {
28286 Roo.log('Invalid Exif data: Invalid segment size.');
28289 // Check for the two null bytes:
28290 if (dataView.getUint16(offset + 8) !== 0x0000) {
28291 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28294 // Check the byte alignment:
28295 switch (dataView.getUint16(tiffOffset)) {
28297 littleEndian = true;
28300 littleEndian = false;
28303 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28306 // Check for the TIFF tag marker (0x002A):
28307 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28308 Roo.log('Invalid Exif data: Missing TIFF marker.');
28311 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28312 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28314 this.parseExifTags(
28317 tiffOffset + dirOffset,
28322 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28327 if (dirOffset + 6 > dataView.byteLength) {
28328 Roo.log('Invalid Exif data: Invalid directory offset.');
28331 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28332 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28333 if (dirEndOffset + 4 > dataView.byteLength) {
28334 Roo.log('Invalid Exif data: Invalid directory size.');
28337 for (i = 0; i < tagsNumber; i += 1) {
28341 dirOffset + 2 + 12 * i, // tag offset
28345 // Return the offset to the next directory:
28346 return dataView.getUint32(dirEndOffset, littleEndian);
28349 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28351 var tag = dataView.getUint16(offset, littleEndian);
28353 this.exif[tag] = this.getExifValue(
28357 dataView.getUint16(offset + 2, littleEndian), // tag type
28358 dataView.getUint32(offset + 4, littleEndian), // tag length
28363 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28365 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28374 Roo.log('Invalid Exif data: Invalid tag type.');
28378 tagSize = tagType.size * length;
28379 // Determine if the value is contained in the dataOffset bytes,
28380 // or if the value at the dataOffset is a pointer to the actual data:
28381 dataOffset = tagSize > 4 ?
28382 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28383 if (dataOffset + tagSize > dataView.byteLength) {
28384 Roo.log('Invalid Exif data: Invalid data offset.');
28387 if (length === 1) {
28388 return tagType.getValue(dataView, dataOffset, littleEndian);
28391 for (i = 0; i < length; i += 1) {
28392 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28395 if (tagType.ascii) {
28397 // Concatenate the chars:
28398 for (i = 0; i < values.length; i += 1) {
28400 // Ignore the terminating NULL byte(s):
28401 if (c === '\u0000') {
28413 Roo.apply(Roo.bootstrap.UploadCropbox, {
28415 'Orientation': 0x0112
28419 1: 0, //'top-left',
28421 3: 180, //'bottom-right',
28422 // 4: 'bottom-left',
28424 6: 90, //'right-top',
28425 // 7: 'right-bottom',
28426 8: 270 //'left-bottom'
28430 // byte, 8-bit unsigned int:
28432 getValue: function (dataView, dataOffset) {
28433 return dataView.getUint8(dataOffset);
28437 // ascii, 8-bit byte:
28439 getValue: function (dataView, dataOffset) {
28440 return String.fromCharCode(dataView.getUint8(dataOffset));
28445 // short, 16 bit int:
28447 getValue: function (dataView, dataOffset, littleEndian) {
28448 return dataView.getUint16(dataOffset, littleEndian);
28452 // long, 32 bit int:
28454 getValue: function (dataView, dataOffset, littleEndian) {
28455 return dataView.getUint32(dataOffset, littleEndian);
28459 // rational = two long values, first is numerator, second is denominator:
28461 getValue: function (dataView, dataOffset, littleEndian) {
28462 return dataView.getUint32(dataOffset, littleEndian) /
28463 dataView.getUint32(dataOffset + 4, littleEndian);
28467 // slong, 32 bit signed int:
28469 getValue: function (dataView, dataOffset, littleEndian) {
28470 return dataView.getInt32(dataOffset, littleEndian);
28474 // srational, two slongs, first is numerator, second is denominator:
28476 getValue: function (dataView, dataOffset, littleEndian) {
28477 return dataView.getInt32(dataOffset, littleEndian) /
28478 dataView.getInt32(dataOffset + 4, littleEndian);
28488 cls : 'btn-group roo-upload-cropbox-rotate-left',
28489 action : 'rotate-left',
28493 cls : 'btn btn-default',
28494 html : '<i class="fa fa-undo"></i>'
28500 cls : 'btn-group roo-upload-cropbox-picture',
28501 action : 'picture',
28505 cls : 'btn btn-default',
28506 html : '<i class="fa fa-picture-o"></i>'
28512 cls : 'btn-group roo-upload-cropbox-rotate-right',
28513 action : 'rotate-right',
28517 cls : 'btn btn-default',
28518 html : '<i class="fa fa-repeat"></i>'
28526 cls : 'btn-group roo-upload-cropbox-rotate-left',
28527 action : 'rotate-left',
28531 cls : 'btn btn-default',
28532 html : '<i class="fa fa-undo"></i>'
28538 cls : 'btn-group roo-upload-cropbox-download',
28539 action : 'download',
28543 cls : 'btn btn-default',
28544 html : '<i class="fa fa-download"></i>'
28550 cls : 'btn-group roo-upload-cropbox-crop',
28555 cls : 'btn btn-default',
28556 html : '<i class="fa fa-crop"></i>'
28562 cls : 'btn-group roo-upload-cropbox-trash',
28567 cls : 'btn btn-default',
28568 html : '<i class="fa fa-trash"></i>'
28574 cls : 'btn-group roo-upload-cropbox-rotate-right',
28575 action : 'rotate-right',
28579 cls : 'btn btn-default',
28580 html : '<i class="fa fa-repeat"></i>'
28588 cls : 'btn-group roo-upload-cropbox-rotate-left',
28589 action : 'rotate-left',
28593 cls : 'btn btn-default',
28594 html : '<i class="fa fa-undo"></i>'
28600 cls : 'btn-group roo-upload-cropbox-rotate-right',
28601 action : 'rotate-right',
28605 cls : 'btn btn-default',
28606 html : '<i class="fa fa-repeat"></i>'
28619 * @class Roo.bootstrap.DocumentManager
28620 * @extends Roo.bootstrap.Component
28621 * Bootstrap DocumentManager class
28622 * @cfg {String} paramName default 'imageUpload'
28623 * @cfg {String} toolTipName default 'filename'
28624 * @cfg {String} method default POST
28625 * @cfg {String} url action url
28626 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28627 * @cfg {Boolean} multiple multiple upload default true
28628 * @cfg {Number} thumbSize default 300
28629 * @cfg {String} fieldLabel
28630 * @cfg {Number} labelWidth default 4
28631 * @cfg {String} labelAlign (left|top) default left
28632 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28633 * @cfg {Number} labellg set the width of label (1-12)
28634 * @cfg {Number} labelmd set the width of label (1-12)
28635 * @cfg {Number} labelsm set the width of label (1-12)
28636 * @cfg {Number} labelxs set the width of label (1-12)
28639 * Create a new DocumentManager
28640 * @param {Object} config The config object
28643 Roo.bootstrap.DocumentManager = function(config){
28644 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28647 this.delegates = [];
28652 * Fire when initial the DocumentManager
28653 * @param {Roo.bootstrap.DocumentManager} this
28658 * inspect selected file
28659 * @param {Roo.bootstrap.DocumentManager} this
28660 * @param {File} file
28665 * Fire when xhr load exception
28666 * @param {Roo.bootstrap.DocumentManager} this
28667 * @param {XMLHttpRequest} xhr
28669 "exception" : true,
28671 * @event afterupload
28672 * Fire when xhr load exception
28673 * @param {Roo.bootstrap.DocumentManager} this
28674 * @param {XMLHttpRequest} xhr
28676 "afterupload" : true,
28679 * prepare the form data
28680 * @param {Roo.bootstrap.DocumentManager} this
28681 * @param {Object} formData
28686 * Fire when remove the file
28687 * @param {Roo.bootstrap.DocumentManager} this
28688 * @param {Object} file
28693 * Fire after refresh the file
28694 * @param {Roo.bootstrap.DocumentManager} this
28699 * Fire after click the image
28700 * @param {Roo.bootstrap.DocumentManager} this
28701 * @param {Object} file
28706 * Fire when upload a image and editable set to true
28707 * @param {Roo.bootstrap.DocumentManager} this
28708 * @param {Object} file
28712 * @event beforeselectfile
28713 * Fire before select file
28714 * @param {Roo.bootstrap.DocumentManager} this
28716 "beforeselectfile" : true,
28719 * Fire before process file
28720 * @param {Roo.bootstrap.DocumentManager} this
28721 * @param {Object} file
28725 * @event previewrendered
28726 * Fire when preview rendered
28727 * @param {Roo.bootstrap.DocumentManager} this
28728 * @param {Object} file
28730 "previewrendered" : true,
28733 "previewResize" : true
28738 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28747 paramName : 'imageUpload',
28748 toolTipName : 'filename',
28751 labelAlign : 'left',
28761 getAutoCreate : function()
28763 var managerWidget = {
28765 cls : 'roo-document-manager',
28769 cls : 'roo-document-manager-selector',
28774 cls : 'roo-document-manager-uploader',
28778 cls : 'roo-document-manager-upload-btn',
28779 html : '<i class="fa fa-plus"></i>'
28790 cls : 'column col-md-12',
28795 if(this.fieldLabel.length){
28800 cls : 'column col-md-12',
28801 html : this.fieldLabel
28805 cls : 'column col-md-12',
28810 if(this.labelAlign == 'left'){
28815 html : this.fieldLabel
28824 if(this.labelWidth > 12){
28825 content[0].style = "width: " + this.labelWidth + 'px';
28828 if(this.labelWidth < 13 && this.labelmd == 0){
28829 this.labelmd = this.labelWidth;
28832 if(this.labellg > 0){
28833 content[0].cls += ' col-lg-' + this.labellg;
28834 content[1].cls += ' col-lg-' + (12 - this.labellg);
28837 if(this.labelmd > 0){
28838 content[0].cls += ' col-md-' + this.labelmd;
28839 content[1].cls += ' col-md-' + (12 - this.labelmd);
28842 if(this.labelsm > 0){
28843 content[0].cls += ' col-sm-' + this.labelsm;
28844 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28847 if(this.labelxs > 0){
28848 content[0].cls += ' col-xs-' + this.labelxs;
28849 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28857 cls : 'row clearfix',
28865 initEvents : function()
28867 this.managerEl = this.el.select('.roo-document-manager', true).first();
28868 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28870 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28871 this.selectorEl.hide();
28874 this.selectorEl.attr('multiple', 'multiple');
28877 this.selectorEl.on('change', this.onFileSelected, this);
28879 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28880 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28882 this.uploader.on('click', this.onUploaderClick, this);
28884 this.renderProgressDialog();
28888 window.addEventListener("resize", function() { _this.refresh(); } );
28890 this.fireEvent('initial', this);
28893 renderProgressDialog : function()
28897 this.progressDialog = new Roo.bootstrap.Modal({
28898 cls : 'roo-document-manager-progress-dialog',
28899 allow_close : false,
28909 btnclick : function() {
28910 _this.uploadCancel();
28916 this.progressDialog.render(Roo.get(document.body));
28918 this.progress = new Roo.bootstrap.Progress({
28919 cls : 'roo-document-manager-progress',
28924 this.progress.render(this.progressDialog.getChildContainer());
28926 this.progressBar = new Roo.bootstrap.ProgressBar({
28927 cls : 'roo-document-manager-progress-bar',
28930 aria_valuemax : 12,
28934 this.progressBar.render(this.progress.getChildContainer());
28937 onUploaderClick : function(e)
28939 e.preventDefault();
28941 if(this.fireEvent('beforeselectfile', this) != false){
28942 this.selectorEl.dom.click();
28947 onFileSelected : function(e)
28949 e.preventDefault();
28951 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28955 Roo.each(this.selectorEl.dom.files, function(file){
28956 if(this.fireEvent('inspect', this, file) != false){
28957 this.files.push(file);
28967 this.selectorEl.dom.value = '';
28969 if(!this.files || !this.files.length){
28973 if(this.boxes > 0 && this.files.length > this.boxes){
28974 this.files = this.files.slice(0, this.boxes);
28977 this.uploader.show();
28979 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28980 this.uploader.hide();
28989 Roo.each(this.files, function(file){
28991 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28992 var f = this.renderPreview(file);
28997 if(file.type.indexOf('image') != -1){
28998 this.delegates.push(
29000 _this.process(file);
29001 }).createDelegate(this)
29009 _this.process(file);
29010 }).createDelegate(this)
29015 this.files = files;
29017 this.delegates = this.delegates.concat(docs);
29019 if(!this.delegates.length){
29024 this.progressBar.aria_valuemax = this.delegates.length;
29031 arrange : function()
29033 if(!this.delegates.length){
29034 this.progressDialog.hide();
29039 var delegate = this.delegates.shift();
29041 this.progressDialog.show();
29043 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29045 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29050 refresh : function()
29052 this.uploader.show();
29054 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29055 this.uploader.hide();
29058 Roo.isTouch ? this.closable(false) : this.closable(true);
29060 this.fireEvent('refresh', this);
29063 onRemove : function(e, el, o)
29065 e.preventDefault();
29067 this.fireEvent('remove', this, o);
29071 remove : function(o)
29075 Roo.each(this.files, function(file){
29076 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29085 this.files = files;
29092 Roo.each(this.files, function(file){
29097 file.target.remove();
29106 onClick : function(e, el, o)
29108 e.preventDefault();
29110 this.fireEvent('click', this, o);
29114 closable : function(closable)
29116 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29118 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29130 xhrOnLoad : function(xhr)
29132 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29136 if (xhr.readyState !== 4) {
29138 this.fireEvent('exception', this, xhr);
29142 var response = Roo.decode(xhr.responseText);
29144 if(!response.success){
29146 this.fireEvent('exception', this, xhr);
29150 var file = this.renderPreview(response.data);
29152 this.files.push(file);
29156 this.fireEvent('afterupload', this, xhr);
29160 xhrOnError : function(xhr)
29162 Roo.log('xhr on error');
29164 var response = Roo.decode(xhr.responseText);
29171 process : function(file)
29173 if(this.fireEvent('process', this, file) !== false){
29174 if(this.editable && file.type.indexOf('image') != -1){
29175 this.fireEvent('edit', this, file);
29179 this.uploadStart(file, false);
29186 uploadStart : function(file, crop)
29188 this.xhr = new XMLHttpRequest();
29190 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29195 file.xhr = this.xhr;
29197 this.managerEl.createChild({
29199 cls : 'roo-document-manager-loading',
29203 tooltip : file.name,
29204 cls : 'roo-document-manager-thumb',
29205 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29211 this.xhr.open(this.method, this.url, true);
29214 "Accept": "application/json",
29215 "Cache-Control": "no-cache",
29216 "X-Requested-With": "XMLHttpRequest"
29219 for (var headerName in headers) {
29220 var headerValue = headers[headerName];
29222 this.xhr.setRequestHeader(headerName, headerValue);
29228 this.xhr.onload = function()
29230 _this.xhrOnLoad(_this.xhr);
29233 this.xhr.onerror = function()
29235 _this.xhrOnError(_this.xhr);
29238 var formData = new FormData();
29240 formData.append('returnHTML', 'NO');
29243 formData.append('crop', crop);
29246 formData.append(this.paramName, file, file.name);
29253 if(this.fireEvent('prepare', this, formData, options) != false){
29255 if(options.manually){
29259 this.xhr.send(formData);
29263 this.uploadCancel();
29266 uploadCancel : function()
29272 this.delegates = [];
29274 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29281 renderPreview : function(file)
29283 if(typeof(file.target) != 'undefined' && file.target){
29287 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29289 var previewEl = this.managerEl.createChild({
29291 cls : 'roo-document-manager-preview',
29295 tooltip : file[this.toolTipName],
29296 cls : 'roo-document-manager-thumb',
29297 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29302 html : '<i class="fa fa-times-circle"></i>'
29307 var close = previewEl.select('button.close', true).first();
29309 close.on('click', this.onRemove, this, file);
29311 file.target = previewEl;
29313 var image = previewEl.select('img', true).first();
29317 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29319 image.on('click', this.onClick, this, file);
29321 this.fireEvent('previewrendered', this, file);
29327 onPreviewLoad : function(file, image)
29329 if(typeof(file.target) == 'undefined' || !file.target){
29333 var width = image.dom.naturalWidth || image.dom.width;
29334 var height = image.dom.naturalHeight || image.dom.height;
29336 if(!this.previewResize) {
29340 if(width > height){
29341 file.target.addClass('wide');
29345 file.target.addClass('tall');
29350 uploadFromSource : function(file, crop)
29352 this.xhr = new XMLHttpRequest();
29354 this.managerEl.createChild({
29356 cls : 'roo-document-manager-loading',
29360 tooltip : file.name,
29361 cls : 'roo-document-manager-thumb',
29362 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29368 this.xhr.open(this.method, this.url, true);
29371 "Accept": "application/json",
29372 "Cache-Control": "no-cache",
29373 "X-Requested-With": "XMLHttpRequest"
29376 for (var headerName in headers) {
29377 var headerValue = headers[headerName];
29379 this.xhr.setRequestHeader(headerName, headerValue);
29385 this.xhr.onload = function()
29387 _this.xhrOnLoad(_this.xhr);
29390 this.xhr.onerror = function()
29392 _this.xhrOnError(_this.xhr);
29395 var formData = new FormData();
29397 formData.append('returnHTML', 'NO');
29399 formData.append('crop', crop);
29401 if(typeof(file.filename) != 'undefined'){
29402 formData.append('filename', file.filename);
29405 if(typeof(file.mimetype) != 'undefined'){
29406 formData.append('mimetype', file.mimetype);
29411 if(this.fireEvent('prepare', this, formData) != false){
29412 this.xhr.send(formData);
29422 * @class Roo.bootstrap.DocumentViewer
29423 * @extends Roo.bootstrap.Component
29424 * Bootstrap DocumentViewer class
29425 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29426 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29429 * Create a new DocumentViewer
29430 * @param {Object} config The config object
29433 Roo.bootstrap.DocumentViewer = function(config){
29434 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29439 * Fire after initEvent
29440 * @param {Roo.bootstrap.DocumentViewer} this
29446 * @param {Roo.bootstrap.DocumentViewer} this
29451 * Fire after download button
29452 * @param {Roo.bootstrap.DocumentViewer} this
29457 * Fire after trash button
29458 * @param {Roo.bootstrap.DocumentViewer} this
29465 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29467 showDownload : true,
29471 getAutoCreate : function()
29475 cls : 'roo-document-viewer',
29479 cls : 'roo-document-viewer-body',
29483 cls : 'roo-document-viewer-thumb',
29487 cls : 'roo-document-viewer-image'
29495 cls : 'roo-document-viewer-footer',
29498 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29502 cls : 'btn-group roo-document-viewer-download',
29506 cls : 'btn btn-default',
29507 html : '<i class="fa fa-download"></i>'
29513 cls : 'btn-group roo-document-viewer-trash',
29517 cls : 'btn btn-default',
29518 html : '<i class="fa fa-trash"></i>'
29531 initEvents : function()
29533 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29534 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29536 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29537 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29539 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29540 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29542 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29543 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29545 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29546 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29548 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29549 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29551 this.bodyEl.on('click', this.onClick, this);
29552 this.downloadBtn.on('click', this.onDownload, this);
29553 this.trashBtn.on('click', this.onTrash, this);
29555 this.downloadBtn.hide();
29556 this.trashBtn.hide();
29558 if(this.showDownload){
29559 this.downloadBtn.show();
29562 if(this.showTrash){
29563 this.trashBtn.show();
29566 if(!this.showDownload && !this.showTrash) {
29567 this.footerEl.hide();
29572 initial : function()
29574 this.fireEvent('initial', this);
29578 onClick : function(e)
29580 e.preventDefault();
29582 this.fireEvent('click', this);
29585 onDownload : function(e)
29587 e.preventDefault();
29589 this.fireEvent('download', this);
29592 onTrash : function(e)
29594 e.preventDefault();
29596 this.fireEvent('trash', this);
29608 * @class Roo.bootstrap.NavProgressBar
29609 * @extends Roo.bootstrap.Component
29610 * Bootstrap NavProgressBar class
29613 * Create a new nav progress bar
29614 * @param {Object} config The config object
29617 Roo.bootstrap.NavProgressBar = function(config){
29618 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29620 this.bullets = this.bullets || [];
29622 // Roo.bootstrap.NavProgressBar.register(this);
29626 * Fires when the active item changes
29627 * @param {Roo.bootstrap.NavProgressBar} this
29628 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29629 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29636 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29641 getAutoCreate : function()
29643 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29647 cls : 'roo-navigation-bar-group',
29651 cls : 'roo-navigation-top-bar'
29655 cls : 'roo-navigation-bullets-bar',
29659 cls : 'roo-navigation-bar'
29666 cls : 'roo-navigation-bottom-bar'
29676 initEvents: function()
29681 onRender : function(ct, position)
29683 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29685 if(this.bullets.length){
29686 Roo.each(this.bullets, function(b){
29695 addItem : function(cfg)
29697 var item = new Roo.bootstrap.NavProgressItem(cfg);
29699 item.parentId = this.id;
29700 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29703 var top = new Roo.bootstrap.Element({
29705 cls : 'roo-navigation-bar-text'
29708 var bottom = new Roo.bootstrap.Element({
29710 cls : 'roo-navigation-bar-text'
29713 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29714 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29716 var topText = new Roo.bootstrap.Element({
29718 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29721 var bottomText = new Roo.bootstrap.Element({
29723 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29726 topText.onRender(top.el, null);
29727 bottomText.onRender(bottom.el, null);
29730 item.bottomEl = bottom;
29733 this.barItems.push(item);
29738 getActive : function()
29740 var active = false;
29742 Roo.each(this.barItems, function(v){
29744 if (!v.isActive()) {
29756 setActiveItem : function(item)
29760 Roo.each(this.barItems, function(v){
29761 if (v.rid == item.rid) {
29765 if (v.isActive()) {
29766 v.setActive(false);
29771 item.setActive(true);
29773 this.fireEvent('changed', this, item, prev);
29776 getBarItem: function(rid)
29780 Roo.each(this.barItems, function(e) {
29781 if (e.rid != rid) {
29792 indexOfItem : function(item)
29796 Roo.each(this.barItems, function(v, i){
29798 if (v.rid != item.rid) {
29809 setActiveNext : function()
29811 var i = this.indexOfItem(this.getActive());
29813 if (i > this.barItems.length) {
29817 this.setActiveItem(this.barItems[i+1]);
29820 setActivePrev : function()
29822 var i = this.indexOfItem(this.getActive());
29828 this.setActiveItem(this.barItems[i-1]);
29831 format : function()
29833 if(!this.barItems.length){
29837 var width = 100 / this.barItems.length;
29839 Roo.each(this.barItems, function(i){
29840 i.el.setStyle('width', width + '%');
29841 i.topEl.el.setStyle('width', width + '%');
29842 i.bottomEl.el.setStyle('width', width + '%');
29851 * Nav Progress Item
29856 * @class Roo.bootstrap.NavProgressItem
29857 * @extends Roo.bootstrap.Component
29858 * Bootstrap NavProgressItem class
29859 * @cfg {String} rid the reference id
29860 * @cfg {Boolean} active (true|false) Is item active default false
29861 * @cfg {Boolean} disabled (true|false) Is item active default false
29862 * @cfg {String} html
29863 * @cfg {String} position (top|bottom) text position default bottom
29864 * @cfg {String} icon show icon instead of number
29867 * Create a new NavProgressItem
29868 * @param {Object} config The config object
29870 Roo.bootstrap.NavProgressItem = function(config){
29871 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29876 * The raw click event for the entire grid.
29877 * @param {Roo.bootstrap.NavProgressItem} this
29878 * @param {Roo.EventObject} e
29885 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29891 position : 'bottom',
29894 getAutoCreate : function()
29896 var iconCls = 'roo-navigation-bar-item-icon';
29898 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29902 cls: 'roo-navigation-bar-item',
29912 cfg.cls += ' active';
29915 cfg.cls += ' disabled';
29921 disable : function()
29923 this.setDisabled(true);
29926 enable : function()
29928 this.setDisabled(false);
29931 initEvents: function()
29933 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29935 this.iconEl.on('click', this.onClick, this);
29938 onClick : function(e)
29940 e.preventDefault();
29946 if(this.fireEvent('click', this, e) === false){
29950 this.parent().setActiveItem(this);
29953 isActive: function ()
29955 return this.active;
29958 setActive : function(state)
29960 if(this.active == state){
29964 this.active = state;
29967 this.el.addClass('active');
29971 this.el.removeClass('active');
29976 setDisabled : function(state)
29978 if(this.disabled == state){
29982 this.disabled = state;
29985 this.el.addClass('disabled');
29989 this.el.removeClass('disabled');
29992 tooltipEl : function()
29994 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30007 * @class Roo.bootstrap.FieldLabel
30008 * @extends Roo.bootstrap.Component
30009 * Bootstrap FieldLabel class
30010 * @cfg {String} html contents of the element
30011 * @cfg {String} tag tag of the element default label
30012 * @cfg {String} cls class of the element
30013 * @cfg {String} target label target
30014 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30015 * @cfg {String} invalidClass default "text-warning"
30016 * @cfg {String} validClass default "text-success"
30017 * @cfg {String} iconTooltip default "This field is required"
30018 * @cfg {String} indicatorpos (left|right) default left
30021 * Create a new FieldLabel
30022 * @param {Object} config The config object
30025 Roo.bootstrap.FieldLabel = function(config){
30026 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30031 * Fires after the field has been marked as invalid.
30032 * @param {Roo.form.FieldLabel} this
30033 * @param {String} msg The validation message
30038 * Fires after the field has been validated with no errors.
30039 * @param {Roo.form.FieldLabel} this
30045 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30052 invalidClass : 'has-warning',
30053 validClass : 'has-success',
30054 iconTooltip : 'This field is required',
30055 indicatorpos : 'left',
30057 getAutoCreate : function(){
30060 if (!this.allowBlank) {
30066 cls : 'roo-bootstrap-field-label ' + this.cls,
30071 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30072 tooltip : this.iconTooltip
30081 if(this.indicatorpos == 'right'){
30084 cls : 'roo-bootstrap-field-label ' + this.cls,
30093 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30094 tooltip : this.iconTooltip
30103 initEvents: function()
30105 Roo.bootstrap.Element.superclass.initEvents.call(this);
30107 this.indicator = this.indicatorEl();
30109 if(this.indicator){
30110 this.indicator.removeClass('visible');
30111 this.indicator.addClass('invisible');
30114 Roo.bootstrap.FieldLabel.register(this);
30117 indicatorEl : function()
30119 var indicator = this.el.select('i.roo-required-indicator',true).first();
30130 * Mark this field as valid
30132 markValid : function()
30134 if(this.indicator){
30135 this.indicator.removeClass('visible');
30136 this.indicator.addClass('invisible');
30139 this.el.removeClass(this.invalidClass);
30141 this.el.addClass(this.validClass);
30143 this.fireEvent('valid', this);
30147 * Mark this field as invalid
30148 * @param {String} msg The validation message
30150 markInvalid : function(msg)
30152 if(this.indicator){
30153 this.indicator.removeClass('invisible');
30154 this.indicator.addClass('visible');
30157 this.el.removeClass(this.validClass);
30159 this.el.addClass(this.invalidClass);
30161 this.fireEvent('invalid', this, msg);
30167 Roo.apply(Roo.bootstrap.FieldLabel, {
30172 * register a FieldLabel Group
30173 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30175 register : function(label)
30177 if(this.groups.hasOwnProperty(label.target)){
30181 this.groups[label.target] = label;
30185 * fetch a FieldLabel Group based on the target
30186 * @param {string} target
30187 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30189 get: function(target) {
30190 if (typeof(this.groups[target]) == 'undefined') {
30194 return this.groups[target] ;
30203 * page DateSplitField.
30209 * @class Roo.bootstrap.DateSplitField
30210 * @extends Roo.bootstrap.Component
30211 * Bootstrap DateSplitField class
30212 * @cfg {string} fieldLabel - the label associated
30213 * @cfg {Number} labelWidth set the width of label (0-12)
30214 * @cfg {String} labelAlign (top|left)
30215 * @cfg {Boolean} dayAllowBlank (true|false) default false
30216 * @cfg {Boolean} monthAllowBlank (true|false) default false
30217 * @cfg {Boolean} yearAllowBlank (true|false) default false
30218 * @cfg {string} dayPlaceholder
30219 * @cfg {string} monthPlaceholder
30220 * @cfg {string} yearPlaceholder
30221 * @cfg {string} dayFormat default 'd'
30222 * @cfg {string} monthFormat default 'm'
30223 * @cfg {string} yearFormat default 'Y'
30224 * @cfg {Number} labellg set the width of label (1-12)
30225 * @cfg {Number} labelmd set the width of label (1-12)
30226 * @cfg {Number} labelsm set the width of label (1-12)
30227 * @cfg {Number} labelxs set the width of label (1-12)
30231 * Create a new DateSplitField
30232 * @param {Object} config The config object
30235 Roo.bootstrap.DateSplitField = function(config){
30236 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30242 * getting the data of years
30243 * @param {Roo.bootstrap.DateSplitField} this
30244 * @param {Object} years
30249 * getting the data of days
30250 * @param {Roo.bootstrap.DateSplitField} this
30251 * @param {Object} days
30256 * Fires after the field has been marked as invalid.
30257 * @param {Roo.form.Field} this
30258 * @param {String} msg The validation message
30263 * Fires after the field has been validated with no errors.
30264 * @param {Roo.form.Field} this
30270 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30273 labelAlign : 'top',
30275 dayAllowBlank : false,
30276 monthAllowBlank : false,
30277 yearAllowBlank : false,
30278 dayPlaceholder : '',
30279 monthPlaceholder : '',
30280 yearPlaceholder : '',
30284 isFormField : true,
30290 getAutoCreate : function()
30294 cls : 'row roo-date-split-field-group',
30299 cls : 'form-hidden-field roo-date-split-field-group-value',
30305 var labelCls = 'col-md-12';
30306 var contentCls = 'col-md-4';
30308 if(this.fieldLabel){
30312 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30316 html : this.fieldLabel
30321 if(this.labelAlign == 'left'){
30323 if(this.labelWidth > 12){
30324 label.style = "width: " + this.labelWidth + 'px';
30327 if(this.labelWidth < 13 && this.labelmd == 0){
30328 this.labelmd = this.labelWidth;
30331 if(this.labellg > 0){
30332 labelCls = ' col-lg-' + this.labellg;
30333 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30336 if(this.labelmd > 0){
30337 labelCls = ' col-md-' + this.labelmd;
30338 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30341 if(this.labelsm > 0){
30342 labelCls = ' col-sm-' + this.labelsm;
30343 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30346 if(this.labelxs > 0){
30347 labelCls = ' col-xs-' + this.labelxs;
30348 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30352 label.cls += ' ' + labelCls;
30354 cfg.cn.push(label);
30357 Roo.each(['day', 'month', 'year'], function(t){
30360 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30367 inputEl: function ()
30369 return this.el.select('.roo-date-split-field-group-value', true).first();
30372 onRender : function(ct, position)
30376 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30378 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30380 this.dayField = new Roo.bootstrap.ComboBox({
30381 allowBlank : this.dayAllowBlank,
30382 alwaysQuery : true,
30383 displayField : 'value',
30386 forceSelection : true,
30388 placeholder : this.dayPlaceholder,
30389 selectOnFocus : true,
30390 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30391 triggerAction : 'all',
30393 valueField : 'value',
30394 store : new Roo.data.SimpleStore({
30395 data : (function() {
30397 _this.fireEvent('days', _this, days);
30400 fields : [ 'value' ]
30403 select : function (_self, record, index)
30405 _this.setValue(_this.getValue());
30410 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30412 this.monthField = new Roo.bootstrap.MonthField({
30413 after : '<i class=\"fa fa-calendar\"></i>',
30414 allowBlank : this.monthAllowBlank,
30415 placeholder : this.monthPlaceholder,
30418 render : function (_self)
30420 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30421 e.preventDefault();
30425 select : function (_self, oldvalue, newvalue)
30427 _this.setValue(_this.getValue());
30432 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30434 this.yearField = new Roo.bootstrap.ComboBox({
30435 allowBlank : this.yearAllowBlank,
30436 alwaysQuery : true,
30437 displayField : 'value',
30440 forceSelection : true,
30442 placeholder : this.yearPlaceholder,
30443 selectOnFocus : true,
30444 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30445 triggerAction : 'all',
30447 valueField : 'value',
30448 store : new Roo.data.SimpleStore({
30449 data : (function() {
30451 _this.fireEvent('years', _this, years);
30454 fields : [ 'value' ]
30457 select : function (_self, record, index)
30459 _this.setValue(_this.getValue());
30464 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30467 setValue : function(v, format)
30469 this.inputEl.dom.value = v;
30471 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30473 var d = Date.parseDate(v, f);
30480 this.setDay(d.format(this.dayFormat));
30481 this.setMonth(d.format(this.monthFormat));
30482 this.setYear(d.format(this.yearFormat));
30489 setDay : function(v)
30491 this.dayField.setValue(v);
30492 this.inputEl.dom.value = this.getValue();
30497 setMonth : function(v)
30499 this.monthField.setValue(v, true);
30500 this.inputEl.dom.value = this.getValue();
30505 setYear : function(v)
30507 this.yearField.setValue(v);
30508 this.inputEl.dom.value = this.getValue();
30513 getDay : function()
30515 return this.dayField.getValue();
30518 getMonth : function()
30520 return this.monthField.getValue();
30523 getYear : function()
30525 return this.yearField.getValue();
30528 getValue : function()
30530 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30532 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30542 this.inputEl.dom.value = '';
30547 validate : function()
30549 var d = this.dayField.validate();
30550 var m = this.monthField.validate();
30551 var y = this.yearField.validate();
30556 (!this.dayAllowBlank && !d) ||
30557 (!this.monthAllowBlank && !m) ||
30558 (!this.yearAllowBlank && !y)
30563 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30572 this.markInvalid();
30577 markValid : function()
30580 var label = this.el.select('label', true).first();
30581 var icon = this.el.select('i.fa-star', true).first();
30587 this.fireEvent('valid', this);
30591 * Mark this field as invalid
30592 * @param {String} msg The validation message
30594 markInvalid : function(msg)
30597 var label = this.el.select('label', true).first();
30598 var icon = this.el.select('i.fa-star', true).first();
30600 if(label && !icon){
30601 this.el.select('.roo-date-split-field-label', true).createChild({
30603 cls : 'text-danger fa fa-lg fa-star',
30604 tooltip : 'This field is required',
30605 style : 'margin-right:5px;'
30609 this.fireEvent('invalid', this, msg);
30612 clearInvalid : function()
30614 var label = this.el.select('label', true).first();
30615 var icon = this.el.select('i.fa-star', true).first();
30621 this.fireEvent('valid', this);
30624 getName: function()
30634 * http://masonry.desandro.com
30636 * The idea is to render all the bricks based on vertical width...
30638 * The original code extends 'outlayer' - we might need to use that....
30644 * @class Roo.bootstrap.LayoutMasonry
30645 * @extends Roo.bootstrap.Component
30646 * Bootstrap Layout Masonry class
30649 * Create a new Element
30650 * @param {Object} config The config object
30653 Roo.bootstrap.LayoutMasonry = function(config){
30655 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30659 Roo.bootstrap.LayoutMasonry.register(this);
30665 * Fire after layout the items
30666 * @param {Roo.bootstrap.LayoutMasonry} this
30667 * @param {Roo.EventObject} e
30674 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30677 * @cfg {Boolean} isLayoutInstant = no animation?
30679 isLayoutInstant : false, // needed?
30682 * @cfg {Number} boxWidth width of the columns
30687 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30692 * @cfg {Number} padWidth padding below box..
30697 * @cfg {Number} gutter gutter width..
30702 * @cfg {Number} maxCols maximum number of columns
30708 * @cfg {Boolean} isAutoInitial defalut true
30710 isAutoInitial : true,
30715 * @cfg {Boolean} isHorizontal defalut false
30717 isHorizontal : false,
30719 currentSize : null,
30725 bricks: null, //CompositeElement
30729 _isLayoutInited : false,
30731 // isAlternative : false, // only use for vertical layout...
30734 * @cfg {Number} alternativePadWidth padding below box..
30736 alternativePadWidth : 50,
30738 selectedBrick : [],
30740 getAutoCreate : function(){
30742 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30746 cls: 'blog-masonary-wrapper ' + this.cls,
30748 cls : 'mas-boxes masonary'
30755 getChildContainer: function( )
30757 if (this.boxesEl) {
30758 return this.boxesEl;
30761 this.boxesEl = this.el.select('.mas-boxes').first();
30763 return this.boxesEl;
30767 initEvents : function()
30771 if(this.isAutoInitial){
30772 Roo.log('hook children rendered');
30773 this.on('childrenrendered', function() {
30774 Roo.log('children rendered');
30780 initial : function()
30782 this.selectedBrick = [];
30784 this.currentSize = this.el.getBox(true);
30786 Roo.EventManager.onWindowResize(this.resize, this);
30788 if(!this.isAutoInitial){
30796 //this.layout.defer(500,this);
30800 resize : function()
30802 var cs = this.el.getBox(true);
30805 this.currentSize.width == cs.width &&
30806 this.currentSize.x == cs.x &&
30807 this.currentSize.height == cs.height &&
30808 this.currentSize.y == cs.y
30810 Roo.log("no change in with or X or Y");
30814 this.currentSize = cs;
30820 layout : function()
30822 this._resetLayout();
30824 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30826 this.layoutItems( isInstant );
30828 this._isLayoutInited = true;
30830 this.fireEvent('layout', this);
30834 _resetLayout : function()
30836 if(this.isHorizontal){
30837 this.horizontalMeasureColumns();
30841 this.verticalMeasureColumns();
30845 verticalMeasureColumns : function()
30847 this.getContainerWidth();
30849 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30850 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30854 var boxWidth = this.boxWidth + this.padWidth;
30856 if(this.containerWidth < this.boxWidth){
30857 boxWidth = this.containerWidth
30860 var containerWidth = this.containerWidth;
30862 var cols = Math.floor(containerWidth / boxWidth);
30864 this.cols = Math.max( cols, 1 );
30866 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30868 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30870 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30872 this.colWidth = boxWidth + avail - this.padWidth;
30874 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30875 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30878 horizontalMeasureColumns : function()
30880 this.getContainerWidth();
30882 var boxWidth = this.boxWidth;
30884 if(this.containerWidth < boxWidth){
30885 boxWidth = this.containerWidth;
30888 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30890 this.el.setHeight(boxWidth);
30894 getContainerWidth : function()
30896 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30899 layoutItems : function( isInstant )
30901 Roo.log(this.bricks);
30903 var items = Roo.apply([], this.bricks);
30905 if(this.isHorizontal){
30906 this._horizontalLayoutItems( items , isInstant );
30910 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30911 // this._verticalAlternativeLayoutItems( items , isInstant );
30915 this._verticalLayoutItems( items , isInstant );
30919 _verticalLayoutItems : function ( items , isInstant)
30921 if ( !items || !items.length ) {
30926 ['xs', 'xs', 'xs', 'tall'],
30927 ['xs', 'xs', 'tall'],
30928 ['xs', 'xs', 'sm'],
30929 ['xs', 'xs', 'xs'],
30935 ['sm', 'xs', 'xs'],
30939 ['tall', 'xs', 'xs', 'xs'],
30940 ['tall', 'xs', 'xs'],
30952 Roo.each(items, function(item, k){
30954 switch (item.size) {
30955 // these layouts take up a full box,
30966 boxes.push([item]);
30989 var filterPattern = function(box, length)
30997 var pattern = box.slice(0, length);
31001 Roo.each(pattern, function(i){
31002 format.push(i.size);
31005 Roo.each(standard, function(s){
31007 if(String(s) != String(format)){
31016 if(!match && length == 1){
31021 filterPattern(box, length - 1);
31025 queue.push(pattern);
31027 box = box.slice(length, box.length);
31029 filterPattern(box, 4);
31035 Roo.each(boxes, function(box, k){
31041 if(box.length == 1){
31046 filterPattern(box, 4);
31050 this._processVerticalLayoutQueue( queue, isInstant );
31054 // _verticalAlternativeLayoutItems : function( items , isInstant )
31056 // if ( !items || !items.length ) {
31060 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31064 _horizontalLayoutItems : function ( items , isInstant)
31066 if ( !items || !items.length || items.length < 3) {
31072 var eItems = items.slice(0, 3);
31074 items = items.slice(3, items.length);
31077 ['xs', 'xs', 'xs', 'wide'],
31078 ['xs', 'xs', 'wide'],
31079 ['xs', 'xs', 'sm'],
31080 ['xs', 'xs', 'xs'],
31086 ['sm', 'xs', 'xs'],
31090 ['wide', 'xs', 'xs', 'xs'],
31091 ['wide', 'xs', 'xs'],
31104 Roo.each(items, function(item, k){
31106 switch (item.size) {
31117 boxes.push([item]);
31141 var filterPattern = function(box, length)
31149 var pattern = box.slice(0, length);
31153 Roo.each(pattern, function(i){
31154 format.push(i.size);
31157 Roo.each(standard, function(s){
31159 if(String(s) != String(format)){
31168 if(!match && length == 1){
31173 filterPattern(box, length - 1);
31177 queue.push(pattern);
31179 box = box.slice(length, box.length);
31181 filterPattern(box, 4);
31187 Roo.each(boxes, function(box, k){
31193 if(box.length == 1){
31198 filterPattern(box, 4);
31205 var pos = this.el.getBox(true);
31209 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31211 var hit_end = false;
31213 Roo.each(queue, function(box){
31217 Roo.each(box, function(b){
31219 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31229 Roo.each(box, function(b){
31231 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31234 mx = Math.max(mx, b.x);
31238 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31242 Roo.each(box, function(b){
31244 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31258 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31261 /** Sets position of item in DOM
31262 * @param {Element} item
31263 * @param {Number} x - horizontal position
31264 * @param {Number} y - vertical position
31265 * @param {Boolean} isInstant - disables transitions
31267 _processVerticalLayoutQueue : function( queue, isInstant )
31269 var pos = this.el.getBox(true);
31274 for (var i = 0; i < this.cols; i++){
31278 Roo.each(queue, function(box, k){
31280 var col = k % this.cols;
31282 Roo.each(box, function(b,kk){
31284 b.el.position('absolute');
31286 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31287 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31289 if(b.size == 'md-left' || b.size == 'md-right'){
31290 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31291 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31294 b.el.setWidth(width);
31295 b.el.setHeight(height);
31297 b.el.select('iframe',true).setSize(width,height);
31301 for (var i = 0; i < this.cols; i++){
31303 if(maxY[i] < maxY[col]){
31308 col = Math.min(col, i);
31312 x = pos.x + col * (this.colWidth + this.padWidth);
31316 var positions = [];
31318 switch (box.length){
31320 positions = this.getVerticalOneBoxColPositions(x, y, box);
31323 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31326 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31329 positions = this.getVerticalFourBoxColPositions(x, y, box);
31335 Roo.each(box, function(b,kk){
31337 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31339 var sz = b.el.getSize();
31341 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31349 for (var i = 0; i < this.cols; i++){
31350 mY = Math.max(mY, maxY[i]);
31353 this.el.setHeight(mY - pos.y);
31357 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31359 // var pos = this.el.getBox(true);
31362 // var maxX = pos.right;
31364 // var maxHeight = 0;
31366 // Roo.each(items, function(item, k){
31370 // item.el.position('absolute');
31372 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31374 // item.el.setWidth(width);
31376 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31378 // item.el.setHeight(height);
31381 // item.el.setXY([x, y], isInstant ? false : true);
31383 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31386 // y = y + height + this.alternativePadWidth;
31388 // maxHeight = maxHeight + height + this.alternativePadWidth;
31392 // this.el.setHeight(maxHeight);
31396 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31398 var pos = this.el.getBox(true);
31403 var maxX = pos.right;
31405 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31407 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31409 Roo.each(queue, function(box, k){
31411 Roo.each(box, function(b, kk){
31413 b.el.position('absolute');
31415 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31416 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31418 if(b.size == 'md-left' || b.size == 'md-right'){
31419 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31420 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31423 b.el.setWidth(width);
31424 b.el.setHeight(height);
31432 var positions = [];
31434 switch (box.length){
31436 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31439 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31442 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31445 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31451 Roo.each(box, function(b,kk){
31453 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31455 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31463 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31465 Roo.each(eItems, function(b,k){
31467 b.size = (k == 0) ? 'sm' : 'xs';
31468 b.x = (k == 0) ? 2 : 1;
31469 b.y = (k == 0) ? 2 : 1;
31471 b.el.position('absolute');
31473 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31475 b.el.setWidth(width);
31477 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31479 b.el.setHeight(height);
31483 var positions = [];
31486 x : maxX - this.unitWidth * 2 - this.gutter,
31491 x : maxX - this.unitWidth,
31492 y : minY + (this.unitWidth + this.gutter) * 2
31496 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31500 Roo.each(eItems, function(b,k){
31502 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31508 getVerticalOneBoxColPositions : function(x, y, box)
31512 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31514 if(box[0].size == 'md-left'){
31518 if(box[0].size == 'md-right'){
31523 x : x + (this.unitWidth + this.gutter) * rand,
31530 getVerticalTwoBoxColPositions : function(x, y, box)
31534 if(box[0].size == 'xs'){
31538 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31542 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31556 x : x + (this.unitWidth + this.gutter) * 2,
31557 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31564 getVerticalThreeBoxColPositions : function(x, y, box)
31568 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31576 x : x + (this.unitWidth + this.gutter) * 1,
31581 x : x + (this.unitWidth + this.gutter) * 2,
31589 if(box[0].size == 'xs' && box[1].size == 'xs'){
31598 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31602 x : x + (this.unitWidth + this.gutter) * 1,
31616 x : x + (this.unitWidth + this.gutter) * 2,
31621 x : x + (this.unitWidth + this.gutter) * 2,
31622 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31629 getVerticalFourBoxColPositions : function(x, y, box)
31633 if(box[0].size == 'xs'){
31642 y : y + (this.unitHeight + this.gutter) * 1
31647 y : y + (this.unitHeight + this.gutter) * 2
31651 x : x + (this.unitWidth + this.gutter) * 1,
31665 x : x + (this.unitWidth + this.gutter) * 2,
31670 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31671 y : y + (this.unitHeight + this.gutter) * 1
31675 x : x + (this.unitWidth + this.gutter) * 2,
31676 y : y + (this.unitWidth + this.gutter) * 2
31683 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31687 if(box[0].size == 'md-left'){
31689 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31696 if(box[0].size == 'md-right'){
31698 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31699 y : minY + (this.unitWidth + this.gutter) * 1
31705 var rand = Math.floor(Math.random() * (4 - box[0].y));
31708 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31709 y : minY + (this.unitWidth + this.gutter) * rand
31716 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31720 if(box[0].size == 'xs'){
31723 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31728 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31729 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31737 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31742 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31743 y : minY + (this.unitWidth + this.gutter) * 2
31750 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31754 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31757 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31762 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31763 y : minY + (this.unitWidth + this.gutter) * 1
31767 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31768 y : minY + (this.unitWidth + this.gutter) * 2
31775 if(box[0].size == 'xs' && box[1].size == 'xs'){
31778 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31783 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31788 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31789 y : minY + (this.unitWidth + this.gutter) * 1
31797 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31802 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31803 y : minY + (this.unitWidth + this.gutter) * 2
31807 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31808 y : minY + (this.unitWidth + this.gutter) * 2
31815 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31819 if(box[0].size == 'xs'){
31822 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31827 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31832 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),
31837 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31838 y : minY + (this.unitWidth + this.gutter) * 1
31846 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31851 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31852 y : minY + (this.unitWidth + this.gutter) * 2
31856 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31857 y : minY + (this.unitWidth + this.gutter) * 2
31861 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),
31862 y : minY + (this.unitWidth + this.gutter) * 2
31870 * remove a Masonry Brick
31871 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31873 removeBrick : function(brick_id)
31879 for (var i = 0; i<this.bricks.length; i++) {
31880 if (this.bricks[i].id == brick_id) {
31881 this.bricks.splice(i,1);
31882 this.el.dom.removeChild(Roo.get(brick_id).dom);
31889 * adds a Masonry Brick
31890 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31892 addBrick : function(cfg)
31894 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31895 //this.register(cn);
31896 cn.parentId = this.id;
31897 cn.onRender(this.el, null);
31902 * register a Masonry Brick
31903 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31906 register : function(brick)
31908 this.bricks.push(brick);
31909 brick.masonryId = this.id;
31913 * clear all the Masonry Brick
31915 clearAll : function()
31918 //this.getChildContainer().dom.innerHTML = "";
31919 this.el.dom.innerHTML = '';
31922 getSelected : function()
31924 if (!this.selectedBrick) {
31928 return this.selectedBrick;
31932 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31936 * register a Masonry Layout
31937 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31940 register : function(layout)
31942 this.groups[layout.id] = layout;
31945 * fetch a Masonry Layout based on the masonry layout ID
31946 * @param {string} the masonry layout to add
31947 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31950 get: function(layout_id) {
31951 if (typeof(this.groups[layout_id]) == 'undefined') {
31954 return this.groups[layout_id] ;
31966 * http://masonry.desandro.com
31968 * The idea is to render all the bricks based on vertical width...
31970 * The original code extends 'outlayer' - we might need to use that....
31976 * @class Roo.bootstrap.LayoutMasonryAuto
31977 * @extends Roo.bootstrap.Component
31978 * Bootstrap Layout Masonry class
31981 * Create a new Element
31982 * @param {Object} config The config object
31985 Roo.bootstrap.LayoutMasonryAuto = function(config){
31986 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31989 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31992 * @cfg {Boolean} isFitWidth - resize the width..
31994 isFitWidth : false, // options..
31996 * @cfg {Boolean} isOriginLeft = left align?
31998 isOriginLeft : true,
32000 * @cfg {Boolean} isOriginTop = top align?
32002 isOriginTop : false,
32004 * @cfg {Boolean} isLayoutInstant = no animation?
32006 isLayoutInstant : false, // needed?
32008 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32010 isResizingContainer : true,
32012 * @cfg {Number} columnWidth width of the columns
32018 * @cfg {Number} maxCols maximum number of columns
32023 * @cfg {Number} padHeight padding below box..
32029 * @cfg {Boolean} isAutoInitial defalut true
32032 isAutoInitial : true,
32038 initialColumnWidth : 0,
32039 currentSize : null,
32041 colYs : null, // array.
32048 bricks: null, //CompositeElement
32049 cols : 0, // array?
32050 // element : null, // wrapped now this.el
32051 _isLayoutInited : null,
32054 getAutoCreate : function(){
32058 cls: 'blog-masonary-wrapper ' + this.cls,
32060 cls : 'mas-boxes masonary'
32067 getChildContainer: function( )
32069 if (this.boxesEl) {
32070 return this.boxesEl;
32073 this.boxesEl = this.el.select('.mas-boxes').first();
32075 return this.boxesEl;
32079 initEvents : function()
32083 if(this.isAutoInitial){
32084 Roo.log('hook children rendered');
32085 this.on('childrenrendered', function() {
32086 Roo.log('children rendered');
32093 initial : function()
32095 this.reloadItems();
32097 this.currentSize = this.el.getBox(true);
32099 /// was window resize... - let's see if this works..
32100 Roo.EventManager.onWindowResize(this.resize, this);
32102 if(!this.isAutoInitial){
32107 this.layout.defer(500,this);
32110 reloadItems: function()
32112 this.bricks = this.el.select('.masonry-brick', true);
32114 this.bricks.each(function(b) {
32115 //Roo.log(b.getSize());
32116 if (!b.attr('originalwidth')) {
32117 b.attr('originalwidth', b.getSize().width);
32122 Roo.log(this.bricks.elements.length);
32125 resize : function()
32128 var cs = this.el.getBox(true);
32130 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32131 Roo.log("no change in with or X");
32134 this.currentSize = cs;
32138 layout : function()
32141 this._resetLayout();
32142 //this._manageStamps();
32144 // don't animate first layout
32145 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32146 this.layoutItems( isInstant );
32148 // flag for initalized
32149 this._isLayoutInited = true;
32152 layoutItems : function( isInstant )
32154 //var items = this._getItemsForLayout( this.items );
32155 // original code supports filtering layout items.. we just ignore it..
32157 this._layoutItems( this.bricks , isInstant );
32159 this._postLayout();
32161 _layoutItems : function ( items , isInstant)
32163 //this.fireEvent( 'layout', this, items );
32166 if ( !items || !items.elements.length ) {
32167 // no items, emit event with empty array
32172 items.each(function(item) {
32173 Roo.log("layout item");
32175 // get x/y object from method
32176 var position = this._getItemLayoutPosition( item );
32178 position.item = item;
32179 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32180 queue.push( position );
32183 this._processLayoutQueue( queue );
32185 /** Sets position of item in DOM
32186 * @param {Element} item
32187 * @param {Number} x - horizontal position
32188 * @param {Number} y - vertical position
32189 * @param {Boolean} isInstant - disables transitions
32191 _processLayoutQueue : function( queue )
32193 for ( var i=0, len = queue.length; i < len; i++ ) {
32194 var obj = queue[i];
32195 obj.item.position('absolute');
32196 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32202 * Any logic you want to do after each layout,
32203 * i.e. size the container
32205 _postLayout : function()
32207 this.resizeContainer();
32210 resizeContainer : function()
32212 if ( !this.isResizingContainer ) {
32215 var size = this._getContainerSize();
32217 this.el.setSize(size.width,size.height);
32218 this.boxesEl.setSize(size.width,size.height);
32224 _resetLayout : function()
32226 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32227 this.colWidth = this.el.getWidth();
32228 //this.gutter = this.el.getWidth();
32230 this.measureColumns();
32236 this.colYs.push( 0 );
32242 measureColumns : function()
32244 this.getContainerWidth();
32245 // if columnWidth is 0, default to outerWidth of first item
32246 if ( !this.columnWidth ) {
32247 var firstItem = this.bricks.first();
32248 Roo.log(firstItem);
32249 this.columnWidth = this.containerWidth;
32250 if (firstItem && firstItem.attr('originalwidth') ) {
32251 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32253 // columnWidth fall back to item of first element
32254 Roo.log("set column width?");
32255 this.initialColumnWidth = this.columnWidth ;
32257 // if first elem has no width, default to size of container
32262 if (this.initialColumnWidth) {
32263 this.columnWidth = this.initialColumnWidth;
32268 // column width is fixed at the top - however if container width get's smaller we should
32271 // this bit calcs how man columns..
32273 var columnWidth = this.columnWidth += this.gutter;
32275 // calculate columns
32276 var containerWidth = this.containerWidth + this.gutter;
32278 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32279 // fix rounding errors, typically with gutters
32280 var excess = columnWidth - containerWidth % columnWidth;
32283 // if overshoot is less than a pixel, round up, otherwise floor it
32284 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32285 cols = Math[ mathMethod ]( cols );
32286 this.cols = Math.max( cols, 1 );
32287 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32289 // padding positioning..
32290 var totalColWidth = this.cols * this.columnWidth;
32291 var padavail = this.containerWidth - totalColWidth;
32292 // so for 2 columns - we need 3 'pads'
32294 var padNeeded = (1+this.cols) * this.padWidth;
32296 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32298 this.columnWidth += padExtra
32299 //this.padWidth = Math.floor(padavail / ( this.cols));
32301 // adjust colum width so that padding is fixed??
32303 // we have 3 columns ... total = width * 3
32304 // we have X left over... that should be used by
32306 //if (this.expandC) {
32314 getContainerWidth : function()
32316 /* // container is parent if fit width
32317 var container = this.isFitWidth ? this.element.parentNode : this.element;
32318 // check that this.size and size are there
32319 // IE8 triggers resize on body size change, so they might not be
32321 var size = getSize( container ); //FIXME
32322 this.containerWidth = size && size.innerWidth; //FIXME
32325 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32329 _getItemLayoutPosition : function( item ) // what is item?
32331 // we resize the item to our columnWidth..
32333 item.setWidth(this.columnWidth);
32334 item.autoBoxAdjust = false;
32336 var sz = item.getSize();
32338 // how many columns does this brick span
32339 var remainder = this.containerWidth % this.columnWidth;
32341 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32342 // round if off by 1 pixel, otherwise use ceil
32343 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32344 colSpan = Math.min( colSpan, this.cols );
32346 // normally this should be '1' as we dont' currently allow multi width columns..
32348 var colGroup = this._getColGroup( colSpan );
32349 // get the minimum Y value from the columns
32350 var minimumY = Math.min.apply( Math, colGroup );
32351 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32353 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32355 // position the brick
32357 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32358 y: this.currentSize.y + minimumY + this.padHeight
32362 // apply setHeight to necessary columns
32363 var setHeight = minimumY + sz.height + this.padHeight;
32364 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32366 var setSpan = this.cols + 1 - colGroup.length;
32367 for ( var i = 0; i < setSpan; i++ ) {
32368 this.colYs[ shortColIndex + i ] = setHeight ;
32375 * @param {Number} colSpan - number of columns the element spans
32376 * @returns {Array} colGroup
32378 _getColGroup : function( colSpan )
32380 if ( colSpan < 2 ) {
32381 // if brick spans only one column, use all the column Ys
32386 // how many different places could this brick fit horizontally
32387 var groupCount = this.cols + 1 - colSpan;
32388 // for each group potential horizontal position
32389 for ( var i = 0; i < groupCount; i++ ) {
32390 // make an array of colY values for that one group
32391 var groupColYs = this.colYs.slice( i, i + colSpan );
32392 // and get the max value of the array
32393 colGroup[i] = Math.max.apply( Math, groupColYs );
32398 _manageStamp : function( stamp )
32400 var stampSize = stamp.getSize();
32401 var offset = stamp.getBox();
32402 // get the columns that this stamp affects
32403 var firstX = this.isOriginLeft ? offset.x : offset.right;
32404 var lastX = firstX + stampSize.width;
32405 var firstCol = Math.floor( firstX / this.columnWidth );
32406 firstCol = Math.max( 0, firstCol );
32408 var lastCol = Math.floor( lastX / this.columnWidth );
32409 // lastCol should not go over if multiple of columnWidth #425
32410 lastCol -= lastX % this.columnWidth ? 0 : 1;
32411 lastCol = Math.min( this.cols - 1, lastCol );
32413 // set colYs to bottom of the stamp
32414 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32417 for ( var i = firstCol; i <= lastCol; i++ ) {
32418 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32423 _getContainerSize : function()
32425 this.maxY = Math.max.apply( Math, this.colYs );
32430 if ( this.isFitWidth ) {
32431 size.width = this._getContainerFitWidth();
32437 _getContainerFitWidth : function()
32439 var unusedCols = 0;
32440 // count unused columns
32443 if ( this.colYs[i] !== 0 ) {
32448 // fit container to columns that have been used
32449 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32452 needsResizeLayout : function()
32454 var previousWidth = this.containerWidth;
32455 this.getContainerWidth();
32456 return previousWidth !== this.containerWidth;
32471 * @class Roo.bootstrap.MasonryBrick
32472 * @extends Roo.bootstrap.Component
32473 * Bootstrap MasonryBrick class
32476 * Create a new MasonryBrick
32477 * @param {Object} config The config object
32480 Roo.bootstrap.MasonryBrick = function(config){
32482 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32484 Roo.bootstrap.MasonryBrick.register(this);
32490 * When a MasonryBrick is clcik
32491 * @param {Roo.bootstrap.MasonryBrick} this
32492 * @param {Roo.EventObject} e
32498 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32501 * @cfg {String} title
32505 * @cfg {String} html
32509 * @cfg {String} bgimage
32513 * @cfg {String} videourl
32517 * @cfg {String} cls
32521 * @cfg {String} href
32525 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32530 * @cfg {String} placetitle (center|bottom)
32535 * @cfg {Boolean} isFitContainer defalut true
32537 isFitContainer : true,
32540 * @cfg {Boolean} preventDefault defalut false
32542 preventDefault : false,
32545 * @cfg {Boolean} inverse defalut false
32547 maskInverse : false,
32549 getAutoCreate : function()
32551 if(!this.isFitContainer){
32552 return this.getSplitAutoCreate();
32555 var cls = 'masonry-brick masonry-brick-full';
32557 if(this.href.length){
32558 cls += ' masonry-brick-link';
32561 if(this.bgimage.length){
32562 cls += ' masonry-brick-image';
32565 if(this.maskInverse){
32566 cls += ' mask-inverse';
32569 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32570 cls += ' enable-mask';
32574 cls += ' masonry-' + this.size + '-brick';
32577 if(this.placetitle.length){
32579 switch (this.placetitle) {
32581 cls += ' masonry-center-title';
32584 cls += ' masonry-bottom-title';
32591 if(!this.html.length && !this.bgimage.length){
32592 cls += ' masonry-center-title';
32595 if(!this.html.length && this.bgimage.length){
32596 cls += ' masonry-bottom-title';
32601 cls += ' ' + this.cls;
32605 tag: (this.href.length) ? 'a' : 'div',
32610 cls: 'masonry-brick-mask'
32614 cls: 'masonry-brick-paragraph',
32620 if(this.href.length){
32621 cfg.href = this.href;
32624 var cn = cfg.cn[1].cn;
32626 if(this.title.length){
32629 cls: 'masonry-brick-title',
32634 if(this.html.length){
32637 cls: 'masonry-brick-text',
32642 if (!this.title.length && !this.html.length) {
32643 cfg.cn[1].cls += ' hide';
32646 if(this.bgimage.length){
32649 cls: 'masonry-brick-image-view',
32654 if(this.videourl.length){
32655 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32656 // youtube support only?
32659 cls: 'masonry-brick-image-view',
32662 allowfullscreen : true
32670 getSplitAutoCreate : function()
32672 var cls = 'masonry-brick masonry-brick-split';
32674 if(this.href.length){
32675 cls += ' masonry-brick-link';
32678 if(this.bgimage.length){
32679 cls += ' masonry-brick-image';
32683 cls += ' masonry-' + this.size + '-brick';
32686 switch (this.placetitle) {
32688 cls += ' masonry-center-title';
32691 cls += ' masonry-bottom-title';
32694 if(!this.bgimage.length){
32695 cls += ' masonry-center-title';
32698 if(this.bgimage.length){
32699 cls += ' masonry-bottom-title';
32705 cls += ' ' + this.cls;
32709 tag: (this.href.length) ? 'a' : 'div',
32714 cls: 'masonry-brick-split-head',
32718 cls: 'masonry-brick-paragraph',
32725 cls: 'masonry-brick-split-body',
32731 if(this.href.length){
32732 cfg.href = this.href;
32735 if(this.title.length){
32736 cfg.cn[0].cn[0].cn.push({
32738 cls: 'masonry-brick-title',
32743 if(this.html.length){
32744 cfg.cn[1].cn.push({
32746 cls: 'masonry-brick-text',
32751 if(this.bgimage.length){
32752 cfg.cn[0].cn.push({
32754 cls: 'masonry-brick-image-view',
32759 if(this.videourl.length){
32760 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32761 // youtube support only?
32762 cfg.cn[0].cn.cn.push({
32764 cls: 'masonry-brick-image-view',
32767 allowfullscreen : true
32774 initEvents: function()
32776 switch (this.size) {
32809 this.el.on('touchstart', this.onTouchStart, this);
32810 this.el.on('touchmove', this.onTouchMove, this);
32811 this.el.on('touchend', this.onTouchEnd, this);
32812 this.el.on('contextmenu', this.onContextMenu, this);
32814 this.el.on('mouseenter' ,this.enter, this);
32815 this.el.on('mouseleave', this.leave, this);
32816 this.el.on('click', this.onClick, this);
32819 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32820 this.parent().bricks.push(this);
32825 onClick: function(e, el)
32827 var time = this.endTimer - this.startTimer;
32828 // Roo.log(e.preventDefault());
32831 e.preventDefault();
32836 if(!this.preventDefault){
32840 e.preventDefault();
32842 if (this.activeClass != '') {
32843 this.selectBrick();
32846 this.fireEvent('click', this, e);
32849 enter: function(e, el)
32851 e.preventDefault();
32853 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32857 if(this.bgimage.length && this.html.length){
32858 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32862 leave: function(e, el)
32864 e.preventDefault();
32866 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32870 if(this.bgimage.length && this.html.length){
32871 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32875 onTouchStart: function(e, el)
32877 // e.preventDefault();
32879 this.touchmoved = false;
32881 if(!this.isFitContainer){
32885 if(!this.bgimage.length || !this.html.length){
32889 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32891 this.timer = new Date().getTime();
32895 onTouchMove: function(e, el)
32897 this.touchmoved = true;
32900 onContextMenu : function(e,el)
32902 e.preventDefault();
32903 e.stopPropagation();
32907 onTouchEnd: function(e, el)
32909 // e.preventDefault();
32911 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32918 if(!this.bgimage.length || !this.html.length){
32920 if(this.href.length){
32921 window.location.href = this.href;
32927 if(!this.isFitContainer){
32931 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32933 window.location.href = this.href;
32936 //selection on single brick only
32937 selectBrick : function() {
32939 if (!this.parentId) {
32943 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32944 var index = m.selectedBrick.indexOf(this.id);
32947 m.selectedBrick.splice(index,1);
32948 this.el.removeClass(this.activeClass);
32952 for(var i = 0; i < m.selectedBrick.length; i++) {
32953 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32954 b.el.removeClass(b.activeClass);
32957 m.selectedBrick = [];
32959 m.selectedBrick.push(this.id);
32960 this.el.addClass(this.activeClass);
32964 isSelected : function(){
32965 return this.el.hasClass(this.activeClass);
32970 Roo.apply(Roo.bootstrap.MasonryBrick, {
32973 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32975 * register a Masonry Brick
32976 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32979 register : function(brick)
32981 //this.groups[brick.id] = brick;
32982 this.groups.add(brick.id, brick);
32985 * fetch a masonry brick based on the masonry brick ID
32986 * @param {string} the masonry brick to add
32987 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32990 get: function(brick_id)
32992 // if (typeof(this.groups[brick_id]) == 'undefined') {
32995 // return this.groups[brick_id] ;
32997 if(this.groups.key(brick_id)) {
32998 return this.groups.key(brick_id);
33016 * @class Roo.bootstrap.Brick
33017 * @extends Roo.bootstrap.Component
33018 * Bootstrap Brick class
33021 * Create a new Brick
33022 * @param {Object} config The config object
33025 Roo.bootstrap.Brick = function(config){
33026 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33032 * When a Brick is click
33033 * @param {Roo.bootstrap.Brick} this
33034 * @param {Roo.EventObject} e
33040 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33043 * @cfg {String} title
33047 * @cfg {String} html
33051 * @cfg {String} bgimage
33055 * @cfg {String} cls
33059 * @cfg {String} href
33063 * @cfg {String} video
33067 * @cfg {Boolean} square
33071 getAutoCreate : function()
33073 var cls = 'roo-brick';
33075 if(this.href.length){
33076 cls += ' roo-brick-link';
33079 if(this.bgimage.length){
33080 cls += ' roo-brick-image';
33083 if(!this.html.length && !this.bgimage.length){
33084 cls += ' roo-brick-center-title';
33087 if(!this.html.length && this.bgimage.length){
33088 cls += ' roo-brick-bottom-title';
33092 cls += ' ' + this.cls;
33096 tag: (this.href.length) ? 'a' : 'div',
33101 cls: 'roo-brick-paragraph',
33107 if(this.href.length){
33108 cfg.href = this.href;
33111 var cn = cfg.cn[0].cn;
33113 if(this.title.length){
33116 cls: 'roo-brick-title',
33121 if(this.html.length){
33124 cls: 'roo-brick-text',
33131 if(this.bgimage.length){
33134 cls: 'roo-brick-image-view',
33142 initEvents: function()
33144 if(this.title.length || this.html.length){
33145 this.el.on('mouseenter' ,this.enter, this);
33146 this.el.on('mouseleave', this.leave, this);
33149 Roo.EventManager.onWindowResize(this.resize, this);
33151 if(this.bgimage.length){
33152 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33153 this.imageEl.on('load', this.onImageLoad, this);
33160 onImageLoad : function()
33165 resize : function()
33167 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33169 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33171 if(this.bgimage.length){
33172 var image = this.el.select('.roo-brick-image-view', true).first();
33174 image.setWidth(paragraph.getWidth());
33177 image.setHeight(paragraph.getWidth());
33180 this.el.setHeight(image.getHeight());
33181 paragraph.setHeight(image.getHeight());
33187 enter: function(e, el)
33189 e.preventDefault();
33191 if(this.bgimage.length){
33192 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33193 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33197 leave: function(e, el)
33199 e.preventDefault();
33201 if(this.bgimage.length){
33202 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33203 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33218 * @class Roo.bootstrap.NumberField
33219 * @extends Roo.bootstrap.Input
33220 * Bootstrap NumberField class
33226 * Create a new NumberField
33227 * @param {Object} config The config object
33230 Roo.bootstrap.NumberField = function(config){
33231 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33234 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33237 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33239 allowDecimals : true,
33241 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33243 decimalSeparator : ".",
33245 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33247 decimalPrecision : 2,
33249 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33251 allowNegative : true,
33254 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33258 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33260 minValue : Number.NEGATIVE_INFINITY,
33262 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33264 maxValue : Number.MAX_VALUE,
33266 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33268 minText : "The minimum value for this field is {0}",
33270 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33272 maxText : "The maximum value for this field is {0}",
33274 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33275 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33277 nanText : "{0} is not a valid number",
33279 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33281 thousandsDelimiter : false,
33283 * @cfg {String} valueAlign alignment of value
33285 valueAlign : "left",
33287 getAutoCreate : function()
33289 var hiddenInput = {
33293 cls: 'hidden-number-input'
33297 hiddenInput.name = this.name;
33302 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33304 this.name = hiddenInput.name;
33306 if(cfg.cn.length > 0) {
33307 cfg.cn.push(hiddenInput);
33314 initEvents : function()
33316 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33318 var allowed = "0123456789";
33320 if(this.allowDecimals){
33321 allowed += this.decimalSeparator;
33324 if(this.allowNegative){
33328 if(this.thousandsDelimiter) {
33332 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33334 var keyPress = function(e){
33336 var k = e.getKey();
33338 var c = e.getCharCode();
33341 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33342 allowed.indexOf(String.fromCharCode(c)) === -1
33348 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33352 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33357 this.el.on("keypress", keyPress, this);
33360 validateValue : function(value)
33363 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33367 var num = this.parseValue(value);
33370 this.markInvalid(String.format(this.nanText, value));
33374 if(num < this.minValue){
33375 this.markInvalid(String.format(this.minText, this.minValue));
33379 if(num > this.maxValue){
33380 this.markInvalid(String.format(this.maxText, this.maxValue));
33387 getValue : function()
33389 var v = this.hiddenEl().getValue();
33391 return this.fixPrecision(this.parseValue(v));
33394 parseValue : function(value)
33396 if(this.thousandsDelimiter) {
33398 r = new RegExp(",", "g");
33399 value = value.replace(r, "");
33402 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33403 return isNaN(value) ? '' : value;
33406 fixPrecision : function(value)
33408 if(this.thousandsDelimiter) {
33410 r = new RegExp(",", "g");
33411 value = value.replace(r, "");
33414 var nan = isNaN(value);
33416 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33417 return nan ? '' : value;
33419 return parseFloat(value).toFixed(this.decimalPrecision);
33422 setValue : function(v)
33424 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33430 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33432 this.inputEl().dom.value = (v == '') ? '' :
33433 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33435 if(!this.allowZero && v === '0') {
33436 this.hiddenEl().dom.value = '';
33437 this.inputEl().dom.value = '';
33444 decimalPrecisionFcn : function(v)
33446 return Math.floor(v);
33449 beforeBlur : function()
33451 var v = this.parseValue(this.getRawValue());
33453 if(v || v === 0 || v === ''){
33458 hiddenEl : function()
33460 return this.el.select('input.hidden-number-input',true).first();
33472 * @class Roo.bootstrap.DocumentSlider
33473 * @extends Roo.bootstrap.Component
33474 * Bootstrap DocumentSlider class
33477 * Create a new DocumentViewer
33478 * @param {Object} config The config object
33481 Roo.bootstrap.DocumentSlider = function(config){
33482 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33489 * Fire after initEvent
33490 * @param {Roo.bootstrap.DocumentSlider} this
33495 * Fire after update
33496 * @param {Roo.bootstrap.DocumentSlider} this
33502 * @param {Roo.bootstrap.DocumentSlider} this
33508 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33514 getAutoCreate : function()
33518 cls : 'roo-document-slider',
33522 cls : 'roo-document-slider-header',
33526 cls : 'roo-document-slider-header-title'
33532 cls : 'roo-document-slider-body',
33536 cls : 'roo-document-slider-prev',
33540 cls : 'fa fa-chevron-left'
33546 cls : 'roo-document-slider-thumb',
33550 cls : 'roo-document-slider-image'
33556 cls : 'roo-document-slider-next',
33560 cls : 'fa fa-chevron-right'
33572 initEvents : function()
33574 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33575 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33577 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33578 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33580 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33581 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33583 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33584 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33586 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33587 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33589 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33590 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33592 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33593 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33595 this.thumbEl.on('click', this.onClick, this);
33597 this.prevIndicator.on('click', this.prev, this);
33599 this.nextIndicator.on('click', this.next, this);
33603 initial : function()
33605 if(this.files.length){
33606 this.indicator = 1;
33610 this.fireEvent('initial', this);
33613 update : function()
33615 this.imageEl.attr('src', this.files[this.indicator - 1]);
33617 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33619 this.prevIndicator.show();
33621 if(this.indicator == 1){
33622 this.prevIndicator.hide();
33625 this.nextIndicator.show();
33627 if(this.indicator == this.files.length){
33628 this.nextIndicator.hide();
33631 this.thumbEl.scrollTo('top');
33633 this.fireEvent('update', this);
33636 onClick : function(e)
33638 e.preventDefault();
33640 this.fireEvent('click', this);
33645 e.preventDefault();
33647 this.indicator = Math.max(1, this.indicator - 1);
33654 e.preventDefault();
33656 this.indicator = Math.min(this.files.length, this.indicator + 1);
33670 * @class Roo.bootstrap.RadioSet
33671 * @extends Roo.bootstrap.Input
33672 * Bootstrap RadioSet class
33673 * @cfg {String} indicatorpos (left|right) default left
33674 * @cfg {Boolean} inline (true|false) inline the element (default true)
33675 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33677 * Create a new RadioSet
33678 * @param {Object} config The config object
33681 Roo.bootstrap.RadioSet = function(config){
33683 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33687 Roo.bootstrap.RadioSet.register(this);
33692 * Fires when the element is checked or unchecked.
33693 * @param {Roo.bootstrap.RadioSet} this This radio
33694 * @param {Roo.bootstrap.Radio} item The checked item
33699 * Fires when the element is click.
33700 * @param {Roo.bootstrap.RadioSet} this This radio set
33701 * @param {Roo.bootstrap.Radio} item The checked item
33702 * @param {Roo.EventObject} e The event object
33709 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33717 indicatorpos : 'left',
33719 getAutoCreate : function()
33723 cls : 'roo-radio-set-label',
33727 html : this.fieldLabel
33732 if(this.indicatorpos == 'left'){
33735 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33736 tooltip : 'This field is required'
33741 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33742 tooltip : 'This field is required'
33748 cls : 'roo-radio-set-items'
33751 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33753 if (align === 'left' && this.fieldLabel.length) {
33756 cls : "roo-radio-set-right",
33762 if(this.labelWidth > 12){
33763 label.style = "width: " + this.labelWidth + 'px';
33766 if(this.labelWidth < 13 && this.labelmd == 0){
33767 this.labelmd = this.labelWidth;
33770 if(this.labellg > 0){
33771 label.cls += ' col-lg-' + this.labellg;
33772 items.cls += ' col-lg-' + (12 - this.labellg);
33775 if(this.labelmd > 0){
33776 label.cls += ' col-md-' + this.labelmd;
33777 items.cls += ' col-md-' + (12 - this.labelmd);
33780 if(this.labelsm > 0){
33781 label.cls += ' col-sm-' + this.labelsm;
33782 items.cls += ' col-sm-' + (12 - this.labelsm);
33785 if(this.labelxs > 0){
33786 label.cls += ' col-xs-' + this.labelxs;
33787 items.cls += ' col-xs-' + (12 - this.labelxs);
33793 cls : 'roo-radio-set',
33797 cls : 'roo-radio-set-input',
33800 value : this.value ? this.value : ''
33807 if(this.weight.length){
33808 cfg.cls += ' roo-radio-' + this.weight;
33812 cfg.cls += ' roo-radio-set-inline';
33816 ['xs','sm','md','lg'].map(function(size){
33817 if (settings[size]) {
33818 cfg.cls += ' col-' + size + '-' + settings[size];
33826 initEvents : function()
33828 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33829 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33831 if(!this.fieldLabel.length){
33832 this.labelEl.hide();
33835 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33836 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33838 this.indicator = this.indicatorEl();
33840 if(this.indicator){
33841 this.indicator.addClass('invisible');
33844 this.originalValue = this.getValue();
33848 inputEl: function ()
33850 return this.el.select('.roo-radio-set-input', true).first();
33853 getChildContainer : function()
33855 return this.itemsEl;
33858 register : function(item)
33860 this.radioes.push(item);
33864 validate : function()
33866 if(this.getVisibilityEl().hasClass('hidden')){
33872 Roo.each(this.radioes, function(i){
33881 if(this.allowBlank) {
33885 if(this.disabled || valid){
33890 this.markInvalid();
33895 markValid : function()
33897 if(this.labelEl.isVisible(true)){
33898 this.indicatorEl().removeClass('visible');
33899 this.indicatorEl().addClass('invisible');
33902 this.el.removeClass([this.invalidClass, this.validClass]);
33903 this.el.addClass(this.validClass);
33905 this.fireEvent('valid', this);
33908 markInvalid : function(msg)
33910 if(this.allowBlank || this.disabled){
33914 if(this.labelEl.isVisible(true)){
33915 this.indicatorEl().removeClass('invisible');
33916 this.indicatorEl().addClass('visible');
33919 this.el.removeClass([this.invalidClass, this.validClass]);
33920 this.el.addClass(this.invalidClass);
33922 this.fireEvent('invalid', this, msg);
33926 setValue : function(v, suppressEvent)
33928 if(this.value === v){
33935 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33938 Roo.each(this.radioes, function(i){
33940 i.el.removeClass('checked');
33943 Roo.each(this.radioes, function(i){
33945 if(i.value === v || i.value.toString() === v.toString()){
33947 i.el.addClass('checked');
33949 if(suppressEvent !== true){
33950 this.fireEvent('check', this, i);
33961 clearInvalid : function(){
33963 if(!this.el || this.preventMark){
33967 this.el.removeClass([this.invalidClass]);
33969 this.fireEvent('valid', this);
33974 Roo.apply(Roo.bootstrap.RadioSet, {
33978 register : function(set)
33980 this.groups[set.name] = set;
33983 get: function(name)
33985 if (typeof(this.groups[name]) == 'undefined') {
33989 return this.groups[name] ;
33995 * Ext JS Library 1.1.1
33996 * Copyright(c) 2006-2007, Ext JS, LLC.
33998 * Originally Released Under LGPL - original licence link has changed is not relivant.
34001 * <script type="text/javascript">
34006 * @class Roo.bootstrap.SplitBar
34007 * @extends Roo.util.Observable
34008 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34012 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34013 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34014 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34015 split.minSize = 100;
34016 split.maxSize = 600;
34017 split.animate = true;
34018 split.on('moved', splitterMoved);
34021 * Create a new SplitBar
34022 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34023 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34024 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34025 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34026 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34027 position of the SplitBar).
34029 Roo.bootstrap.SplitBar = function(cfg){
34034 // dragElement : elm
34035 // resizingElement: el,
34037 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34038 // placement : Roo.bootstrap.SplitBar.LEFT ,
34039 // existingProxy ???
34042 this.el = Roo.get(cfg.dragElement, true);
34043 this.el.dom.unselectable = "on";
34045 this.resizingEl = Roo.get(cfg.resizingElement, true);
34049 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34050 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34053 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34056 * The minimum size of the resizing element. (Defaults to 0)
34062 * The maximum size of the resizing element. (Defaults to 2000)
34065 this.maxSize = 2000;
34068 * Whether to animate the transition to the new size
34071 this.animate = false;
34074 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34077 this.useShim = false;
34082 if(!cfg.existingProxy){
34084 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34086 this.proxy = Roo.get(cfg.existingProxy).dom;
34089 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34092 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34095 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34098 this.dragSpecs = {};
34101 * @private The adapter to use to positon and resize elements
34103 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34104 this.adapter.init(this);
34106 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34108 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34109 this.el.addClass("roo-splitbar-h");
34112 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34113 this.el.addClass("roo-splitbar-v");
34119 * Fires when the splitter is moved (alias for {@link #event-moved})
34120 * @param {Roo.bootstrap.SplitBar} this
34121 * @param {Number} newSize the new width or height
34126 * Fires when the splitter is moved
34127 * @param {Roo.bootstrap.SplitBar} this
34128 * @param {Number} newSize the new width or height
34132 * @event beforeresize
34133 * Fires before the splitter is dragged
34134 * @param {Roo.bootstrap.SplitBar} this
34136 "beforeresize" : true,
34138 "beforeapply" : true
34141 Roo.util.Observable.call(this);
34144 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34145 onStartProxyDrag : function(x, y){
34146 this.fireEvent("beforeresize", this);
34148 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34150 o.enableDisplayMode("block");
34151 // all splitbars share the same overlay
34152 Roo.bootstrap.SplitBar.prototype.overlay = o;
34154 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34155 this.overlay.show();
34156 Roo.get(this.proxy).setDisplayed("block");
34157 var size = this.adapter.getElementSize(this);
34158 this.activeMinSize = this.getMinimumSize();;
34159 this.activeMaxSize = this.getMaximumSize();;
34160 var c1 = size - this.activeMinSize;
34161 var c2 = Math.max(this.activeMaxSize - size, 0);
34162 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34163 this.dd.resetConstraints();
34164 this.dd.setXConstraint(
34165 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34166 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34168 this.dd.setYConstraint(0, 0);
34170 this.dd.resetConstraints();
34171 this.dd.setXConstraint(0, 0);
34172 this.dd.setYConstraint(
34173 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34174 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34177 this.dragSpecs.startSize = size;
34178 this.dragSpecs.startPoint = [x, y];
34179 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34183 * @private Called after the drag operation by the DDProxy
34185 onEndProxyDrag : function(e){
34186 Roo.get(this.proxy).setDisplayed(false);
34187 var endPoint = Roo.lib.Event.getXY(e);
34189 this.overlay.hide();
34192 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34193 newSize = this.dragSpecs.startSize +
34194 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34195 endPoint[0] - this.dragSpecs.startPoint[0] :
34196 this.dragSpecs.startPoint[0] - endPoint[0]
34199 newSize = this.dragSpecs.startSize +
34200 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34201 endPoint[1] - this.dragSpecs.startPoint[1] :
34202 this.dragSpecs.startPoint[1] - endPoint[1]
34205 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34206 if(newSize != this.dragSpecs.startSize){
34207 if(this.fireEvent('beforeapply', this, newSize) !== false){
34208 this.adapter.setElementSize(this, newSize);
34209 this.fireEvent("moved", this, newSize);
34210 this.fireEvent("resize", this, newSize);
34216 * Get the adapter this SplitBar uses
34217 * @return The adapter object
34219 getAdapter : function(){
34220 return this.adapter;
34224 * Set the adapter this SplitBar uses
34225 * @param {Object} adapter A SplitBar adapter object
34227 setAdapter : function(adapter){
34228 this.adapter = adapter;
34229 this.adapter.init(this);
34233 * Gets the minimum size for the resizing element
34234 * @return {Number} The minimum size
34236 getMinimumSize : function(){
34237 return this.minSize;
34241 * Sets the minimum size for the resizing element
34242 * @param {Number} minSize The minimum size
34244 setMinimumSize : function(minSize){
34245 this.minSize = minSize;
34249 * Gets the maximum size for the resizing element
34250 * @return {Number} The maximum size
34252 getMaximumSize : function(){
34253 return this.maxSize;
34257 * Sets the maximum size for the resizing element
34258 * @param {Number} maxSize The maximum size
34260 setMaximumSize : function(maxSize){
34261 this.maxSize = maxSize;
34265 * Sets the initialize size for the resizing element
34266 * @param {Number} size The initial size
34268 setCurrentSize : function(size){
34269 var oldAnimate = this.animate;
34270 this.animate = false;
34271 this.adapter.setElementSize(this, size);
34272 this.animate = oldAnimate;
34276 * Destroy this splitbar.
34277 * @param {Boolean} removeEl True to remove the element
34279 destroy : function(removeEl){
34281 this.shim.remove();
34284 this.proxy.parentNode.removeChild(this.proxy);
34292 * @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.
34294 Roo.bootstrap.SplitBar.createProxy = function(dir){
34295 var proxy = new Roo.Element(document.createElement("div"));
34296 proxy.unselectable();
34297 var cls = 'roo-splitbar-proxy';
34298 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34299 document.body.appendChild(proxy.dom);
34304 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34305 * Default Adapter. It assumes the splitter and resizing element are not positioned
34306 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34308 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34311 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34312 // do nothing for now
34313 init : function(s){
34317 * Called before drag operations to get the current size of the resizing element.
34318 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34320 getElementSize : function(s){
34321 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34322 return s.resizingEl.getWidth();
34324 return s.resizingEl.getHeight();
34329 * Called after drag operations to set the size of the resizing element.
34330 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34331 * @param {Number} newSize The new size to set
34332 * @param {Function} onComplete A function to be invoked when resizing is complete
34334 setElementSize : function(s, newSize, onComplete){
34335 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34337 s.resizingEl.setWidth(newSize);
34339 onComplete(s, newSize);
34342 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34347 s.resizingEl.setHeight(newSize);
34349 onComplete(s, newSize);
34352 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34359 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34360 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34361 * Adapter that moves the splitter element to align with the resized sizing element.
34362 * Used with an absolute positioned SplitBar.
34363 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34364 * document.body, make sure you assign an id to the body element.
34366 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34367 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34368 this.container = Roo.get(container);
34371 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34372 init : function(s){
34373 this.basic.init(s);
34376 getElementSize : function(s){
34377 return this.basic.getElementSize(s);
34380 setElementSize : function(s, newSize, onComplete){
34381 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34384 moveSplitter : function(s){
34385 var yes = Roo.bootstrap.SplitBar;
34386 switch(s.placement){
34388 s.el.setX(s.resizingEl.getRight());
34391 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34394 s.el.setY(s.resizingEl.getBottom());
34397 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34404 * Orientation constant - Create a vertical SplitBar
34408 Roo.bootstrap.SplitBar.VERTICAL = 1;
34411 * Orientation constant - Create a horizontal SplitBar
34415 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34418 * Placement constant - The resizing element is to the left of the splitter element
34422 Roo.bootstrap.SplitBar.LEFT = 1;
34425 * Placement constant - The resizing element is to the right of the splitter element
34429 Roo.bootstrap.SplitBar.RIGHT = 2;
34432 * Placement constant - The resizing element is positioned above the splitter element
34436 Roo.bootstrap.SplitBar.TOP = 3;
34439 * Placement constant - The resizing element is positioned under splitter element
34443 Roo.bootstrap.SplitBar.BOTTOM = 4;
34444 Roo.namespace("Roo.bootstrap.layout");/*
34446 * Ext JS Library 1.1.1
34447 * Copyright(c) 2006-2007, Ext JS, LLC.
34449 * Originally Released Under LGPL - original licence link has changed is not relivant.
34452 * <script type="text/javascript">
34456 * @class Roo.bootstrap.layout.Manager
34457 * @extends Roo.bootstrap.Component
34458 * Base class for layout managers.
34460 Roo.bootstrap.layout.Manager = function(config)
34462 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34468 /** false to disable window resize monitoring @type Boolean */
34469 this.monitorWindowResize = true;
34474 * Fires when a layout is performed.
34475 * @param {Roo.LayoutManager} this
34479 * @event regionresized
34480 * Fires when the user resizes a region.
34481 * @param {Roo.LayoutRegion} region The resized region
34482 * @param {Number} newSize The new size (width for east/west, height for north/south)
34484 "regionresized" : true,
34486 * @event regioncollapsed
34487 * Fires when a region is collapsed.
34488 * @param {Roo.LayoutRegion} region The collapsed region
34490 "regioncollapsed" : true,
34492 * @event regionexpanded
34493 * Fires when a region is expanded.
34494 * @param {Roo.LayoutRegion} region The expanded region
34496 "regionexpanded" : true
34498 this.updating = false;
34501 this.el = Roo.get(config.el);
34507 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34512 monitorWindowResize : true,
34518 onRender : function(ct, position)
34521 this.el = Roo.get(ct);
34524 //this.fireEvent('render',this);
34528 initEvents: function()
34532 // ie scrollbar fix
34533 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34534 document.body.scroll = "no";
34535 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34536 this.el.position('relative');
34538 this.id = this.el.id;
34539 this.el.addClass("roo-layout-container");
34540 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34541 if(this.el.dom != document.body ) {
34542 this.el.on('resize', this.layout,this);
34543 this.el.on('show', this.layout,this);
34549 * Returns true if this layout is currently being updated
34550 * @return {Boolean}
34552 isUpdating : function(){
34553 return this.updating;
34557 * Suspend the LayoutManager from doing auto-layouts while
34558 * making multiple add or remove calls
34560 beginUpdate : function(){
34561 this.updating = true;
34565 * Restore auto-layouts and optionally disable the manager from performing a layout
34566 * @param {Boolean} noLayout true to disable a layout update
34568 endUpdate : function(noLayout){
34569 this.updating = false;
34575 layout: function(){
34579 onRegionResized : function(region, newSize){
34580 this.fireEvent("regionresized", region, newSize);
34584 onRegionCollapsed : function(region){
34585 this.fireEvent("regioncollapsed", region);
34588 onRegionExpanded : function(region){
34589 this.fireEvent("regionexpanded", region);
34593 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34594 * performs box-model adjustments.
34595 * @return {Object} The size as an object {width: (the width), height: (the height)}
34597 getViewSize : function()
34600 if(this.el.dom != document.body){
34601 size = this.el.getSize();
34603 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34605 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34606 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34611 * Returns the Element this layout is bound to.
34612 * @return {Roo.Element}
34614 getEl : function(){
34619 * Returns the specified region.
34620 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34621 * @return {Roo.LayoutRegion}
34623 getRegion : function(target){
34624 return this.regions[target.toLowerCase()];
34627 onWindowResize : function(){
34628 if(this.monitorWindowResize){
34635 * Ext JS Library 1.1.1
34636 * Copyright(c) 2006-2007, Ext JS, LLC.
34638 * Originally Released Under LGPL - original licence link has changed is not relivant.
34641 * <script type="text/javascript">
34644 * @class Roo.bootstrap.layout.Border
34645 * @extends Roo.bootstrap.layout.Manager
34646 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34647 * please see: examples/bootstrap/nested.html<br><br>
34649 <b>The container the layout is rendered into can be either the body element or any other element.
34650 If it is not the body element, the container needs to either be an absolute positioned element,
34651 or you will need to add "position:relative" to the css of the container. You will also need to specify
34652 the container size if it is not the body element.</b>
34655 * Create a new Border
34656 * @param {Object} config Configuration options
34658 Roo.bootstrap.layout.Border = function(config){
34659 config = config || {};
34660 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34664 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34665 if(config[region]){
34666 config[region].region = region;
34667 this.addRegion(config[region]);
34673 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34675 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34677 * Creates and adds a new region if it doesn't already exist.
34678 * @param {String} target The target region key (north, south, east, west or center).
34679 * @param {Object} config The regions config object
34680 * @return {BorderLayoutRegion} The new region
34682 addRegion : function(config)
34684 if(!this.regions[config.region]){
34685 var r = this.factory(config);
34686 this.bindRegion(r);
34688 return this.regions[config.region];
34692 bindRegion : function(r){
34693 this.regions[r.config.region] = r;
34695 r.on("visibilitychange", this.layout, this);
34696 r.on("paneladded", this.layout, this);
34697 r.on("panelremoved", this.layout, this);
34698 r.on("invalidated", this.layout, this);
34699 r.on("resized", this.onRegionResized, this);
34700 r.on("collapsed", this.onRegionCollapsed, this);
34701 r.on("expanded", this.onRegionExpanded, this);
34705 * Performs a layout update.
34707 layout : function()
34709 if(this.updating) {
34713 // render all the rebions if they have not been done alreayd?
34714 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34715 if(this.regions[region] && !this.regions[region].bodyEl){
34716 this.regions[region].onRender(this.el)
34720 var size = this.getViewSize();
34721 var w = size.width;
34722 var h = size.height;
34727 //var x = 0, y = 0;
34729 var rs = this.regions;
34730 var north = rs["north"];
34731 var south = rs["south"];
34732 var west = rs["west"];
34733 var east = rs["east"];
34734 var center = rs["center"];
34735 //if(this.hideOnLayout){ // not supported anymore
34736 //c.el.setStyle("display", "none");
34738 if(north && north.isVisible()){
34739 var b = north.getBox();
34740 var m = north.getMargins();
34741 b.width = w - (m.left+m.right);
34744 centerY = b.height + b.y + m.bottom;
34745 centerH -= centerY;
34746 north.updateBox(this.safeBox(b));
34748 if(south && south.isVisible()){
34749 var b = south.getBox();
34750 var m = south.getMargins();
34751 b.width = w - (m.left+m.right);
34753 var totalHeight = (b.height + m.top + m.bottom);
34754 b.y = h - totalHeight + m.top;
34755 centerH -= totalHeight;
34756 south.updateBox(this.safeBox(b));
34758 if(west && west.isVisible()){
34759 var b = west.getBox();
34760 var m = west.getMargins();
34761 b.height = centerH - (m.top+m.bottom);
34763 b.y = centerY + m.top;
34764 var totalWidth = (b.width + m.left + m.right);
34765 centerX += totalWidth;
34766 centerW -= totalWidth;
34767 west.updateBox(this.safeBox(b));
34769 if(east && east.isVisible()){
34770 var b = east.getBox();
34771 var m = east.getMargins();
34772 b.height = centerH - (m.top+m.bottom);
34773 var totalWidth = (b.width + m.left + m.right);
34774 b.x = w - totalWidth + m.left;
34775 b.y = centerY + m.top;
34776 centerW -= totalWidth;
34777 east.updateBox(this.safeBox(b));
34780 var m = center.getMargins();
34782 x: centerX + m.left,
34783 y: centerY + m.top,
34784 width: centerW - (m.left+m.right),
34785 height: centerH - (m.top+m.bottom)
34787 //if(this.hideOnLayout){
34788 //center.el.setStyle("display", "block");
34790 center.updateBox(this.safeBox(centerBox));
34793 this.fireEvent("layout", this);
34797 safeBox : function(box){
34798 box.width = Math.max(0, box.width);
34799 box.height = Math.max(0, box.height);
34804 * Adds a ContentPanel (or subclass) to this layout.
34805 * @param {String} target The target region key (north, south, east, west or center).
34806 * @param {Roo.ContentPanel} panel The panel to add
34807 * @return {Roo.ContentPanel} The added panel
34809 add : function(target, panel){
34811 target = target.toLowerCase();
34812 return this.regions[target].add(panel);
34816 * Remove a ContentPanel (or subclass) to this layout.
34817 * @param {String} target The target region key (north, south, east, west or center).
34818 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34819 * @return {Roo.ContentPanel} The removed panel
34821 remove : function(target, panel){
34822 target = target.toLowerCase();
34823 return this.regions[target].remove(panel);
34827 * Searches all regions for a panel with the specified id
34828 * @param {String} panelId
34829 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34831 findPanel : function(panelId){
34832 var rs = this.regions;
34833 for(var target in rs){
34834 if(typeof rs[target] != "function"){
34835 var p = rs[target].getPanel(panelId);
34845 * Searches all regions for a panel with the specified id and activates (shows) it.
34846 * @param {String/ContentPanel} panelId The panels id or the panel itself
34847 * @return {Roo.ContentPanel} The shown panel or null
34849 showPanel : function(panelId) {
34850 var rs = this.regions;
34851 for(var target in rs){
34852 var r = rs[target];
34853 if(typeof r != "function"){
34854 if(r.hasPanel(panelId)){
34855 return r.showPanel(panelId);
34863 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34864 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34867 restoreState : function(provider){
34869 provider = Roo.state.Manager;
34871 var sm = new Roo.LayoutStateManager();
34872 sm.init(this, provider);
34878 * Adds a xtype elements to the layout.
34882 xtype : 'ContentPanel',
34889 xtype : 'NestedLayoutPanel',
34895 items : [ ... list of content panels or nested layout panels.. ]
34899 * @param {Object} cfg Xtype definition of item to add.
34901 addxtype : function(cfg)
34903 // basically accepts a pannel...
34904 // can accept a layout region..!?!?
34905 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34908 // theory? children can only be panels??
34910 //if (!cfg.xtype.match(/Panel$/)) {
34915 if (typeof(cfg.region) == 'undefined') {
34916 Roo.log("Failed to add Panel, region was not set");
34920 var region = cfg.region;
34926 xitems = cfg.items;
34933 case 'Content': // ContentPanel (el, cfg)
34934 case 'Scroll': // ContentPanel (el, cfg)
34936 cfg.autoCreate = true;
34937 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34939 // var el = this.el.createChild();
34940 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34943 this.add(region, ret);
34947 case 'TreePanel': // our new panel!
34948 cfg.el = this.el.createChild();
34949 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34950 this.add(region, ret);
34955 // create a new Layout (which is a Border Layout...
34957 var clayout = cfg.layout;
34958 clayout.el = this.el.createChild();
34959 clayout.items = clayout.items || [];
34963 // replace this exitems with the clayout ones..
34964 xitems = clayout.items;
34966 // force background off if it's in center...
34967 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34968 cfg.background = false;
34970 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34973 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34974 //console.log('adding nested layout panel ' + cfg.toSource());
34975 this.add(region, ret);
34976 nb = {}; /// find first...
34981 // needs grid and region
34983 //var el = this.getRegion(region).el.createChild();
34985 *var el = this.el.createChild();
34986 // create the grid first...
34987 cfg.grid.container = el;
34988 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34991 if (region == 'center' && this.active ) {
34992 cfg.background = false;
34995 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34997 this.add(region, ret);
34999 if (cfg.background) {
35000 // render grid on panel activation (if panel background)
35001 ret.on('activate', function(gp) {
35002 if (!gp.grid.rendered) {
35003 // gp.grid.render(el);
35007 // cfg.grid.render(el);
35013 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35014 // it was the old xcomponent building that caused this before.
35015 // espeically if border is the top element in the tree.
35025 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35027 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35028 this.add(region, ret);
35032 throw "Can not add '" + cfg.xtype + "' to Border";
35038 this.beginUpdate();
35042 Roo.each(xitems, function(i) {
35043 region = nb && i.region ? i.region : false;
35045 var add = ret.addxtype(i);
35048 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35049 if (!i.background) {
35050 abn[region] = nb[region] ;
35057 // make the last non-background panel active..
35058 //if (nb) { Roo.log(abn); }
35061 for(var r in abn) {
35062 region = this.getRegion(r);
35064 // tried using nb[r], but it does not work..
35066 region.showPanel(abn[r]);
35077 factory : function(cfg)
35080 var validRegions = Roo.bootstrap.layout.Border.regions;
35082 var target = cfg.region;
35085 var r = Roo.bootstrap.layout;
35089 return new r.North(cfg);
35091 return new r.South(cfg);
35093 return new r.East(cfg);
35095 return new r.West(cfg);
35097 return new r.Center(cfg);
35099 throw 'Layout region "'+target+'" not supported.';
35106 * Ext JS Library 1.1.1
35107 * Copyright(c) 2006-2007, Ext JS, LLC.
35109 * Originally Released Under LGPL - original licence link has changed is not relivant.
35112 * <script type="text/javascript">
35116 * @class Roo.bootstrap.layout.Basic
35117 * @extends Roo.util.Observable
35118 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35119 * and does not have a titlebar, tabs or any other features. All it does is size and position
35120 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35121 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35122 * @cfg {string} region the region that it inhabits..
35123 * @cfg {bool} skipConfig skip config?
35127 Roo.bootstrap.layout.Basic = function(config){
35129 this.mgr = config.mgr;
35131 this.position = config.region;
35133 var skipConfig = config.skipConfig;
35137 * @scope Roo.BasicLayoutRegion
35141 * @event beforeremove
35142 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35143 * @param {Roo.LayoutRegion} this
35144 * @param {Roo.ContentPanel} panel The panel
35145 * @param {Object} e The cancel event object
35147 "beforeremove" : true,
35149 * @event invalidated
35150 * Fires when the layout for this region is changed.
35151 * @param {Roo.LayoutRegion} this
35153 "invalidated" : true,
35155 * @event visibilitychange
35156 * Fires when this region is shown or hidden
35157 * @param {Roo.LayoutRegion} this
35158 * @param {Boolean} visibility true or false
35160 "visibilitychange" : true,
35162 * @event paneladded
35163 * Fires when a panel is added.
35164 * @param {Roo.LayoutRegion} this
35165 * @param {Roo.ContentPanel} panel The panel
35167 "paneladded" : true,
35169 * @event panelremoved
35170 * Fires when a panel is removed.
35171 * @param {Roo.LayoutRegion} this
35172 * @param {Roo.ContentPanel} panel The panel
35174 "panelremoved" : true,
35176 * @event beforecollapse
35177 * Fires when this region before collapse.
35178 * @param {Roo.LayoutRegion} this
35180 "beforecollapse" : true,
35183 * Fires when this region is collapsed.
35184 * @param {Roo.LayoutRegion} this
35186 "collapsed" : true,
35189 * Fires when this region is expanded.
35190 * @param {Roo.LayoutRegion} this
35195 * Fires when this region is slid into view.
35196 * @param {Roo.LayoutRegion} this
35198 "slideshow" : true,
35201 * Fires when this region slides out of view.
35202 * @param {Roo.LayoutRegion} this
35204 "slidehide" : true,
35206 * @event panelactivated
35207 * Fires when a panel is activated.
35208 * @param {Roo.LayoutRegion} this
35209 * @param {Roo.ContentPanel} panel The activated panel
35211 "panelactivated" : true,
35214 * Fires when the user resizes this region.
35215 * @param {Roo.LayoutRegion} this
35216 * @param {Number} newSize The new size (width for east/west, height for north/south)
35220 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35221 this.panels = new Roo.util.MixedCollection();
35222 this.panels.getKey = this.getPanelId.createDelegate(this);
35224 this.activePanel = null;
35225 // ensure listeners are added...
35227 if (config.listeners || config.events) {
35228 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35229 listeners : config.listeners || {},
35230 events : config.events || {}
35234 if(skipConfig !== true){
35235 this.applyConfig(config);
35239 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35241 getPanelId : function(p){
35245 applyConfig : function(config){
35246 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35247 this.config = config;
35252 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35253 * the width, for horizontal (north, south) the height.
35254 * @param {Number} newSize The new width or height
35256 resizeTo : function(newSize){
35257 var el = this.el ? this.el :
35258 (this.activePanel ? this.activePanel.getEl() : null);
35260 switch(this.position){
35263 el.setWidth(newSize);
35264 this.fireEvent("resized", this, newSize);
35268 el.setHeight(newSize);
35269 this.fireEvent("resized", this, newSize);
35275 getBox : function(){
35276 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35279 getMargins : function(){
35280 return this.margins;
35283 updateBox : function(box){
35285 var el = this.activePanel.getEl();
35286 el.dom.style.left = box.x + "px";
35287 el.dom.style.top = box.y + "px";
35288 this.activePanel.setSize(box.width, box.height);
35292 * Returns the container element for this region.
35293 * @return {Roo.Element}
35295 getEl : function(){
35296 return this.activePanel;
35300 * Returns true if this region is currently visible.
35301 * @return {Boolean}
35303 isVisible : function(){
35304 return this.activePanel ? true : false;
35307 setActivePanel : function(panel){
35308 panel = this.getPanel(panel);
35309 if(this.activePanel && this.activePanel != panel){
35310 this.activePanel.setActiveState(false);
35311 this.activePanel.getEl().setLeftTop(-10000,-10000);
35313 this.activePanel = panel;
35314 panel.setActiveState(true);
35316 panel.setSize(this.box.width, this.box.height);
35318 this.fireEvent("panelactivated", this, panel);
35319 this.fireEvent("invalidated");
35323 * Show the specified panel.
35324 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35325 * @return {Roo.ContentPanel} The shown panel or null
35327 showPanel : function(panel){
35328 panel = this.getPanel(panel);
35330 this.setActivePanel(panel);
35336 * Get the active panel for this region.
35337 * @return {Roo.ContentPanel} The active panel or null
35339 getActivePanel : function(){
35340 return this.activePanel;
35344 * Add the passed ContentPanel(s)
35345 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35346 * @return {Roo.ContentPanel} The panel added (if only one was added)
35348 add : function(panel){
35349 if(arguments.length > 1){
35350 for(var i = 0, len = arguments.length; i < len; i++) {
35351 this.add(arguments[i]);
35355 if(this.hasPanel(panel)){
35356 this.showPanel(panel);
35359 var el = panel.getEl();
35360 if(el.dom.parentNode != this.mgr.el.dom){
35361 this.mgr.el.dom.appendChild(el.dom);
35363 if(panel.setRegion){
35364 panel.setRegion(this);
35366 this.panels.add(panel);
35367 el.setStyle("position", "absolute");
35368 if(!panel.background){
35369 this.setActivePanel(panel);
35370 if(this.config.initialSize && this.panels.getCount()==1){
35371 this.resizeTo(this.config.initialSize);
35374 this.fireEvent("paneladded", this, panel);
35379 * Returns true if the panel is in this region.
35380 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35381 * @return {Boolean}
35383 hasPanel : function(panel){
35384 if(typeof panel == "object"){ // must be panel obj
35385 panel = panel.getId();
35387 return this.getPanel(panel) ? true : false;
35391 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35392 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35393 * @param {Boolean} preservePanel Overrides the config preservePanel option
35394 * @return {Roo.ContentPanel} The panel that was removed
35396 remove : function(panel, preservePanel){
35397 panel = this.getPanel(panel);
35402 this.fireEvent("beforeremove", this, panel, e);
35403 if(e.cancel === true){
35406 var panelId = panel.getId();
35407 this.panels.removeKey(panelId);
35412 * Returns the panel specified or null if it's not in this region.
35413 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35414 * @return {Roo.ContentPanel}
35416 getPanel : function(id){
35417 if(typeof id == "object"){ // must be panel obj
35420 return this.panels.get(id);
35424 * Returns this regions position (north/south/east/west/center).
35427 getPosition: function(){
35428 return this.position;
35432 * Ext JS Library 1.1.1
35433 * Copyright(c) 2006-2007, Ext JS, LLC.
35435 * Originally Released Under LGPL - original licence link has changed is not relivant.
35438 * <script type="text/javascript">
35442 * @class Roo.bootstrap.layout.Region
35443 * @extends Roo.bootstrap.layout.Basic
35444 * This class represents a region in a layout manager.
35446 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35447 * @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})
35448 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35449 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35450 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35451 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35452 * @cfg {String} title The title for the region (overrides panel titles)
35453 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35454 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35455 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35456 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35457 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35458 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35459 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35460 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35461 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35462 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35464 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35465 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35466 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35467 * @cfg {Number} width For East/West panels
35468 * @cfg {Number} height For North/South panels
35469 * @cfg {Boolean} split To show the splitter
35470 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35472 * @cfg {string} cls Extra CSS classes to add to region
35474 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35475 * @cfg {string} region the region that it inhabits..
35478 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35479 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35481 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35482 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35483 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35485 Roo.bootstrap.layout.Region = function(config)
35487 this.applyConfig(config);
35489 var mgr = config.mgr;
35490 var pos = config.region;
35491 config.skipConfig = true;
35492 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35495 this.onRender(mgr.el);
35498 this.visible = true;
35499 this.collapsed = false;
35500 this.unrendered_panels = [];
35503 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35505 position: '', // set by wrapper (eg. north/south etc..)
35506 unrendered_panels : null, // unrendered panels.
35507 createBody : function(){
35508 /** This region's body element
35509 * @type Roo.Element */
35510 this.bodyEl = this.el.createChild({
35512 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35516 onRender: function(ctr, pos)
35518 var dh = Roo.DomHelper;
35519 /** This region's container element
35520 * @type Roo.Element */
35521 this.el = dh.append(ctr.dom, {
35523 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35525 /** This region's title element
35526 * @type Roo.Element */
35528 this.titleEl = dh.append(this.el.dom,
35531 unselectable: "on",
35532 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35534 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35535 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35538 this.titleEl.enableDisplayMode();
35539 /** This region's title text element
35540 * @type HTMLElement */
35541 this.titleTextEl = this.titleEl.dom.firstChild;
35542 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35544 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35545 this.closeBtn.enableDisplayMode();
35546 this.closeBtn.on("click", this.closeClicked, this);
35547 this.closeBtn.hide();
35549 this.createBody(this.config);
35550 if(this.config.hideWhenEmpty){
35552 this.on("paneladded", this.validateVisibility, this);
35553 this.on("panelremoved", this.validateVisibility, this);
35555 if(this.autoScroll){
35556 this.bodyEl.setStyle("overflow", "auto");
35558 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35560 //if(c.titlebar !== false){
35561 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35562 this.titleEl.hide();
35564 this.titleEl.show();
35565 if(this.config.title){
35566 this.titleTextEl.innerHTML = this.config.title;
35570 if(this.config.collapsed){
35571 this.collapse(true);
35573 if(this.config.hidden){
35577 if (this.unrendered_panels && this.unrendered_panels.length) {
35578 for (var i =0;i< this.unrendered_panels.length; i++) {
35579 this.add(this.unrendered_panels[i]);
35581 this.unrendered_panels = null;
35587 applyConfig : function(c)
35590 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35591 var dh = Roo.DomHelper;
35592 if(c.titlebar !== false){
35593 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35594 this.collapseBtn.on("click", this.collapse, this);
35595 this.collapseBtn.enableDisplayMode();
35597 if(c.showPin === true || this.showPin){
35598 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35599 this.stickBtn.enableDisplayMode();
35600 this.stickBtn.on("click", this.expand, this);
35601 this.stickBtn.hide();
35606 /** This region's collapsed element
35607 * @type Roo.Element */
35610 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35611 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35614 if(c.floatable !== false){
35615 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35616 this.collapsedEl.on("click", this.collapseClick, this);
35619 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35620 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35621 id: "message", unselectable: "on", style:{"float":"left"}});
35622 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35624 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35625 this.expandBtn.on("click", this.expand, this);
35629 if(this.collapseBtn){
35630 this.collapseBtn.setVisible(c.collapsible == true);
35633 this.cmargins = c.cmargins || this.cmargins ||
35634 (this.position == "west" || this.position == "east" ?
35635 {top: 0, left: 2, right:2, bottom: 0} :
35636 {top: 2, left: 0, right:0, bottom: 2});
35638 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35641 this.bottomTabs = c.tabPosition != "top";
35643 this.autoScroll = c.autoScroll || false;
35648 this.duration = c.duration || .30;
35649 this.slideDuration = c.slideDuration || .45;
35654 * Returns true if this region is currently visible.
35655 * @return {Boolean}
35657 isVisible : function(){
35658 return this.visible;
35662 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35663 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35665 //setCollapsedTitle : function(title){
35666 // title = title || " ";
35667 // if(this.collapsedTitleTextEl){
35668 // this.collapsedTitleTextEl.innerHTML = title;
35672 getBox : function(){
35674 // if(!this.collapsed){
35675 b = this.el.getBox(false, true);
35677 // b = this.collapsedEl.getBox(false, true);
35682 getMargins : function(){
35683 return this.margins;
35684 //return this.collapsed ? this.cmargins : this.margins;
35687 highlight : function(){
35688 this.el.addClass("x-layout-panel-dragover");
35691 unhighlight : function(){
35692 this.el.removeClass("x-layout-panel-dragover");
35695 updateBox : function(box)
35697 if (!this.bodyEl) {
35698 return; // not rendered yet..
35702 if(!this.collapsed){
35703 this.el.dom.style.left = box.x + "px";
35704 this.el.dom.style.top = box.y + "px";
35705 this.updateBody(box.width, box.height);
35707 this.collapsedEl.dom.style.left = box.x + "px";
35708 this.collapsedEl.dom.style.top = box.y + "px";
35709 this.collapsedEl.setSize(box.width, box.height);
35712 this.tabs.autoSizeTabs();
35716 updateBody : function(w, h)
35719 this.el.setWidth(w);
35720 w -= this.el.getBorderWidth("rl");
35721 if(this.config.adjustments){
35722 w += this.config.adjustments[0];
35725 if(h !== null && h > 0){
35726 this.el.setHeight(h);
35727 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35728 h -= this.el.getBorderWidth("tb");
35729 if(this.config.adjustments){
35730 h += this.config.adjustments[1];
35732 this.bodyEl.setHeight(h);
35734 h = this.tabs.syncHeight(h);
35737 if(this.panelSize){
35738 w = w !== null ? w : this.panelSize.width;
35739 h = h !== null ? h : this.panelSize.height;
35741 if(this.activePanel){
35742 var el = this.activePanel.getEl();
35743 w = w !== null ? w : el.getWidth();
35744 h = h !== null ? h : el.getHeight();
35745 this.panelSize = {width: w, height: h};
35746 this.activePanel.setSize(w, h);
35748 if(Roo.isIE && this.tabs){
35749 this.tabs.el.repaint();
35754 * Returns the container element for this region.
35755 * @return {Roo.Element}
35757 getEl : function(){
35762 * Hides this region.
35765 //if(!this.collapsed){
35766 this.el.dom.style.left = "-2000px";
35769 // this.collapsedEl.dom.style.left = "-2000px";
35770 // this.collapsedEl.hide();
35772 this.visible = false;
35773 this.fireEvent("visibilitychange", this, false);
35777 * Shows this region if it was previously hidden.
35780 //if(!this.collapsed){
35783 // this.collapsedEl.show();
35785 this.visible = true;
35786 this.fireEvent("visibilitychange", this, true);
35789 closeClicked : function(){
35790 if(this.activePanel){
35791 this.remove(this.activePanel);
35795 collapseClick : function(e){
35797 e.stopPropagation();
35800 e.stopPropagation();
35806 * Collapses this region.
35807 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35810 collapse : function(skipAnim, skipCheck = false){
35811 if(this.collapsed) {
35815 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35817 this.collapsed = true;
35819 this.split.el.hide();
35821 if(this.config.animate && skipAnim !== true){
35822 this.fireEvent("invalidated", this);
35823 this.animateCollapse();
35825 this.el.setLocation(-20000,-20000);
35827 this.collapsedEl.show();
35828 this.fireEvent("collapsed", this);
35829 this.fireEvent("invalidated", this);
35835 animateCollapse : function(){
35840 * Expands this region if it was previously collapsed.
35841 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35842 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35845 expand : function(e, skipAnim){
35847 e.stopPropagation();
35849 if(!this.collapsed || this.el.hasActiveFx()) {
35853 this.afterSlideIn();
35856 this.collapsed = false;
35857 if(this.config.animate && skipAnim !== true){
35858 this.animateExpand();
35862 this.split.el.show();
35864 this.collapsedEl.setLocation(-2000,-2000);
35865 this.collapsedEl.hide();
35866 this.fireEvent("invalidated", this);
35867 this.fireEvent("expanded", this);
35871 animateExpand : function(){
35875 initTabs : function()
35877 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35879 var ts = new Roo.bootstrap.panel.Tabs({
35880 el: this.bodyEl.dom,
35881 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35882 disableTooltips: this.config.disableTabTips,
35883 toolbar : this.config.toolbar
35886 if(this.config.hideTabs){
35887 ts.stripWrap.setDisplayed(false);
35890 ts.resizeTabs = this.config.resizeTabs === true;
35891 ts.minTabWidth = this.config.minTabWidth || 40;
35892 ts.maxTabWidth = this.config.maxTabWidth || 250;
35893 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35894 ts.monitorResize = false;
35895 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35896 ts.bodyEl.addClass('roo-layout-tabs-body');
35897 this.panels.each(this.initPanelAsTab, this);
35900 initPanelAsTab : function(panel){
35901 var ti = this.tabs.addTab(
35905 this.config.closeOnTab && panel.isClosable(),
35908 if(panel.tabTip !== undefined){
35909 ti.setTooltip(panel.tabTip);
35911 ti.on("activate", function(){
35912 this.setActivePanel(panel);
35915 if(this.config.closeOnTab){
35916 ti.on("beforeclose", function(t, e){
35918 this.remove(panel);
35922 panel.tabItem = ti;
35927 updatePanelTitle : function(panel, title)
35929 if(this.activePanel == panel){
35930 this.updateTitle(title);
35933 var ti = this.tabs.getTab(panel.getEl().id);
35935 if(panel.tabTip !== undefined){
35936 ti.setTooltip(panel.tabTip);
35941 updateTitle : function(title){
35942 if(this.titleTextEl && !this.config.title){
35943 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35947 setActivePanel : function(panel)
35949 panel = this.getPanel(panel);
35950 if(this.activePanel && this.activePanel != panel){
35951 if(this.activePanel.setActiveState(false) === false){
35955 this.activePanel = panel;
35956 panel.setActiveState(true);
35957 if(this.panelSize){
35958 panel.setSize(this.panelSize.width, this.panelSize.height);
35961 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35963 this.updateTitle(panel.getTitle());
35965 this.fireEvent("invalidated", this);
35967 this.fireEvent("panelactivated", this, panel);
35971 * Shows the specified panel.
35972 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35973 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35975 showPanel : function(panel)
35977 panel = this.getPanel(panel);
35980 var tab = this.tabs.getTab(panel.getEl().id);
35981 if(tab.isHidden()){
35982 this.tabs.unhideTab(tab.id);
35986 this.setActivePanel(panel);
35993 * Get the active panel for this region.
35994 * @return {Roo.ContentPanel} The active panel or null
35996 getActivePanel : function(){
35997 return this.activePanel;
36000 validateVisibility : function(){
36001 if(this.panels.getCount() < 1){
36002 this.updateTitle(" ");
36003 this.closeBtn.hide();
36006 if(!this.isVisible()){
36013 * Adds the passed ContentPanel(s) to this region.
36014 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36015 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36017 add : function(panel)
36019 if(arguments.length > 1){
36020 for(var i = 0, len = arguments.length; i < len; i++) {
36021 this.add(arguments[i]);
36026 // if we have not been rendered yet, then we can not really do much of this..
36027 if (!this.bodyEl) {
36028 this.unrendered_panels.push(panel);
36035 if(this.hasPanel(panel)){
36036 this.showPanel(panel);
36039 panel.setRegion(this);
36040 this.panels.add(panel);
36041 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36042 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36043 // and hide them... ???
36044 this.bodyEl.dom.appendChild(panel.getEl().dom);
36045 if(panel.background !== true){
36046 this.setActivePanel(panel);
36048 this.fireEvent("paneladded", this, panel);
36055 this.initPanelAsTab(panel);
36059 if(panel.background !== true){
36060 this.tabs.activate(panel.getEl().id);
36062 this.fireEvent("paneladded", this, panel);
36067 * Hides the tab for the specified panel.
36068 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36070 hidePanel : function(panel){
36071 if(this.tabs && (panel = this.getPanel(panel))){
36072 this.tabs.hideTab(panel.getEl().id);
36077 * Unhides the tab for a previously hidden panel.
36078 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36080 unhidePanel : function(panel){
36081 if(this.tabs && (panel = this.getPanel(panel))){
36082 this.tabs.unhideTab(panel.getEl().id);
36086 clearPanels : function(){
36087 while(this.panels.getCount() > 0){
36088 this.remove(this.panels.first());
36093 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36094 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36095 * @param {Boolean} preservePanel Overrides the config preservePanel option
36096 * @return {Roo.ContentPanel} The panel that was removed
36098 remove : function(panel, preservePanel)
36100 panel = this.getPanel(panel);
36105 this.fireEvent("beforeremove", this, panel, e);
36106 if(e.cancel === true){
36109 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36110 var panelId = panel.getId();
36111 this.panels.removeKey(panelId);
36113 document.body.appendChild(panel.getEl().dom);
36116 this.tabs.removeTab(panel.getEl().id);
36117 }else if (!preservePanel){
36118 this.bodyEl.dom.removeChild(panel.getEl().dom);
36120 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36121 var p = this.panels.first();
36122 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36123 tempEl.appendChild(p.getEl().dom);
36124 this.bodyEl.update("");
36125 this.bodyEl.dom.appendChild(p.getEl().dom);
36127 this.updateTitle(p.getTitle());
36129 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36130 this.setActivePanel(p);
36132 panel.setRegion(null);
36133 if(this.activePanel == panel){
36134 this.activePanel = null;
36136 if(this.config.autoDestroy !== false && preservePanel !== true){
36137 try{panel.destroy();}catch(e){}
36139 this.fireEvent("panelremoved", this, panel);
36144 * Returns the TabPanel component used by this region
36145 * @return {Roo.TabPanel}
36147 getTabs : function(){
36151 createTool : function(parentEl, className){
36152 var btn = Roo.DomHelper.append(parentEl, {
36154 cls: "x-layout-tools-button",
36157 cls: "roo-layout-tools-button-inner " + className,
36161 btn.addClassOnOver("roo-layout-tools-button-over");
36166 * Ext JS Library 1.1.1
36167 * Copyright(c) 2006-2007, Ext JS, LLC.
36169 * Originally Released Under LGPL - original licence link has changed is not relivant.
36172 * <script type="text/javascript">
36178 * @class Roo.SplitLayoutRegion
36179 * @extends Roo.LayoutRegion
36180 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36182 Roo.bootstrap.layout.Split = function(config){
36183 this.cursor = config.cursor;
36184 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36187 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36189 splitTip : "Drag to resize.",
36190 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36191 useSplitTips : false,
36193 applyConfig : function(config){
36194 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36197 onRender : function(ctr,pos) {
36199 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36200 if(!this.config.split){
36205 var splitEl = Roo.DomHelper.append(ctr.dom, {
36207 id: this.el.id + "-split",
36208 cls: "roo-layout-split roo-layout-split-"+this.position,
36211 /** The SplitBar for this region
36212 * @type Roo.SplitBar */
36213 // does not exist yet...
36214 Roo.log([this.position, this.orientation]);
36216 this.split = new Roo.bootstrap.SplitBar({
36217 dragElement : splitEl,
36218 resizingElement: this.el,
36219 orientation : this.orientation
36222 this.split.on("moved", this.onSplitMove, this);
36223 this.split.useShim = this.config.useShim === true;
36224 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36225 if(this.useSplitTips){
36226 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36228 //if(config.collapsible){
36229 // this.split.el.on("dblclick", this.collapse, this);
36232 if(typeof this.config.minSize != "undefined"){
36233 this.split.minSize = this.config.minSize;
36235 if(typeof this.config.maxSize != "undefined"){
36236 this.split.maxSize = this.config.maxSize;
36238 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36239 this.hideSplitter();
36244 getHMaxSize : function(){
36245 var cmax = this.config.maxSize || 10000;
36246 var center = this.mgr.getRegion("center");
36247 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36250 getVMaxSize : function(){
36251 var cmax = this.config.maxSize || 10000;
36252 var center = this.mgr.getRegion("center");
36253 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36256 onSplitMove : function(split, newSize){
36257 this.fireEvent("resized", this, newSize);
36261 * Returns the {@link Roo.SplitBar} for this region.
36262 * @return {Roo.SplitBar}
36264 getSplitBar : function(){
36269 this.hideSplitter();
36270 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36273 hideSplitter : function(){
36275 this.split.el.setLocation(-2000,-2000);
36276 this.split.el.hide();
36282 this.split.el.show();
36284 Roo.bootstrap.layout.Split.superclass.show.call(this);
36287 beforeSlide: function(){
36288 if(Roo.isGecko){// firefox overflow auto bug workaround
36289 this.bodyEl.clip();
36291 this.tabs.bodyEl.clip();
36293 if(this.activePanel){
36294 this.activePanel.getEl().clip();
36296 if(this.activePanel.beforeSlide){
36297 this.activePanel.beforeSlide();
36303 afterSlide : function(){
36304 if(Roo.isGecko){// firefox overflow auto bug workaround
36305 this.bodyEl.unclip();
36307 this.tabs.bodyEl.unclip();
36309 if(this.activePanel){
36310 this.activePanel.getEl().unclip();
36311 if(this.activePanel.afterSlide){
36312 this.activePanel.afterSlide();
36318 initAutoHide : function(){
36319 if(this.autoHide !== false){
36320 if(!this.autoHideHd){
36321 var st = new Roo.util.DelayedTask(this.slideIn, this);
36322 this.autoHideHd = {
36323 "mouseout": function(e){
36324 if(!e.within(this.el, true)){
36328 "mouseover" : function(e){
36334 this.el.on(this.autoHideHd);
36338 clearAutoHide : function(){
36339 if(this.autoHide !== false){
36340 this.el.un("mouseout", this.autoHideHd.mouseout);
36341 this.el.un("mouseover", this.autoHideHd.mouseover);
36345 clearMonitor : function(){
36346 Roo.get(document).un("click", this.slideInIf, this);
36349 // these names are backwards but not changed for compat
36350 slideOut : function(){
36351 if(this.isSlid || this.el.hasActiveFx()){
36354 this.isSlid = true;
36355 if(this.collapseBtn){
36356 this.collapseBtn.hide();
36358 this.closeBtnState = this.closeBtn.getStyle('display');
36359 this.closeBtn.hide();
36361 this.stickBtn.show();
36364 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36365 this.beforeSlide();
36366 this.el.setStyle("z-index", 10001);
36367 this.el.slideIn(this.getSlideAnchor(), {
36368 callback: function(){
36370 this.initAutoHide();
36371 Roo.get(document).on("click", this.slideInIf, this);
36372 this.fireEvent("slideshow", this);
36379 afterSlideIn : function(){
36380 this.clearAutoHide();
36381 this.isSlid = false;
36382 this.clearMonitor();
36383 this.el.setStyle("z-index", "");
36384 if(this.collapseBtn){
36385 this.collapseBtn.show();
36387 this.closeBtn.setStyle('display', this.closeBtnState);
36389 this.stickBtn.hide();
36391 this.fireEvent("slidehide", this);
36394 slideIn : function(cb){
36395 if(!this.isSlid || this.el.hasActiveFx()){
36399 this.isSlid = false;
36400 this.beforeSlide();
36401 this.el.slideOut(this.getSlideAnchor(), {
36402 callback: function(){
36403 this.el.setLeftTop(-10000, -10000);
36405 this.afterSlideIn();
36413 slideInIf : function(e){
36414 if(!e.within(this.el)){
36419 animateCollapse : function(){
36420 this.beforeSlide();
36421 this.el.setStyle("z-index", 20000);
36422 var anchor = this.getSlideAnchor();
36423 this.el.slideOut(anchor, {
36424 callback : function(){
36425 this.el.setStyle("z-index", "");
36426 this.collapsedEl.slideIn(anchor, {duration:.3});
36428 this.el.setLocation(-10000,-10000);
36430 this.fireEvent("collapsed", this);
36437 animateExpand : function(){
36438 this.beforeSlide();
36439 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36440 this.el.setStyle("z-index", 20000);
36441 this.collapsedEl.hide({
36444 this.el.slideIn(this.getSlideAnchor(), {
36445 callback : function(){
36446 this.el.setStyle("z-index", "");
36449 this.split.el.show();
36451 this.fireEvent("invalidated", this);
36452 this.fireEvent("expanded", this);
36480 getAnchor : function(){
36481 return this.anchors[this.position];
36484 getCollapseAnchor : function(){
36485 return this.canchors[this.position];
36488 getSlideAnchor : function(){
36489 return this.sanchors[this.position];
36492 getAlignAdj : function(){
36493 var cm = this.cmargins;
36494 switch(this.position){
36510 getExpandAdj : function(){
36511 var c = this.collapsedEl, cm = this.cmargins;
36512 switch(this.position){
36514 return [-(cm.right+c.getWidth()+cm.left), 0];
36517 return [cm.right+c.getWidth()+cm.left, 0];
36520 return [0, -(cm.top+cm.bottom+c.getHeight())];
36523 return [0, cm.top+cm.bottom+c.getHeight()];
36529 * Ext JS Library 1.1.1
36530 * Copyright(c) 2006-2007, Ext JS, LLC.
36532 * Originally Released Under LGPL - original licence link has changed is not relivant.
36535 * <script type="text/javascript">
36538 * These classes are private internal classes
36540 Roo.bootstrap.layout.Center = function(config){
36541 config.region = "center";
36542 Roo.bootstrap.layout.Region.call(this, config);
36543 this.visible = true;
36544 this.minWidth = config.minWidth || 20;
36545 this.minHeight = config.minHeight || 20;
36548 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36550 // center panel can't be hidden
36554 // center panel can't be hidden
36557 getMinWidth: function(){
36558 return this.minWidth;
36561 getMinHeight: function(){
36562 return this.minHeight;
36575 Roo.bootstrap.layout.North = function(config)
36577 config.region = 'north';
36578 config.cursor = 'n-resize';
36580 Roo.bootstrap.layout.Split.call(this, config);
36584 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36585 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36586 this.split.el.addClass("roo-layout-split-v");
36588 var size = config.initialSize || config.height;
36589 if(typeof size != "undefined"){
36590 this.el.setHeight(size);
36593 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36595 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36599 getBox : function(){
36600 if(this.collapsed){
36601 return this.collapsedEl.getBox();
36603 var box = this.el.getBox();
36605 box.height += this.split.el.getHeight();
36610 updateBox : function(box){
36611 if(this.split && !this.collapsed){
36612 box.height -= this.split.el.getHeight();
36613 this.split.el.setLeft(box.x);
36614 this.split.el.setTop(box.y+box.height);
36615 this.split.el.setWidth(box.width);
36617 if(this.collapsed){
36618 this.updateBody(box.width, null);
36620 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36628 Roo.bootstrap.layout.South = function(config){
36629 config.region = 'south';
36630 config.cursor = 's-resize';
36631 Roo.bootstrap.layout.Split.call(this, config);
36633 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36634 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36635 this.split.el.addClass("roo-layout-split-v");
36637 var size = config.initialSize || config.height;
36638 if(typeof size != "undefined"){
36639 this.el.setHeight(size);
36643 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36644 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36645 getBox : function(){
36646 if(this.collapsed){
36647 return this.collapsedEl.getBox();
36649 var box = this.el.getBox();
36651 var sh = this.split.el.getHeight();
36658 updateBox : function(box){
36659 if(this.split && !this.collapsed){
36660 var sh = this.split.el.getHeight();
36663 this.split.el.setLeft(box.x);
36664 this.split.el.setTop(box.y-sh);
36665 this.split.el.setWidth(box.width);
36667 if(this.collapsed){
36668 this.updateBody(box.width, null);
36670 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36674 Roo.bootstrap.layout.East = function(config){
36675 config.region = "east";
36676 config.cursor = "e-resize";
36677 Roo.bootstrap.layout.Split.call(this, config);
36679 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36680 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36681 this.split.el.addClass("roo-layout-split-h");
36683 var size = config.initialSize || config.width;
36684 if(typeof size != "undefined"){
36685 this.el.setWidth(size);
36688 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36689 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36690 getBox : function(){
36691 if(this.collapsed){
36692 return this.collapsedEl.getBox();
36694 var box = this.el.getBox();
36696 var sw = this.split.el.getWidth();
36703 updateBox : function(box){
36704 if(this.split && !this.collapsed){
36705 var sw = this.split.el.getWidth();
36707 this.split.el.setLeft(box.x);
36708 this.split.el.setTop(box.y);
36709 this.split.el.setHeight(box.height);
36712 if(this.collapsed){
36713 this.updateBody(null, box.height);
36715 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36719 Roo.bootstrap.layout.West = function(config){
36720 config.region = "west";
36721 config.cursor = "w-resize";
36723 Roo.bootstrap.layout.Split.call(this, config);
36725 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36726 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36727 this.split.el.addClass("roo-layout-split-h");
36731 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36732 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36734 onRender: function(ctr, pos)
36736 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36737 var size = this.config.initialSize || this.config.width;
36738 if(typeof size != "undefined"){
36739 this.el.setWidth(size);
36743 getBox : function(){
36744 if(this.collapsed){
36745 return this.collapsedEl.getBox();
36747 var box = this.el.getBox();
36749 box.width += this.split.el.getWidth();
36754 updateBox : function(box){
36755 if(this.split && !this.collapsed){
36756 var sw = this.split.el.getWidth();
36758 this.split.el.setLeft(box.x+box.width);
36759 this.split.el.setTop(box.y);
36760 this.split.el.setHeight(box.height);
36762 if(this.collapsed){
36763 this.updateBody(null, box.height);
36765 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36768 Roo.namespace("Roo.bootstrap.panel");/*
36770 * Ext JS Library 1.1.1
36771 * Copyright(c) 2006-2007, Ext JS, LLC.
36773 * Originally Released Under LGPL - original licence link has changed is not relivant.
36776 * <script type="text/javascript">
36779 * @class Roo.ContentPanel
36780 * @extends Roo.util.Observable
36781 * A basic ContentPanel element.
36782 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36783 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36784 * @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
36785 * @cfg {Boolean} closable True if the panel can be closed/removed
36786 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36787 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36788 * @cfg {Toolbar} toolbar A toolbar for this panel
36789 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36790 * @cfg {String} title The title for this panel
36791 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36792 * @cfg {String} url Calls {@link #setUrl} with this value
36793 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36794 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36795 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36796 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36797 * @cfg {Boolean} badges render the badges
36800 * Create a new ContentPanel.
36801 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36802 * @param {String/Object} config A string to set only the title or a config object
36803 * @param {String} content (optional) Set the HTML content for this panel
36804 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36806 Roo.bootstrap.panel.Content = function( config){
36808 this.tpl = config.tpl || false;
36810 var el = config.el;
36811 var content = config.content;
36813 if(config.autoCreate){ // xtype is available if this is called from factory
36816 this.el = Roo.get(el);
36817 if(!this.el && config && config.autoCreate){
36818 if(typeof config.autoCreate == "object"){
36819 if(!config.autoCreate.id){
36820 config.autoCreate.id = config.id||el;
36822 this.el = Roo.DomHelper.append(document.body,
36823 config.autoCreate, true);
36825 var elcfg = { tag: "div",
36826 cls: "roo-layout-inactive-content",
36830 elcfg.html = config.html;
36834 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36837 this.closable = false;
36838 this.loaded = false;
36839 this.active = false;
36842 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36844 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36846 this.wrapEl = this.el; //this.el.wrap();
36848 if (config.toolbar.items) {
36849 ti = config.toolbar.items ;
36850 delete config.toolbar.items ;
36854 this.toolbar.render(this.wrapEl, 'before');
36855 for(var i =0;i < ti.length;i++) {
36856 // Roo.log(['add child', items[i]]);
36857 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36859 this.toolbar.items = nitems;
36860 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36861 delete config.toolbar;
36865 // xtype created footer. - not sure if will work as we normally have to render first..
36866 if (this.footer && !this.footer.el && this.footer.xtype) {
36867 if (!this.wrapEl) {
36868 this.wrapEl = this.el.wrap();
36871 this.footer.container = this.wrapEl.createChild();
36873 this.footer = Roo.factory(this.footer, Roo);
36878 if(typeof config == "string"){
36879 this.title = config;
36881 Roo.apply(this, config);
36885 this.resizeEl = Roo.get(this.resizeEl, true);
36887 this.resizeEl = this.el;
36889 // handle view.xtype
36897 * Fires when this panel is activated.
36898 * @param {Roo.ContentPanel} this
36902 * @event deactivate
36903 * Fires when this panel is activated.
36904 * @param {Roo.ContentPanel} this
36906 "deactivate" : true,
36910 * Fires when this panel is resized if fitToFrame is true.
36911 * @param {Roo.ContentPanel} this
36912 * @param {Number} width The width after any component adjustments
36913 * @param {Number} height The height after any component adjustments
36919 * Fires when this tab is created
36920 * @param {Roo.ContentPanel} this
36931 if(this.autoScroll){
36932 this.resizeEl.setStyle("overflow", "auto");
36934 // fix randome scrolling
36935 //this.el.on('scroll', function() {
36936 // Roo.log('fix random scolling');
36937 // this.scrollTo('top',0);
36940 content = content || this.content;
36942 this.setContent(content);
36944 if(config && config.url){
36945 this.setUrl(this.url, this.params, this.loadOnce);
36950 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36952 if (this.view && typeof(this.view.xtype) != 'undefined') {
36953 this.view.el = this.el.appendChild(document.createElement("div"));
36954 this.view = Roo.factory(this.view);
36955 this.view.render && this.view.render(false, '');
36959 this.fireEvent('render', this);
36962 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36966 setRegion : function(region){
36967 this.region = region;
36968 this.setActiveClass(region && !this.background);
36972 setActiveClass: function(state)
36975 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36976 this.el.setStyle('position','relative');
36978 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36979 this.el.setStyle('position', 'absolute');
36984 * Returns the toolbar for this Panel if one was configured.
36985 * @return {Roo.Toolbar}
36987 getToolbar : function(){
36988 return this.toolbar;
36991 setActiveState : function(active)
36993 this.active = active;
36994 this.setActiveClass(active);
36996 if(this.fireEvent("deactivate", this) === false){
37001 this.fireEvent("activate", this);
37005 * Updates this panel's element
37006 * @param {String} content The new content
37007 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37009 setContent : function(content, loadScripts){
37010 this.el.update(content, loadScripts);
37013 ignoreResize : function(w, h){
37014 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37017 this.lastSize = {width: w, height: h};
37022 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37023 * @return {Roo.UpdateManager} The UpdateManager
37025 getUpdateManager : function(){
37026 return this.el.getUpdateManager();
37029 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37030 * @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:
37033 url: "your-url.php",
37034 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37035 callback: yourFunction,
37036 scope: yourObject, //(optional scope)
37039 text: "Loading...",
37044 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37045 * 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.
37046 * @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}
37047 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37048 * @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.
37049 * @return {Roo.ContentPanel} this
37052 var um = this.el.getUpdateManager();
37053 um.update.apply(um, arguments);
37059 * 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.
37060 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37061 * @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)
37062 * @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)
37063 * @return {Roo.UpdateManager} The UpdateManager
37065 setUrl : function(url, params, loadOnce){
37066 if(this.refreshDelegate){
37067 this.removeListener("activate", this.refreshDelegate);
37069 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37070 this.on("activate", this.refreshDelegate);
37071 return this.el.getUpdateManager();
37074 _handleRefresh : function(url, params, loadOnce){
37075 if(!loadOnce || !this.loaded){
37076 var updater = this.el.getUpdateManager();
37077 updater.update(url, params, this._setLoaded.createDelegate(this));
37081 _setLoaded : function(){
37082 this.loaded = true;
37086 * Returns this panel's id
37089 getId : function(){
37094 * Returns this panel's element - used by regiosn to add.
37095 * @return {Roo.Element}
37097 getEl : function(){
37098 return this.wrapEl || this.el;
37103 adjustForComponents : function(width, height)
37105 //Roo.log('adjustForComponents ');
37106 if(this.resizeEl != this.el){
37107 width -= this.el.getFrameWidth('lr');
37108 height -= this.el.getFrameWidth('tb');
37111 var te = this.toolbar.getEl();
37112 te.setWidth(width);
37113 height -= te.getHeight();
37116 var te = this.footer.getEl();
37117 te.setWidth(width);
37118 height -= te.getHeight();
37122 if(this.adjustments){
37123 width += this.adjustments[0];
37124 height += this.adjustments[1];
37126 return {"width": width, "height": height};
37129 setSize : function(width, height){
37130 if(this.fitToFrame && !this.ignoreResize(width, height)){
37131 if(this.fitContainer && this.resizeEl != this.el){
37132 this.el.setSize(width, height);
37134 var size = this.adjustForComponents(width, height);
37135 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37136 this.fireEvent('resize', this, size.width, size.height);
37141 * Returns this panel's title
37144 getTitle : function(){
37146 if (typeof(this.title) != 'object') {
37151 for (var k in this.title) {
37152 if (!this.title.hasOwnProperty(k)) {
37156 if (k.indexOf('-') >= 0) {
37157 var s = k.split('-');
37158 for (var i = 0; i<s.length; i++) {
37159 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37162 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37169 * Set this panel's title
37170 * @param {String} title
37172 setTitle : function(title){
37173 this.title = title;
37175 this.region.updatePanelTitle(this, title);
37180 * Returns true is this panel was configured to be closable
37181 * @return {Boolean}
37183 isClosable : function(){
37184 return this.closable;
37187 beforeSlide : function(){
37189 this.resizeEl.clip();
37192 afterSlide : function(){
37194 this.resizeEl.unclip();
37198 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37199 * Will fail silently if the {@link #setUrl} method has not been called.
37200 * This does not activate the panel, just updates its content.
37202 refresh : function(){
37203 if(this.refreshDelegate){
37204 this.loaded = false;
37205 this.refreshDelegate();
37210 * Destroys this panel
37212 destroy : function(){
37213 this.el.removeAllListeners();
37214 var tempEl = document.createElement("span");
37215 tempEl.appendChild(this.el.dom);
37216 tempEl.innerHTML = "";
37222 * form - if the content panel contains a form - this is a reference to it.
37223 * @type {Roo.form.Form}
37227 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37228 * This contains a reference to it.
37234 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37244 * @param {Object} cfg Xtype definition of item to add.
37248 getChildContainer: function () {
37249 return this.getEl();
37254 var ret = new Roo.factory(cfg);
37259 if (cfg.xtype.match(/^Form$/)) {
37262 //if (this.footer) {
37263 // el = this.footer.container.insertSibling(false, 'before');
37265 el = this.el.createChild();
37268 this.form = new Roo.form.Form(cfg);
37271 if ( this.form.allItems.length) {
37272 this.form.render(el.dom);
37276 // should only have one of theses..
37277 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37278 // views.. should not be just added - used named prop 'view''
37280 cfg.el = this.el.appendChild(document.createElement("div"));
37283 var ret = new Roo.factory(cfg);
37285 ret.render && ret.render(false, ''); // render blank..
37295 * @class Roo.bootstrap.panel.Grid
37296 * @extends Roo.bootstrap.panel.Content
37298 * Create a new GridPanel.
37299 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37300 * @param {Object} config A the config object
37306 Roo.bootstrap.panel.Grid = function(config)
37310 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37311 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37313 config.el = this.wrapper;
37314 //this.el = this.wrapper;
37316 if (config.container) {
37317 // ctor'ed from a Border/panel.grid
37320 this.wrapper.setStyle("overflow", "hidden");
37321 this.wrapper.addClass('roo-grid-container');
37326 if(config.toolbar){
37327 var tool_el = this.wrapper.createChild();
37328 this.toolbar = Roo.factory(config.toolbar);
37330 if (config.toolbar.items) {
37331 ti = config.toolbar.items ;
37332 delete config.toolbar.items ;
37336 this.toolbar.render(tool_el);
37337 for(var i =0;i < ti.length;i++) {
37338 // Roo.log(['add child', items[i]]);
37339 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37341 this.toolbar.items = nitems;
37343 delete config.toolbar;
37346 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37347 config.grid.scrollBody = true;;
37348 config.grid.monitorWindowResize = false; // turn off autosizing
37349 config.grid.autoHeight = false;
37350 config.grid.autoWidth = false;
37352 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37354 if (config.background) {
37355 // render grid on panel activation (if panel background)
37356 this.on('activate', function(gp) {
37357 if (!gp.grid.rendered) {
37358 gp.grid.render(this.wrapper);
37359 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37364 this.grid.render(this.wrapper);
37365 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37368 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37369 // ??? needed ??? config.el = this.wrapper;
37374 // xtype created footer. - not sure if will work as we normally have to render first..
37375 if (this.footer && !this.footer.el && this.footer.xtype) {
37377 var ctr = this.grid.getView().getFooterPanel(true);
37378 this.footer.dataSource = this.grid.dataSource;
37379 this.footer = Roo.factory(this.footer, Roo);
37380 this.footer.render(ctr);
37390 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37391 getId : function(){
37392 return this.grid.id;
37396 * Returns the grid for this panel
37397 * @return {Roo.bootstrap.Table}
37399 getGrid : function(){
37403 setSize : function(width, height){
37404 if(!this.ignoreResize(width, height)){
37405 var grid = this.grid;
37406 var size = this.adjustForComponents(width, height);
37407 var gridel = grid.getGridEl();
37408 gridel.setSize(size.width, size.height);
37410 var thd = grid.getGridEl().select('thead',true).first();
37411 var tbd = grid.getGridEl().select('tbody', true).first();
37413 tbd.setSize(width, height - thd.getHeight());
37422 beforeSlide : function(){
37423 this.grid.getView().scroller.clip();
37426 afterSlide : function(){
37427 this.grid.getView().scroller.unclip();
37430 destroy : function(){
37431 this.grid.destroy();
37433 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37438 * @class Roo.bootstrap.panel.Nest
37439 * @extends Roo.bootstrap.panel.Content
37441 * Create a new Panel, that can contain a layout.Border.
37444 * @param {Roo.BorderLayout} layout The layout for this panel
37445 * @param {String/Object} config A string to set only the title or a config object
37447 Roo.bootstrap.panel.Nest = function(config)
37449 // construct with only one argument..
37450 /* FIXME - implement nicer consturctors
37451 if (layout.layout) {
37453 layout = config.layout;
37454 delete config.layout;
37456 if (layout.xtype && !layout.getEl) {
37457 // then layout needs constructing..
37458 layout = Roo.factory(layout, Roo);
37462 config.el = config.layout.getEl();
37464 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37466 config.layout.monitorWindowResize = false; // turn off autosizing
37467 this.layout = config.layout;
37468 this.layout.getEl().addClass("roo-layout-nested-layout");
37475 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37477 setSize : function(width, height){
37478 if(!this.ignoreResize(width, height)){
37479 var size = this.adjustForComponents(width, height);
37480 var el = this.layout.getEl();
37481 if (size.height < 1) {
37482 el.setWidth(size.width);
37484 el.setSize(size.width, size.height);
37486 var touch = el.dom.offsetWidth;
37487 this.layout.layout();
37488 // ie requires a double layout on the first pass
37489 if(Roo.isIE && !this.initialized){
37490 this.initialized = true;
37491 this.layout.layout();
37496 // activate all subpanels if not currently active..
37498 setActiveState : function(active){
37499 this.active = active;
37500 this.setActiveClass(active);
37503 this.fireEvent("deactivate", this);
37507 this.fireEvent("activate", this);
37508 // not sure if this should happen before or after..
37509 if (!this.layout) {
37510 return; // should not happen..
37513 for (var r in this.layout.regions) {
37514 reg = this.layout.getRegion(r);
37515 if (reg.getActivePanel()) {
37516 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37517 reg.setActivePanel(reg.getActivePanel());
37520 if (!reg.panels.length) {
37523 reg.showPanel(reg.getPanel(0));
37532 * Returns the nested BorderLayout for this panel
37533 * @return {Roo.BorderLayout}
37535 getLayout : function(){
37536 return this.layout;
37540 * Adds a xtype elements to the layout of the nested panel
37544 xtype : 'ContentPanel',
37551 xtype : 'NestedLayoutPanel',
37557 items : [ ... list of content panels or nested layout panels.. ]
37561 * @param {Object} cfg Xtype definition of item to add.
37563 addxtype : function(cfg) {
37564 return this.layout.addxtype(cfg);
37569 * Ext JS Library 1.1.1
37570 * Copyright(c) 2006-2007, Ext JS, LLC.
37572 * Originally Released Under LGPL - original licence link has changed is not relivant.
37575 * <script type="text/javascript">
37578 * @class Roo.TabPanel
37579 * @extends Roo.util.Observable
37580 * A lightweight tab container.
37584 // basic tabs 1, built from existing content
37585 var tabs = new Roo.TabPanel("tabs1");
37586 tabs.addTab("script", "View Script");
37587 tabs.addTab("markup", "View Markup");
37588 tabs.activate("script");
37590 // more advanced tabs, built from javascript
37591 var jtabs = new Roo.TabPanel("jtabs");
37592 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37594 // set up the UpdateManager
37595 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37596 var updater = tab2.getUpdateManager();
37597 updater.setDefaultUrl("ajax1.htm");
37598 tab2.on('activate', updater.refresh, updater, true);
37600 // Use setUrl for Ajax loading
37601 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37602 tab3.setUrl("ajax2.htm", null, true);
37605 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37608 jtabs.activate("jtabs-1");
37611 * Create a new TabPanel.
37612 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37613 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37615 Roo.bootstrap.panel.Tabs = function(config){
37617 * The container element for this TabPanel.
37618 * @type Roo.Element
37620 this.el = Roo.get(config.el);
37623 if(typeof config == "boolean"){
37624 this.tabPosition = config ? "bottom" : "top";
37626 Roo.apply(this, config);
37630 if(this.tabPosition == "bottom"){
37631 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37632 this.el.addClass("roo-tabs-bottom");
37634 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37635 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37636 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37638 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37640 if(this.tabPosition != "bottom"){
37641 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37642 * @type Roo.Element
37644 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37645 this.el.addClass("roo-tabs-top");
37649 this.bodyEl.setStyle("position", "relative");
37651 this.active = null;
37652 this.activateDelegate = this.activate.createDelegate(this);
37657 * Fires when the active tab changes
37658 * @param {Roo.TabPanel} this
37659 * @param {Roo.TabPanelItem} activePanel The new active tab
37663 * @event beforetabchange
37664 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37665 * @param {Roo.TabPanel} this
37666 * @param {Object} e Set cancel to true on this object to cancel the tab change
37667 * @param {Roo.TabPanelItem} tab The tab being changed to
37669 "beforetabchange" : true
37672 Roo.EventManager.onWindowResize(this.onResize, this);
37673 this.cpad = this.el.getPadding("lr");
37674 this.hiddenCount = 0;
37677 // toolbar on the tabbar support...
37678 if (this.toolbar) {
37679 alert("no toolbar support yet");
37680 this.toolbar = false;
37682 var tcfg = this.toolbar;
37683 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37684 this.toolbar = new Roo.Toolbar(tcfg);
37685 if (Roo.isSafari) {
37686 var tbl = tcfg.container.child('table', true);
37687 tbl.setAttribute('width', '100%');
37695 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37698 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37700 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37702 tabPosition : "top",
37704 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37706 currentTabWidth : 0,
37708 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37712 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37716 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37718 preferredTabWidth : 175,
37720 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37722 resizeTabs : false,
37724 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37726 monitorResize : true,
37728 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37733 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37734 * @param {String} id The id of the div to use <b>or create</b>
37735 * @param {String} text The text for the tab
37736 * @param {String} content (optional) Content to put in the TabPanelItem body
37737 * @param {Boolean} closable (optional) True to create a close icon on the tab
37738 * @return {Roo.TabPanelItem} The created TabPanelItem
37740 addTab : function(id, text, content, closable, tpl)
37742 var item = new Roo.bootstrap.panel.TabItem({
37746 closable : closable,
37749 this.addTabItem(item);
37751 item.setContent(content);
37757 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37758 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37759 * @return {Roo.TabPanelItem}
37761 getTab : function(id){
37762 return this.items[id];
37766 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37767 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37769 hideTab : function(id){
37770 var t = this.items[id];
37773 this.hiddenCount++;
37774 this.autoSizeTabs();
37779 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37780 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37782 unhideTab : function(id){
37783 var t = this.items[id];
37785 t.setHidden(false);
37786 this.hiddenCount--;
37787 this.autoSizeTabs();
37792 * Adds an existing {@link Roo.TabPanelItem}.
37793 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37795 addTabItem : function(item){
37796 this.items[item.id] = item;
37797 this.items.push(item);
37798 // if(this.resizeTabs){
37799 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37800 // this.autoSizeTabs();
37802 // item.autoSize();
37807 * Removes a {@link Roo.TabPanelItem}.
37808 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37810 removeTab : function(id){
37811 var items = this.items;
37812 var tab = items[id];
37813 if(!tab) { return; }
37814 var index = items.indexOf(tab);
37815 if(this.active == tab && items.length > 1){
37816 var newTab = this.getNextAvailable(index);
37821 this.stripEl.dom.removeChild(tab.pnode.dom);
37822 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37823 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37825 items.splice(index, 1);
37826 delete this.items[tab.id];
37827 tab.fireEvent("close", tab);
37828 tab.purgeListeners();
37829 this.autoSizeTabs();
37832 getNextAvailable : function(start){
37833 var items = this.items;
37835 // look for a next tab that will slide over to
37836 // replace the one being removed
37837 while(index < items.length){
37838 var item = items[++index];
37839 if(item && !item.isHidden()){
37843 // if one isn't found select the previous tab (on the left)
37846 var item = items[--index];
37847 if(item && !item.isHidden()){
37855 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37856 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37858 disableTab : function(id){
37859 var tab = this.items[id];
37860 if(tab && this.active != tab){
37866 * Enables a {@link Roo.TabPanelItem} that is disabled.
37867 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37869 enableTab : function(id){
37870 var tab = this.items[id];
37875 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37876 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37877 * @return {Roo.TabPanelItem} The TabPanelItem.
37879 activate : function(id){
37880 var tab = this.items[id];
37884 if(tab == this.active || tab.disabled){
37888 this.fireEvent("beforetabchange", this, e, tab);
37889 if(e.cancel !== true && !tab.disabled){
37891 this.active.hide();
37893 this.active = this.items[id];
37894 this.active.show();
37895 this.fireEvent("tabchange", this, this.active);
37901 * Gets the active {@link Roo.TabPanelItem}.
37902 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37904 getActiveTab : function(){
37905 return this.active;
37909 * Updates the tab body element to fit the height of the container element
37910 * for overflow scrolling
37911 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37913 syncHeight : function(targetHeight){
37914 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37915 var bm = this.bodyEl.getMargins();
37916 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37917 this.bodyEl.setHeight(newHeight);
37921 onResize : function(){
37922 if(this.monitorResize){
37923 this.autoSizeTabs();
37928 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37930 beginUpdate : function(){
37931 this.updating = true;
37935 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37937 endUpdate : function(){
37938 this.updating = false;
37939 this.autoSizeTabs();
37943 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37945 autoSizeTabs : function(){
37946 var count = this.items.length;
37947 var vcount = count - this.hiddenCount;
37948 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37951 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37952 var availWidth = Math.floor(w / vcount);
37953 var b = this.stripBody;
37954 if(b.getWidth() > w){
37955 var tabs = this.items;
37956 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37957 if(availWidth < this.minTabWidth){
37958 /*if(!this.sleft){ // incomplete scrolling code
37959 this.createScrollButtons();
37962 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37965 if(this.currentTabWidth < this.preferredTabWidth){
37966 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37972 * Returns the number of tabs in this TabPanel.
37975 getCount : function(){
37976 return this.items.length;
37980 * Resizes all the tabs to the passed width
37981 * @param {Number} The new width
37983 setTabWidth : function(width){
37984 this.currentTabWidth = width;
37985 for(var i = 0, len = this.items.length; i < len; i++) {
37986 if(!this.items[i].isHidden()) {
37987 this.items[i].setWidth(width);
37993 * Destroys this TabPanel
37994 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37996 destroy : function(removeEl){
37997 Roo.EventManager.removeResizeListener(this.onResize, this);
37998 for(var i = 0, len = this.items.length; i < len; i++){
37999 this.items[i].purgeListeners();
38001 if(removeEl === true){
38002 this.el.update("");
38007 createStrip : function(container)
38009 var strip = document.createElement("nav");
38010 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38011 container.appendChild(strip);
38015 createStripList : function(strip)
38017 // div wrapper for retard IE
38018 // returns the "tr" element.
38019 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38020 //'<div class="x-tabs-strip-wrap">'+
38021 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38022 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38023 return strip.firstChild; //.firstChild.firstChild.firstChild;
38025 createBody : function(container)
38027 var body = document.createElement("div");
38028 Roo.id(body, "tab-body");
38029 //Roo.fly(body).addClass("x-tabs-body");
38030 Roo.fly(body).addClass("tab-content");
38031 container.appendChild(body);
38034 createItemBody :function(bodyEl, id){
38035 var body = Roo.getDom(id);
38037 body = document.createElement("div");
38040 //Roo.fly(body).addClass("x-tabs-item-body");
38041 Roo.fly(body).addClass("tab-pane");
38042 bodyEl.insertBefore(body, bodyEl.firstChild);
38046 createStripElements : function(stripEl, text, closable, tpl)
38048 var td = document.createElement("li"); // was td..
38051 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38054 stripEl.appendChild(td);
38056 td.className = "x-tabs-closable";
38057 if(!this.closeTpl){
38058 this.closeTpl = new Roo.Template(
38059 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38060 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38061 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38064 var el = this.closeTpl.overwrite(td, {"text": text});
38065 var close = el.getElementsByTagName("div")[0];
38066 var inner = el.getElementsByTagName("em")[0];
38067 return {"el": el, "close": close, "inner": inner};
38070 // not sure what this is..
38071 // if(!this.tabTpl){
38072 //this.tabTpl = new Roo.Template(
38073 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38074 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38076 // this.tabTpl = new Roo.Template(
38077 // '<a href="#">' +
38078 // '<span unselectable="on"' +
38079 // (this.disableTooltips ? '' : ' title="{text}"') +
38080 // ' >{text}</span></a>'
38086 var template = tpl || this.tabTpl || false;
38090 template = new Roo.Template(
38092 '<span unselectable="on"' +
38093 (this.disableTooltips ? '' : ' title="{text}"') +
38094 ' >{text}</span></a>'
38098 switch (typeof(template)) {
38102 template = new Roo.Template(template);
38108 var el = template.overwrite(td, {"text": text});
38110 var inner = el.getElementsByTagName("span")[0];
38112 return {"el": el, "inner": inner};
38120 * @class Roo.TabPanelItem
38121 * @extends Roo.util.Observable
38122 * Represents an individual item (tab plus body) in a TabPanel.
38123 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38124 * @param {String} id The id of this TabPanelItem
38125 * @param {String} text The text for the tab of this TabPanelItem
38126 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38128 Roo.bootstrap.panel.TabItem = function(config){
38130 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38131 * @type Roo.TabPanel
38133 this.tabPanel = config.panel;
38135 * The id for this TabPanelItem
38138 this.id = config.id;
38140 this.disabled = false;
38142 this.text = config.text;
38144 this.loaded = false;
38145 this.closable = config.closable;
38148 * The body element for this TabPanelItem.
38149 * @type Roo.Element
38151 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38152 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38153 this.bodyEl.setStyle("display", "block");
38154 this.bodyEl.setStyle("zoom", "1");
38155 //this.hideAction();
38157 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38159 this.el = Roo.get(els.el);
38160 this.inner = Roo.get(els.inner, true);
38161 this.textEl = Roo.get(this.el.dom.firstChild, true);
38162 this.pnode = Roo.get(els.el.parentNode, true);
38163 // this.el.on("mousedown", this.onTabMouseDown, this);
38164 this.el.on("click", this.onTabClick, this);
38166 if(config.closable){
38167 var c = Roo.get(els.close, true);
38168 c.dom.title = this.closeText;
38169 c.addClassOnOver("close-over");
38170 c.on("click", this.closeClick, this);
38176 * Fires when this tab becomes the active tab.
38177 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38178 * @param {Roo.TabPanelItem} this
38182 * @event beforeclose
38183 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38184 * @param {Roo.TabPanelItem} this
38185 * @param {Object} e Set cancel to true on this object to cancel the close.
38187 "beforeclose": true,
38190 * Fires when this tab is closed.
38191 * @param {Roo.TabPanelItem} this
38195 * @event deactivate
38196 * Fires when this tab is no longer the active tab.
38197 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38198 * @param {Roo.TabPanelItem} this
38200 "deactivate" : true
38202 this.hidden = false;
38204 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38207 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38209 purgeListeners : function(){
38210 Roo.util.Observable.prototype.purgeListeners.call(this);
38211 this.el.removeAllListeners();
38214 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38217 this.pnode.addClass("active");
38220 this.tabPanel.stripWrap.repaint();
38222 this.fireEvent("activate", this.tabPanel, this);
38226 * Returns true if this tab is the active tab.
38227 * @return {Boolean}
38229 isActive : function(){
38230 return this.tabPanel.getActiveTab() == this;
38234 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38237 this.pnode.removeClass("active");
38239 this.fireEvent("deactivate", this.tabPanel, this);
38242 hideAction : function(){
38243 this.bodyEl.hide();
38244 this.bodyEl.setStyle("position", "absolute");
38245 this.bodyEl.setLeft("-20000px");
38246 this.bodyEl.setTop("-20000px");
38249 showAction : function(){
38250 this.bodyEl.setStyle("position", "relative");
38251 this.bodyEl.setTop("");
38252 this.bodyEl.setLeft("");
38253 this.bodyEl.show();
38257 * Set the tooltip for the tab.
38258 * @param {String} tooltip The tab's tooltip
38260 setTooltip : function(text){
38261 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38262 this.textEl.dom.qtip = text;
38263 this.textEl.dom.removeAttribute('title');
38265 this.textEl.dom.title = text;
38269 onTabClick : function(e){
38270 e.preventDefault();
38271 this.tabPanel.activate(this.id);
38274 onTabMouseDown : function(e){
38275 e.preventDefault();
38276 this.tabPanel.activate(this.id);
38279 getWidth : function(){
38280 return this.inner.getWidth();
38283 setWidth : function(width){
38284 var iwidth = width - this.pnode.getPadding("lr");
38285 this.inner.setWidth(iwidth);
38286 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38287 this.pnode.setWidth(width);
38291 * Show or hide the tab
38292 * @param {Boolean} hidden True to hide or false to show.
38294 setHidden : function(hidden){
38295 this.hidden = hidden;
38296 this.pnode.setStyle("display", hidden ? "none" : "");
38300 * Returns true if this tab is "hidden"
38301 * @return {Boolean}
38303 isHidden : function(){
38304 return this.hidden;
38308 * Returns the text for this tab
38311 getText : function(){
38315 autoSize : function(){
38316 //this.el.beginMeasure();
38317 this.textEl.setWidth(1);
38319 * #2804 [new] Tabs in Roojs
38320 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38322 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38323 //this.el.endMeasure();
38327 * Sets the text for the tab (Note: this also sets the tooltip text)
38328 * @param {String} text The tab's text and tooltip
38330 setText : function(text){
38332 this.textEl.update(text);
38333 this.setTooltip(text);
38334 //if(!this.tabPanel.resizeTabs){
38335 // this.autoSize();
38339 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38341 activate : function(){
38342 this.tabPanel.activate(this.id);
38346 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38348 disable : function(){
38349 if(this.tabPanel.active != this){
38350 this.disabled = true;
38351 this.pnode.addClass("disabled");
38356 * Enables this TabPanelItem if it was previously disabled.
38358 enable : function(){
38359 this.disabled = false;
38360 this.pnode.removeClass("disabled");
38364 * Sets the content for this TabPanelItem.
38365 * @param {String} content The content
38366 * @param {Boolean} loadScripts true to look for and load scripts
38368 setContent : function(content, loadScripts){
38369 this.bodyEl.update(content, loadScripts);
38373 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38374 * @return {Roo.UpdateManager} The UpdateManager
38376 getUpdateManager : function(){
38377 return this.bodyEl.getUpdateManager();
38381 * Set a URL to be used to load the content for this TabPanelItem.
38382 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38383 * @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)
38384 * @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)
38385 * @return {Roo.UpdateManager} The UpdateManager
38387 setUrl : function(url, params, loadOnce){
38388 if(this.refreshDelegate){
38389 this.un('activate', this.refreshDelegate);
38391 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38392 this.on("activate", this.refreshDelegate);
38393 return this.bodyEl.getUpdateManager();
38397 _handleRefresh : function(url, params, loadOnce){
38398 if(!loadOnce || !this.loaded){
38399 var updater = this.bodyEl.getUpdateManager();
38400 updater.update(url, params, this._setLoaded.createDelegate(this));
38405 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38406 * Will fail silently if the setUrl method has not been called.
38407 * This does not activate the panel, just updates its content.
38409 refresh : function(){
38410 if(this.refreshDelegate){
38411 this.loaded = false;
38412 this.refreshDelegate();
38417 _setLoaded : function(){
38418 this.loaded = true;
38422 closeClick : function(e){
38425 this.fireEvent("beforeclose", this, o);
38426 if(o.cancel !== true){
38427 this.tabPanel.removeTab(this.id);
38431 * The text displayed in the tooltip for the close icon.
38434 closeText : "Close this tab"
38437 * This script refer to:
38438 * Title: International Telephone Input
38439 * Author: Jack O'Connor
38440 * Code version: v12.1.12
38441 * Availability: https://github.com/jackocnr/intl-tel-input.git
38444 Roo.bootstrap.PhoneInputData = function() {
38447 "Afghanistan (افغانستان)",
38452 "Albania (Shqipëri)",
38457 "Algeria (الجزائر)",
38482 "Antigua and Barbuda",
38492 "Armenia (Հայաստան)",
38508 "Austria (Österreich)",
38513 "Azerbaijan (Azərbaycan)",
38523 "Bahrain (البحرين)",
38528 "Bangladesh (বাংলাদেশ)",
38538 "Belarus (Беларусь)",
38543 "Belgium (België)",
38573 "Bosnia and Herzegovina (Босна и Херцеговина)",
38588 "British Indian Ocean Territory",
38593 "British Virgin Islands",
38603 "Bulgaria (България)",
38613 "Burundi (Uburundi)",
38618 "Cambodia (កម្ពុជា)",
38623 "Cameroon (Cameroun)",
38632 ["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"]
38635 "Cape Verde (Kabu Verdi)",
38640 "Caribbean Netherlands",
38651 "Central African Republic (République centrafricaine)",
38671 "Christmas Island",
38677 "Cocos (Keeling) Islands",
38688 "Comoros (جزر القمر)",
38693 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38698 "Congo (Republic) (Congo-Brazzaville)",
38718 "Croatia (Hrvatska)",
38739 "Czech Republic (Česká republika)",
38744 "Denmark (Danmark)",
38759 "Dominican Republic (República Dominicana)",
38763 ["809", "829", "849"]
38781 "Equatorial Guinea (Guinea Ecuatorial)",
38801 "Falkland Islands (Islas Malvinas)",
38806 "Faroe Islands (Føroyar)",
38827 "French Guiana (Guyane française)",
38832 "French Polynesia (Polynésie française)",
38847 "Georgia (საქართველო)",
38852 "Germany (Deutschland)",
38872 "Greenland (Kalaallit Nunaat)",
38909 "Guinea-Bissau (Guiné Bissau)",
38934 "Hungary (Magyarország)",
38939 "Iceland (Ísland)",
38959 "Iraq (العراق)",
38975 "Israel (ישראל)",
39002 "Jordan (الأردن)",
39007 "Kazakhstan (Казахстан)",
39028 "Kuwait (الكويت)",
39033 "Kyrgyzstan (Кыргызстан)",
39043 "Latvia (Latvija)",
39048 "Lebanon (لبنان)",
39063 "Libya (ليبيا)",
39073 "Lithuania (Lietuva)",
39088 "Macedonia (FYROM) (Македонија)",
39093 "Madagascar (Madagasikara)",
39123 "Marshall Islands",
39133 "Mauritania (موريتانيا)",
39138 "Mauritius (Moris)",
39159 "Moldova (Republica Moldova)",
39169 "Mongolia (Монгол)",
39174 "Montenegro (Crna Gora)",
39184 "Morocco (المغرب)",
39190 "Mozambique (Moçambique)",
39195 "Myanmar (Burma) (မြန်မာ)",
39200 "Namibia (Namibië)",
39215 "Netherlands (Nederland)",
39220 "New Caledonia (Nouvelle-Calédonie)",
39255 "North Korea (조선 민주주의 인민 공화국)",
39260 "Northern Mariana Islands",
39276 "Pakistan (پاکستان)",
39286 "Palestine (فلسطين)",
39296 "Papua New Guinea",
39338 "Réunion (La Réunion)",
39344 "Romania (România)",
39360 "Saint Barthélemy",
39371 "Saint Kitts and Nevis",
39381 "Saint Martin (Saint-Martin (partie française))",
39387 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39392 "Saint Vincent and the Grenadines",
39407 "São Tomé and Príncipe (São Tomé e Príncipe)",
39412 "Saudi Arabia (المملكة العربية السعودية)",
39417 "Senegal (Sénégal)",
39447 "Slovakia (Slovensko)",
39452 "Slovenia (Slovenija)",
39462 "Somalia (Soomaaliya)",
39472 "South Korea (대한민국)",
39477 "South Sudan (جنوب السودان)",
39487 "Sri Lanka (ශ්රී ලංකාව)",
39492 "Sudan (السودان)",
39502 "Svalbard and Jan Mayen",
39513 "Sweden (Sverige)",
39518 "Switzerland (Schweiz)",
39523 "Syria (سوريا)",
39568 "Trinidad and Tobago",
39573 "Tunisia (تونس)",
39578 "Turkey (Türkiye)",
39588 "Turks and Caicos Islands",
39598 "U.S. Virgin Islands",
39608 "Ukraine (Україна)",
39613 "United Arab Emirates (الإمارات العربية المتحدة)",
39635 "Uzbekistan (Oʻzbekiston)",
39645 "Vatican City (Città del Vaticano)",
39656 "Vietnam (Việt Nam)",
39661 "Wallis and Futuna (Wallis-et-Futuna)",
39666 "Western Sahara (الصحراء الغربية)",
39672 "Yemen (اليمن)",
39696 * This script refer to:
39697 * Title: International Telephone Input
39698 * Author: Jack O'Connor
39699 * Code version: v12.1.12
39700 * Availability: https://github.com/jackocnr/intl-tel-input.git
39704 * @class Roo.bootstrap.PhoneInput
39705 * @extends Roo.bootstrap.TriggerField
39706 * An input with International dial-code selection
39708 * @cfg {String} defaultDialCode default '+852'
39709 * @cfg {Array} preferedCountries default []
39712 * Create a new PhoneInput.
39713 * @param {Object} config Configuration options
39716 Roo.bootstrap.PhoneInput = function(config) {
39717 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39720 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39722 listWidth: undefined,
39724 selectedClass: 'active',
39726 invalidClass : "has-warning",
39728 validClass: 'has-success',
39730 allowed: '0123456789',
39733 * @cfg {String} defaultDialCode The default dial code when initializing the input
39735 defaultDialCode: '+852',
39738 * @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
39740 preferedCountries: false,
39742 getAutoCreate : function()
39744 var data = Roo.bootstrap.PhoneInputData();
39745 var align = this.labelAlign || this.parentLabelAlign();
39748 this.allCountries = [];
39749 this.dialCodeMapping = [];
39751 for (var i = 0; i < data.length; i++) {
39753 this.allCountries[i] = {
39757 priority: c[3] || 0,
39758 areaCodes: c[4] || null
39760 this.dialCodeMapping[c[2]] = {
39763 priority: c[3] || 0,
39764 areaCodes: c[4] || null
39776 cls : 'form-control tel-input',
39777 autocomplete: 'new-password'
39780 var hiddenInput = {
39783 cls: 'hidden-tel-input'
39787 hiddenInput.name = this.name;
39790 if (this.disabled) {
39791 input.disabled = true;
39794 var flag_container = {
39811 cls: this.hasFeedback ? 'has-feedback' : '',
39817 cls: 'dial-code-holder',
39824 cls: 'roo-select2-container input-group',
39831 if (this.fieldLabel.length) {
39834 tooltip: 'This field is required'
39840 cls: 'control-label',
39846 html: this.fieldLabel
39849 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39855 if(this.indicatorpos == 'right') {
39856 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39863 if(align == 'left') {
39871 if(this.labelWidth > 12){
39872 label.style = "width: " + this.labelWidth + 'px';
39874 if(this.labelWidth < 13 && this.labelmd == 0){
39875 this.labelmd = this.labelWidth;
39877 if(this.labellg > 0){
39878 label.cls += ' col-lg-' + this.labellg;
39879 input.cls += ' col-lg-' + (12 - this.labellg);
39881 if(this.labelmd > 0){
39882 label.cls += ' col-md-' + this.labelmd;
39883 container.cls += ' col-md-' + (12 - this.labelmd);
39885 if(this.labelsm > 0){
39886 label.cls += ' col-sm-' + this.labelsm;
39887 container.cls += ' col-sm-' + (12 - this.labelsm);
39889 if(this.labelxs > 0){
39890 label.cls += ' col-xs-' + this.labelxs;
39891 container.cls += ' col-xs-' + (12 - this.labelxs);
39901 var settings = this;
39903 ['xs','sm','md','lg'].map(function(size){
39904 if (settings[size]) {
39905 cfg.cls += ' col-' + size + '-' + settings[size];
39909 this.store = new Roo.data.Store({
39910 proxy : new Roo.data.MemoryProxy({}),
39911 reader : new Roo.data.JsonReader({
39922 'name' : 'dialCode',
39926 'name' : 'priority',
39930 'name' : 'areaCodes',
39937 if(!this.preferedCountries) {
39938 this.preferedCountries = [
39945 var p = this.preferedCountries.reverse();
39948 for (var i = 0; i < p.length; i++) {
39949 for (var j = 0; j < this.allCountries.length; j++) {
39950 if(this.allCountries[j].iso2 == p[i]) {
39951 var t = this.allCountries[j];
39952 this.allCountries.splice(j,1);
39953 this.allCountries.unshift(t);
39959 this.store.proxy.data = {
39961 data: this.allCountries
39967 initEvents : function()
39970 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39972 this.indicator = this.indicatorEl();
39973 this.flag = this.flagEl();
39974 this.dialCodeHolder = this.dialCodeHolderEl();
39976 this.trigger = this.el.select('div.flag-box',true).first();
39977 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39982 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39983 _this.list.setWidth(lw);
39986 this.list.on('mouseover', this.onViewOver, this);
39987 this.list.on('mousemove', this.onViewMove, this);
39988 this.inputEl().on("keyup", this.onKeyUp, this);
39990 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39992 this.view = new Roo.View(this.list, this.tpl, {
39993 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39996 this.view.on('click', this.onViewClick, this);
39997 this.setValue(this.defaultDialCode);
40000 onTriggerClick : function(e)
40002 Roo.log('trigger click');
40007 if(this.isExpanded()){
40009 this.hasFocus = false;
40011 this.store.load({});
40012 this.hasFocus = true;
40017 isExpanded : function()
40019 return this.list.isVisible();
40022 collapse : function()
40024 if(!this.isExpanded()){
40028 Roo.get(document).un('mousedown', this.collapseIf, this);
40029 Roo.get(document).un('mousewheel', this.collapseIf, this);
40030 this.fireEvent('collapse', this);
40034 expand : function()
40038 if(this.isExpanded() || !this.hasFocus){
40042 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40043 this.list.setWidth(lw);
40046 this.restrictHeight();
40048 Roo.get(document).on('mousedown', this.collapseIf, this);
40049 Roo.get(document).on('mousewheel', this.collapseIf, this);
40051 this.fireEvent('expand', this);
40054 restrictHeight : function()
40056 this.list.alignTo(this.inputEl(), this.listAlign);
40057 this.list.alignTo(this.inputEl(), this.listAlign);
40060 onViewOver : function(e, t)
40062 if(this.inKeyMode){
40065 var item = this.view.findItemFromChild(t);
40068 var index = this.view.indexOf(item);
40069 this.select(index, false);
40074 onViewClick : function(view, doFocus, el, e)
40076 var index = this.view.getSelectedIndexes()[0];
40078 var r = this.store.getAt(index);
40081 this.onSelect(r, index);
40083 if(doFocus !== false && !this.blockFocus){
40084 this.inputEl().focus();
40088 onViewMove : function(e, t)
40090 this.inKeyMode = false;
40093 select : function(index, scrollIntoView)
40095 this.selectedIndex = index;
40096 this.view.select(index);
40097 if(scrollIntoView !== false){
40098 var el = this.view.getNode(index);
40100 this.list.scrollChildIntoView(el, false);
40105 createList : function()
40107 this.list = Roo.get(document.body).createChild({
40109 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40110 style: 'display:none'
40113 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40116 collapseIf : function(e)
40118 var in_combo = e.within(this.el);
40119 var in_list = e.within(this.list);
40120 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40122 if (in_combo || in_list || is_list) {
40128 onSelect : function(record, index)
40130 if(this.fireEvent('beforeselect', this, record, index) !== false){
40132 this.setFlagClass(record.data.iso2);
40133 this.setDialCode(record.data.dialCode);
40134 this.hasFocus = false;
40136 this.fireEvent('select', this, record, index);
40140 flagEl : function()
40142 var flag = this.el.select('div.flag',true).first();
40149 dialCodeHolderEl : function()
40151 var d = this.el.select('input.dial-code-holder',true).first();
40158 setDialCode : function(v)
40160 this.dialCodeHolder.dom.value = '+'+v;
40163 setFlagClass : function(n)
40165 this.flag.dom.className = 'flag '+n;
40168 getValue : function()
40170 var v = this.inputEl().getValue();
40171 if(this.dialCodeHolder) {
40172 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40177 setValue : function(v)
40179 var d = this.getDialCode(v);
40181 //invalid dial code
40182 if(v.length == 0 || !d || d.length == 0) {
40184 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40185 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40191 this.setFlagClass(this.dialCodeMapping[d].iso2);
40192 this.setDialCode(d);
40193 this.inputEl().dom.value = v.replace('+'+d,'');
40194 this.hiddenEl().dom.value = this.getValue();
40199 getDialCode : function(v)
40203 if (v.length == 0) {
40204 return this.dialCodeHolder.dom.value;
40208 if (v.charAt(0) != "+") {
40211 var numericChars = "";
40212 for (var i = 1; i < v.length; i++) {
40213 var c = v.charAt(i);
40216 if (this.dialCodeMapping[numericChars]) {
40217 dialCode = v.substr(1, i);
40219 if (numericChars.length == 4) {
40229 this.setValue(this.defaultDialCode);
40233 hiddenEl : function()
40235 return this.el.select('input.hidden-tel-input',true).first();
40238 onKeyUp : function(e){
40240 var k = e.getKey();
40241 var c = e.getCharCode();
40244 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40245 this.allowed.indexOf(String.fromCharCode(c)) === -1
40250 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40253 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40257 this.setValue(this.getValue());
40262 * @class Roo.bootstrap.MoneyField
40263 * @extends Roo.bootstrap.ComboBox
40264 * Bootstrap MoneyField class
40267 * Create a new MoneyField.
40268 * @param {Object} config Configuration options
40271 Roo.bootstrap.MoneyField = function(config) {
40273 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40277 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40280 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40282 allowDecimals : true,
40284 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40286 decimalSeparator : ".",
40288 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40290 decimalPrecision : 0,
40292 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40294 allowNegative : true,
40296 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40300 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40302 minValue : Number.NEGATIVE_INFINITY,
40304 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40306 maxValue : Number.MAX_VALUE,
40308 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40310 minText : "The minimum value for this field is {0}",
40312 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40314 maxText : "The maximum value for this field is {0}",
40316 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40317 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40319 nanText : "{0} is not a valid number",
40321 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40325 * @cfg {String} defaults currency of the MoneyField
40326 * value should be in lkey
40328 defaultCurrency : false,
40330 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40332 thousandsDelimiter : false,
40342 getAutoCreate : function()
40344 var align = this.labelAlign || this.parentLabelAlign();
40356 cls : 'form-control roo-money-amount-input',
40357 autocomplete: 'new-password'
40360 var hiddenInput = {
40364 cls: 'hidden-number-input'
40368 hiddenInput.name = this.name;
40371 if (this.disabled) {
40372 input.disabled = true;
40375 var clg = 12 - this.inputlg;
40376 var cmd = 12 - this.inputmd;
40377 var csm = 12 - this.inputsm;
40378 var cxs = 12 - this.inputxs;
40382 cls : 'row roo-money-field',
40386 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40390 cls: 'roo-select2-container input-group',
40394 cls : 'form-control roo-money-currency-input',
40395 autocomplete: 'new-password',
40397 name : this.currencyName
40401 cls : 'input-group-addon',
40415 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40419 cls: this.hasFeedback ? 'has-feedback' : '',
40430 if (this.fieldLabel.length) {
40433 tooltip: 'This field is required'
40439 cls: 'control-label',
40445 html: this.fieldLabel
40448 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40454 if(this.indicatorpos == 'right') {
40455 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40462 if(align == 'left') {
40470 if(this.labelWidth > 12){
40471 label.style = "width: " + this.labelWidth + 'px';
40473 if(this.labelWidth < 13 && this.labelmd == 0){
40474 this.labelmd = this.labelWidth;
40476 if(this.labellg > 0){
40477 label.cls += ' col-lg-' + this.labellg;
40478 input.cls += ' col-lg-' + (12 - this.labellg);
40480 if(this.labelmd > 0){
40481 label.cls += ' col-md-' + this.labelmd;
40482 container.cls += ' col-md-' + (12 - this.labelmd);
40484 if(this.labelsm > 0){
40485 label.cls += ' col-sm-' + this.labelsm;
40486 container.cls += ' col-sm-' + (12 - this.labelsm);
40488 if(this.labelxs > 0){
40489 label.cls += ' col-xs-' + this.labelxs;
40490 container.cls += ' col-xs-' + (12 - this.labelxs);
40501 var settings = this;
40503 ['xs','sm','md','lg'].map(function(size){
40504 if (settings[size]) {
40505 cfg.cls += ' col-' + size + '-' + settings[size];
40512 initEvents : function()
40514 this.indicator = this.indicatorEl();
40516 this.initCurrencyEvent();
40518 this.initNumberEvent();
40521 initCurrencyEvent : function()
40524 throw "can not find store for combo";
40527 this.store = Roo.factory(this.store, Roo.data);
40528 this.store.parent = this;
40532 this.triggerEl = this.el.select('.input-group-addon', true).first();
40534 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40539 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40540 _this.list.setWidth(lw);
40543 this.list.on('mouseover', this.onViewOver, this);
40544 this.list.on('mousemove', this.onViewMove, this);
40545 this.list.on('scroll', this.onViewScroll, this);
40548 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40551 this.view = new Roo.View(this.list, this.tpl, {
40552 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40555 this.view.on('click', this.onViewClick, this);
40557 this.store.on('beforeload', this.onBeforeLoad, this);
40558 this.store.on('load', this.onLoad, this);
40559 this.store.on('loadexception', this.onLoadException, this);
40561 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40562 "up" : function(e){
40563 this.inKeyMode = true;
40567 "down" : function(e){
40568 if(!this.isExpanded()){
40569 this.onTriggerClick();
40571 this.inKeyMode = true;
40576 "enter" : function(e){
40579 if(this.fireEvent("specialkey", this, e)){
40580 this.onViewClick(false);
40586 "esc" : function(e){
40590 "tab" : function(e){
40593 if(this.fireEvent("specialkey", this, e)){
40594 this.onViewClick(false);
40602 doRelay : function(foo, bar, hname){
40603 if(hname == 'down' || this.scope.isExpanded()){
40604 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40612 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40616 initNumberEvent : function(e)
40618 this.inputEl().on("keydown" , this.fireKey, this);
40619 this.inputEl().on("focus", this.onFocus, this);
40620 this.inputEl().on("blur", this.onBlur, this);
40622 this.inputEl().relayEvent('keyup', this);
40624 if(this.indicator){
40625 this.indicator.addClass('invisible');
40628 this.originalValue = this.getValue();
40630 if(this.validationEvent == 'keyup'){
40631 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40632 this.inputEl().on('keyup', this.filterValidation, this);
40634 else if(this.validationEvent !== false){
40635 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40638 if(this.selectOnFocus){
40639 this.on("focus", this.preFocus, this);
40642 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40643 this.inputEl().on("keypress", this.filterKeys, this);
40645 this.inputEl().relayEvent('keypress', this);
40648 var allowed = "0123456789";
40650 if(this.allowDecimals){
40651 allowed += this.decimalSeparator;
40654 if(this.allowNegative){
40658 if(this.thousandsDelimiter) {
40662 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40664 var keyPress = function(e){
40666 var k = e.getKey();
40668 var c = e.getCharCode();
40671 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40672 allowed.indexOf(String.fromCharCode(c)) === -1
40678 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40682 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40687 this.inputEl().on("keypress", keyPress, this);
40691 onTriggerClick : function(e)
40698 this.loadNext = false;
40700 if(this.isExpanded()){
40705 this.hasFocus = true;
40707 if(this.triggerAction == 'all') {
40708 this.doQuery(this.allQuery, true);
40712 this.doQuery(this.getRawValue());
40715 getCurrency : function()
40717 var v = this.currencyEl().getValue();
40722 restrictHeight : function()
40724 this.list.alignTo(this.currencyEl(), this.listAlign);
40725 this.list.alignTo(this.currencyEl(), this.listAlign);
40728 onViewClick : function(view, doFocus, el, e)
40730 var index = this.view.getSelectedIndexes()[0];
40732 var r = this.store.getAt(index);
40735 this.onSelect(r, index);
40739 onSelect : function(record, index){
40741 if(this.fireEvent('beforeselect', this, record, index) !== false){
40743 this.setFromCurrencyData(index > -1 ? record.data : false);
40747 this.fireEvent('select', this, record, index);
40751 setFromCurrencyData : function(o)
40755 this.lastCurrency = o;
40757 if (this.currencyField) {
40758 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40760 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40763 this.lastSelectionText = currency;
40765 //setting default currency
40766 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40767 this.setCurrency(this.defaultCurrency);
40771 this.setCurrency(currency);
40774 setFromData : function(o)
40778 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40780 this.setFromCurrencyData(c);
40785 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40787 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40790 this.setValue(value);
40794 setCurrency : function(v)
40796 this.currencyValue = v;
40799 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40804 setValue : function(v)
40806 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40812 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40814 this.inputEl().dom.value = (v == '') ? '' :
40815 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40817 if(!this.allowZero && v === '0') {
40818 this.hiddenEl().dom.value = '';
40819 this.inputEl().dom.value = '';
40826 getRawValue : function()
40828 var v = this.inputEl().getValue();
40833 getValue : function()
40835 return this.fixPrecision(this.parseValue(this.getRawValue()));
40838 parseValue : function(value)
40840 if(this.thousandsDelimiter) {
40842 r = new RegExp(",", "g");
40843 value = value.replace(r, "");
40846 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40847 return isNaN(value) ? '' : value;
40851 fixPrecision : function(value)
40853 if(this.thousandsDelimiter) {
40855 r = new RegExp(",", "g");
40856 value = value.replace(r, "");
40859 var nan = isNaN(value);
40861 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40862 return nan ? '' : value;
40864 return parseFloat(value).toFixed(this.decimalPrecision);
40867 decimalPrecisionFcn : function(v)
40869 return Math.floor(v);
40872 validateValue : function(value)
40874 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40878 var num = this.parseValue(value);
40881 this.markInvalid(String.format(this.nanText, value));
40885 if(num < this.minValue){
40886 this.markInvalid(String.format(this.minText, this.minValue));
40890 if(num > this.maxValue){
40891 this.markInvalid(String.format(this.maxText, this.maxValue));
40898 validate : function()
40900 if(this.disabled || this.allowBlank){
40905 var currency = this.getCurrency();
40907 if(this.validateValue(this.getRawValue()) && currency.length){
40912 this.markInvalid();
40916 getName: function()
40921 beforeBlur : function()
40927 var v = this.parseValue(this.getRawValue());
40934 onBlur : function()
40938 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40939 //this.el.removeClass(this.focusClass);
40942 this.hasFocus = false;
40944 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40948 var v = this.getValue();
40950 if(String(v) !== String(this.startValue)){
40951 this.fireEvent('change', this, v, this.startValue);
40954 this.fireEvent("blur", this);
40957 inputEl : function()
40959 return this.el.select('.roo-money-amount-input', true).first();
40962 currencyEl : function()
40964 return this.el.select('.roo-money-currency-input', true).first();
40967 hiddenEl : function()
40969 return this.el.select('input.hidden-number-input',true).first();