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){
2833 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2835 Roo.log(this.height);
2836 Roo.log(Roo.lib.Dom.getViewportHeight(true));
2838 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2844 setSize : function(w,h)
2854 if (!this.rendered) {
2858 //this.el.setStyle('display', 'block');
2859 this.el.removeClass('hideing');
2860 this.el.addClass('show');
2862 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2865 this.el.addClass('in');
2868 this.el.addClass('in');
2871 // not sure how we can show data in here..
2873 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2876 Roo.get(document.body).addClass("x-body-masked");
2878 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2879 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2880 this.maskEl.addClass('show');
2884 this.fireEvent('show', this);
2886 // set zindex here - otherwise it appears to be ignored...
2887 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2890 this.items.forEach( function(e) {
2891 e.layout ? e.layout() : false;
2899 if(this.fireEvent("beforehide", this) !== false){
2900 this.maskEl.removeClass('show');
2901 Roo.get(document.body).removeClass("x-body-masked");
2902 this.el.removeClass('in');
2903 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2905 if(this.animate){ // why
2906 this.el.addClass('hideing');
2908 if (!this.el.hasClass('hideing')) {
2909 return; // it's been shown again...
2911 this.el.removeClass('show');
2912 this.el.removeClass('hideing');
2916 this.el.removeClass('show');
2918 this.fireEvent('hide', this);
2921 isVisible : function()
2924 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2928 addButton : function(str, cb)
2932 var b = Roo.apply({}, { html : str } );
2933 b.xns = b.xns || Roo.bootstrap;
2934 b.xtype = b.xtype || 'Button';
2935 if (typeof(b.listeners) == 'undefined') {
2936 b.listeners = { click : cb.createDelegate(this) };
2939 var btn = Roo.factory(b);
2941 btn.render(this.el.select('.modal-footer div').first());
2947 setDefaultButton : function(btn)
2949 //this.el.select('.modal-footer').()
2953 resizeTo: function(w,h)
2957 this.dialogEl.setWidth(w);
2958 if (this.diff === false) {
2959 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2962 this.bodyEl.setHeight(h-this.diff);
2964 this.fireEvent('resize', this);
2967 setContentSize : function(w, h)
2971 onButtonClick: function(btn,e)
2974 this.fireEvent('btnclick', btn.name, e);
2977 * Set the title of the Dialog
2978 * @param {String} str new Title
2980 setTitle: function(str) {
2981 this.titleEl.dom.innerHTML = str;
2984 * Set the body of the Dialog
2985 * @param {String} str new Title
2987 setBody: function(str) {
2988 this.bodyEl.dom.innerHTML = str;
2991 * Set the body of the Dialog using the template
2992 * @param {Obj} data - apply this data to the template and replace the body contents.
2994 applyBody: function(obj)
2997 Roo.log("Error - using apply Body without a template");
3000 this.tmpl.overwrite(this.bodyEl, obj);
3006 Roo.apply(Roo.bootstrap.Modal, {
3008 * Button config that displays a single OK button
3017 * Button config that displays Yes and No buttons
3033 * Button config that displays OK and Cancel buttons
3048 * Button config that displays Yes, No and Cancel buttons
3072 * messagebox - can be used as a replace
3076 * @class Roo.MessageBox
3077 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3081 Roo.Msg.alert('Status', 'Changes saved successfully.');
3083 // Prompt for user data:
3084 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3086 // process text value...
3090 // Show a dialog using config options:
3092 title:'Save Changes?',
3093 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3094 buttons: Roo.Msg.YESNOCANCEL,
3101 Roo.bootstrap.MessageBox = function(){
3102 var dlg, opt, mask, waitTimer;
3103 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3104 var buttons, activeTextEl, bwidth;
3108 var handleButton = function(button){
3110 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3114 var handleHide = function(){
3116 dlg.el.removeClass(opt.cls);
3119 // Roo.TaskMgr.stop(waitTimer);
3120 // waitTimer = null;
3125 var updateButtons = function(b){
3128 buttons["ok"].hide();
3129 buttons["cancel"].hide();
3130 buttons["yes"].hide();
3131 buttons["no"].hide();
3132 //dlg.footer.dom.style.display = 'none';
3135 dlg.footerEl.dom.style.display = '';
3136 for(var k in buttons){
3137 if(typeof buttons[k] != "function"){
3140 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3141 width += buttons[k].el.getWidth()+15;
3151 var handleEsc = function(d, k, e){
3152 if(opt && opt.closable !== false){
3162 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3163 * @return {Roo.BasicDialog} The BasicDialog element
3165 getDialog : function(){
3167 dlg = new Roo.bootstrap.Modal( {
3170 //constraintoviewport:false,
3172 //collapsible : false,
3177 //buttonAlign:"center",
3178 closeClick : function(){
3179 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3182 handleButton("cancel");
3187 dlg.on("hide", handleHide);
3189 //dlg.addKeyListener(27, handleEsc);
3191 this.buttons = buttons;
3192 var bt = this.buttonText;
3193 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3194 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3195 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3196 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3198 bodyEl = dlg.bodyEl.createChild({
3200 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3201 '<textarea class="roo-mb-textarea"></textarea>' +
3202 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3204 msgEl = bodyEl.dom.firstChild;
3205 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3206 textboxEl.enableDisplayMode();
3207 textboxEl.addKeyListener([10,13], function(){
3208 if(dlg.isVisible() && opt && opt.buttons){
3211 }else if(opt.buttons.yes){
3212 handleButton("yes");
3216 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3217 textareaEl.enableDisplayMode();
3218 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3219 progressEl.enableDisplayMode();
3221 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3222 var pf = progressEl.dom.firstChild;
3224 pp = Roo.get(pf.firstChild);
3225 pp.setHeight(pf.offsetHeight);
3233 * Updates the message box body text
3234 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3235 * the XHTML-compliant non-breaking space character '&#160;')
3236 * @return {Roo.MessageBox} This message box
3238 updateText : function(text)
3240 if(!dlg.isVisible() && !opt.width){
3241 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3242 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3244 msgEl.innerHTML = text || ' ';
3246 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3247 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3249 Math.min(opt.width || cw , this.maxWidth),
3250 Math.max(opt.minWidth || this.minWidth, bwidth)
3253 activeTextEl.setWidth(w);
3255 if(dlg.isVisible()){
3256 dlg.fixedcenter = false;
3258 // to big, make it scroll. = But as usual stupid IE does not support
3261 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3262 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3263 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3265 bodyEl.dom.style.height = '';
3266 bodyEl.dom.style.overflowY = '';
3269 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3271 bodyEl.dom.style.overflowX = '';
3274 dlg.setContentSize(w, bodyEl.getHeight());
3275 if(dlg.isVisible()){
3276 dlg.fixedcenter = true;
3282 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3283 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3284 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3285 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3286 * @return {Roo.MessageBox} This message box
3288 updateProgress : function(value, text){
3290 this.updateText(text);
3293 if (pp) { // weird bug on my firefox - for some reason this is not defined
3294 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3295 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3301 * Returns true if the message box is currently displayed
3302 * @return {Boolean} True if the message box is visible, else false
3304 isVisible : function(){
3305 return dlg && dlg.isVisible();
3309 * Hides the message box if it is displayed
3312 if(this.isVisible()){
3318 * Displays a new message box, or reinitializes an existing message box, based on the config options
3319 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3320 * The following config object properties are supported:
3322 Property Type Description
3323 ---------- --------------- ------------------------------------------------------------------------------------
3324 animEl String/Element An id or Element from which the message box should animate as it opens and
3325 closes (defaults to undefined)
3326 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3327 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3328 closable Boolean False to hide the top-right close button (defaults to true). Note that
3329 progress and wait dialogs will ignore this property and always hide the
3330 close button as they can only be closed programmatically.
3331 cls String A custom CSS class to apply to the message box element
3332 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3333 displayed (defaults to 75)
3334 fn Function A callback function to execute after closing the dialog. The arguments to the
3335 function will be btn (the name of the button that was clicked, if applicable,
3336 e.g. "ok"), and text (the value of the active text field, if applicable).
3337 Progress and wait dialogs will ignore this option since they do not respond to
3338 user actions and can only be closed programmatically, so any required function
3339 should be called by the same code after it closes the dialog.
3340 icon String A CSS class that provides a background image to be used as an icon for
3341 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3342 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3343 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3344 modal Boolean False to allow user interaction with the page while the message box is
3345 displayed (defaults to true)
3346 msg String A string that will replace the existing message box body text (defaults
3347 to the XHTML-compliant non-breaking space character ' ')
3348 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3349 progress Boolean True to display a progress bar (defaults to false)
3350 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3351 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3352 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3353 title String The title text
3354 value String The string value to set into the active textbox element if displayed
3355 wait Boolean True to display a progress bar (defaults to false)
3356 width Number The width of the dialog in pixels
3363 msg: 'Please enter your address:',
3365 buttons: Roo.MessageBox.OKCANCEL,
3368 animEl: 'addAddressBtn'
3371 * @param {Object} config Configuration options
3372 * @return {Roo.MessageBox} This message box
3374 show : function(options)
3377 // this causes nightmares if you show one dialog after another
3378 // especially on callbacks..
3380 if(this.isVisible()){
3383 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3384 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3385 Roo.log("New Dialog Message:" + options.msg )
3386 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3387 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3390 var d = this.getDialog();
3392 d.setTitle(opt.title || " ");
3393 d.closeEl.setDisplayed(opt.closable !== false);
3394 activeTextEl = textboxEl;
3395 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3400 textareaEl.setHeight(typeof opt.multiline == "number" ?
3401 opt.multiline : this.defaultTextHeight);
3402 activeTextEl = textareaEl;
3411 progressEl.setDisplayed(opt.progress === true);
3412 this.updateProgress(0);
3413 activeTextEl.dom.value = opt.value || "";
3415 dlg.setDefaultButton(activeTextEl);
3417 var bs = opt.buttons;
3421 }else if(bs && bs.yes){
3422 db = buttons["yes"];
3424 dlg.setDefaultButton(db);
3426 bwidth = updateButtons(opt.buttons);
3427 this.updateText(opt.msg);
3429 d.el.addClass(opt.cls);
3431 d.proxyDrag = opt.proxyDrag === true;
3432 d.modal = opt.modal !== false;
3433 d.mask = opt.modal !== false ? mask : false;
3435 // force it to the end of the z-index stack so it gets a cursor in FF
3436 document.body.appendChild(dlg.el.dom);
3437 d.animateTarget = null;
3438 d.show(options.animEl);
3444 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3445 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3446 * and closing the message box when the process is complete.
3447 * @param {String} title The title bar text
3448 * @param {String} msg The message box body text
3449 * @return {Roo.MessageBox} This message box
3451 progress : function(title, msg){
3458 minWidth: this.minProgressWidth,
3465 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3466 * If a callback function is passed it will be called after the user clicks the button, and the
3467 * id of the button that was clicked will be passed as the only parameter to the callback
3468 * (could also be the top-right close button).
3469 * @param {String} title The title bar text
3470 * @param {String} msg The message box body text
3471 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3472 * @param {Object} scope (optional) The scope of the callback function
3473 * @return {Roo.MessageBox} This message box
3475 alert : function(title, msg, fn, scope)
3490 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3491 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3492 * You are responsible for closing the message box when the process is complete.
3493 * @param {String} msg The message box body text
3494 * @param {String} title (optional) The title bar text
3495 * @return {Roo.MessageBox} This message box
3497 wait : function(msg, title){
3508 waitTimer = Roo.TaskMgr.start({
3510 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3518 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3519 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3520 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3521 * @param {String} title The title bar text
3522 * @param {String} msg The message box body text
3523 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3524 * @param {Object} scope (optional) The scope of the callback function
3525 * @return {Roo.MessageBox} This message box
3527 confirm : function(title, msg, fn, scope){
3531 buttons: this.YESNO,
3540 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3541 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3542 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3543 * (could also be the top-right close button) and the text that was entered will be passed as the two
3544 * parameters to the callback.
3545 * @param {String} title The title bar text
3546 * @param {String} msg The message box body text
3547 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3548 * @param {Object} scope (optional) The scope of the callback function
3549 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3550 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3551 * @return {Roo.MessageBox} This message box
3553 prompt : function(title, msg, fn, scope, multiline){
3557 buttons: this.OKCANCEL,
3562 multiline: multiline,
3569 * Button config that displays a single OK button
3574 * Button config that displays Yes and No buttons
3577 YESNO : {yes:true, no:true},
3579 * Button config that displays OK and Cancel buttons
3582 OKCANCEL : {ok:true, cancel:true},
3584 * Button config that displays Yes, No and Cancel buttons
3587 YESNOCANCEL : {yes:true, no:true, cancel:true},
3590 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3593 defaultTextHeight : 75,
3595 * The maximum width in pixels of the message box (defaults to 600)
3600 * The minimum width in pixels of the message box (defaults to 100)
3605 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3606 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3609 minProgressWidth : 250,
3611 * An object containing the default button text strings that can be overriden for localized language support.
3612 * Supported properties are: ok, cancel, yes and no.
3613 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3626 * Shorthand for {@link Roo.MessageBox}
3628 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3629 Roo.Msg = Roo.Msg || Roo.MessageBox;
3638 * @class Roo.bootstrap.Navbar
3639 * @extends Roo.bootstrap.Component
3640 * Bootstrap Navbar class
3643 * Create a new Navbar
3644 * @param {Object} config The config object
3648 Roo.bootstrap.Navbar = function(config){
3649 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3653 * @event beforetoggle
3654 * Fire before toggle the menu
3655 * @param {Roo.EventObject} e
3657 "beforetoggle" : true
3661 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3670 getAutoCreate : function(){
3673 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3677 initEvents :function ()
3679 //Roo.log(this.el.select('.navbar-toggle',true));
3680 this.el.select('.navbar-toggle',true).on('click', function() {
3681 if(this.fireEvent('beforetoggle', this) !== false){
3682 this.el.select('.navbar-collapse',true).toggleClass('in');
3692 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3694 var size = this.el.getSize();
3695 this.maskEl.setSize(size.width, size.height);
3696 this.maskEl.enableDisplayMode("block");
3705 getChildContainer : function()
3707 if (this.el.select('.collapse').getCount()) {
3708 return this.el.select('.collapse',true).first();
3741 * @class Roo.bootstrap.NavSimplebar
3742 * @extends Roo.bootstrap.Navbar
3743 * Bootstrap Sidebar class
3745 * @cfg {Boolean} inverse is inverted color
3747 * @cfg {String} type (nav | pills | tabs)
3748 * @cfg {Boolean} arrangement stacked | justified
3749 * @cfg {String} align (left | right) alignment
3751 * @cfg {Boolean} main (true|false) main nav bar? default false
3752 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3754 * @cfg {String} tag (header|footer|nav|div) default is nav
3760 * Create a new Sidebar
3761 * @param {Object} config The config object
3765 Roo.bootstrap.NavSimplebar = function(config){
3766 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3769 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3785 getAutoCreate : function(){
3789 tag : this.tag || 'div',
3802 this.type = this.type || 'nav';
3803 if (['tabs','pills'].indexOf(this.type)!==-1) {
3804 cfg.cn[0].cls += ' nav-' + this.type
3808 if (this.type!=='nav') {
3809 Roo.log('nav type must be nav/tabs/pills')
3811 cfg.cn[0].cls += ' navbar-nav'
3817 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3818 cfg.cn[0].cls += ' nav-' + this.arrangement;
3822 if (this.align === 'right') {
3823 cfg.cn[0].cls += ' navbar-right';
3827 cfg.cls += ' navbar-inverse';
3854 * @class Roo.bootstrap.NavHeaderbar
3855 * @extends Roo.bootstrap.NavSimplebar
3856 * Bootstrap Sidebar class
3858 * @cfg {String} brand what is brand
3859 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3860 * @cfg {String} brand_href href of the brand
3861 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3862 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3863 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3864 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3867 * Create a new Sidebar
3868 * @param {Object} config The config object
3872 Roo.bootstrap.NavHeaderbar = function(config){
3873 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3877 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3884 desktopCenter : false,
3887 getAutoCreate : function(){
3890 tag: this.nav || 'nav',
3897 if (this.desktopCenter) {
3898 cn.push({cls : 'container', cn : []});
3905 cls: 'navbar-header',
3910 cls: 'navbar-toggle',
3911 'data-toggle': 'collapse',
3916 html: 'Toggle navigation'
3938 cls: 'collapse navbar-collapse',
3942 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3944 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3945 cfg.cls += ' navbar-' + this.position;
3947 // tag can override this..
3949 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3952 if (this.brand !== '') {
3955 href: this.brand_href ? this.brand_href : '#',
3956 cls: 'navbar-brand',
3964 cfg.cls += ' main-nav';
3972 getHeaderChildContainer : function()
3974 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3975 return this.el.select('.navbar-header',true).first();
3978 return this.getChildContainer();
3982 initEvents : function()
3984 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3986 if (this.autohide) {
3991 Roo.get(document).on('scroll',function(e) {
3992 var ns = Roo.get(document).getScroll().top;
3993 var os = prevScroll;
3997 ft.removeClass('slideDown');
3998 ft.addClass('slideUp');
4001 ft.removeClass('slideUp');
4002 ft.addClass('slideDown');
4023 * @class Roo.bootstrap.NavSidebar
4024 * @extends Roo.bootstrap.Navbar
4025 * Bootstrap Sidebar class
4028 * Create a new Sidebar
4029 * @param {Object} config The config object
4033 Roo.bootstrap.NavSidebar = function(config){
4034 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4037 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4039 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4041 getAutoCreate : function(){
4046 cls: 'sidebar sidebar-nav'
4068 * @class Roo.bootstrap.NavGroup
4069 * @extends Roo.bootstrap.Component
4070 * Bootstrap NavGroup class
4071 * @cfg {String} align (left|right)
4072 * @cfg {Boolean} inverse
4073 * @cfg {String} type (nav|pills|tab) default nav
4074 * @cfg {String} navId - reference Id for navbar.
4078 * Create a new nav group
4079 * @param {Object} config The config object
4082 Roo.bootstrap.NavGroup = function(config){
4083 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4086 Roo.bootstrap.NavGroup.register(this);
4090 * Fires when the active item changes
4091 * @param {Roo.bootstrap.NavGroup} this
4092 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4093 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4100 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4111 getAutoCreate : function()
4113 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4120 if (['tabs','pills'].indexOf(this.type)!==-1) {
4121 cfg.cls += ' nav-' + this.type
4123 if (this.type!=='nav') {
4124 Roo.log('nav type must be nav/tabs/pills')
4126 cfg.cls += ' navbar-nav'
4129 if (this.parent() && this.parent().sidebar) {
4132 cls: 'dashboard-menu sidebar-menu'
4138 if (this.form === true) {
4144 if (this.align === 'right') {
4145 cfg.cls += ' navbar-right';
4147 cfg.cls += ' navbar-left';
4151 if (this.align === 'right') {
4152 cfg.cls += ' navbar-right';
4156 cfg.cls += ' navbar-inverse';
4164 * sets the active Navigation item
4165 * @param {Roo.bootstrap.NavItem} the new current navitem
4167 setActiveItem : function(item)
4170 Roo.each(this.navItems, function(v){
4175 v.setActive(false, true);
4182 item.setActive(true, true);
4183 this.fireEvent('changed', this, item, prev);
4188 * gets the active Navigation item
4189 * @return {Roo.bootstrap.NavItem} the current navitem
4191 getActive : function()
4195 Roo.each(this.navItems, function(v){
4206 indexOfNav : function()
4210 Roo.each(this.navItems, function(v,i){
4221 * adds a Navigation item
4222 * @param {Roo.bootstrap.NavItem} the navitem to add
4224 addItem : function(cfg)
4226 var cn = new Roo.bootstrap.NavItem(cfg);
4228 cn.parentId = this.id;
4229 cn.onRender(this.el, null);
4233 * register a Navigation item
4234 * @param {Roo.bootstrap.NavItem} the navitem to add
4236 register : function(item)
4238 this.navItems.push( item);
4239 item.navId = this.navId;
4244 * clear all the Navigation item
4247 clearAll : function()
4250 this.el.dom.innerHTML = '';
4253 getNavItem: function(tabId)
4256 Roo.each(this.navItems, function(e) {
4257 if (e.tabId == tabId) {
4267 setActiveNext : function()
4269 var i = this.indexOfNav(this.getActive());
4270 if (i > this.navItems.length) {
4273 this.setActiveItem(this.navItems[i+1]);
4275 setActivePrev : function()
4277 var i = this.indexOfNav(this.getActive());
4281 this.setActiveItem(this.navItems[i-1]);
4283 clearWasActive : function(except) {
4284 Roo.each(this.navItems, function(e) {
4285 if (e.tabId != except.tabId && e.was_active) {
4286 e.was_active = false;
4293 getWasActive : function ()
4296 Roo.each(this.navItems, function(e) {
4311 Roo.apply(Roo.bootstrap.NavGroup, {
4315 * register a Navigation Group
4316 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4318 register : function(navgrp)
4320 this.groups[navgrp.navId] = navgrp;
4324 * fetch a Navigation Group based on the navigation ID
4325 * @param {string} the navgroup to add
4326 * @returns {Roo.bootstrap.NavGroup} the navgroup
4328 get: function(navId) {
4329 if (typeof(this.groups[navId]) == 'undefined') {
4331 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4333 return this.groups[navId] ;
4348 * @class Roo.bootstrap.NavItem
4349 * @extends Roo.bootstrap.Component
4350 * Bootstrap Navbar.NavItem class
4351 * @cfg {String} href link to
4352 * @cfg {String} html content of button
4353 * @cfg {String} badge text inside badge
4354 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4355 * @cfg {String} glyphicon name of glyphicon
4356 * @cfg {String} icon name of font awesome icon
4357 * @cfg {Boolean} active Is item active
4358 * @cfg {Boolean} disabled Is item disabled
4360 * @cfg {Boolean} preventDefault (true | false) default false
4361 * @cfg {String} tabId the tab that this item activates.
4362 * @cfg {String} tagtype (a|span) render as a href or span?
4363 * @cfg {Boolean} animateRef (true|false) link to element default false
4366 * Create a new Navbar Item
4367 * @param {Object} config The config object
4369 Roo.bootstrap.NavItem = function(config){
4370 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4375 * The raw click event for the entire grid.
4376 * @param {Roo.EventObject} e
4381 * Fires when the active item active state changes
4382 * @param {Roo.bootstrap.NavItem} this
4383 * @param {boolean} state the new state
4389 * Fires when scroll to element
4390 * @param {Roo.bootstrap.NavItem} this
4391 * @param {Object} options
4392 * @param {Roo.EventObject} e
4400 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4408 preventDefault : false,
4415 getAutoCreate : function(){
4424 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4426 if (this.disabled) {
4427 cfg.cls += ' disabled';
4430 if (this.href || this.html || this.glyphicon || this.icon) {
4434 href : this.href || "#",
4435 html: this.html || ''
4440 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4443 if(this.glyphicon) {
4444 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4449 cfg.cn[0].html += " <span class='caret'></span>";
4453 if (this.badge !== '') {
4455 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4463 initEvents: function()
4465 if (typeof (this.menu) != 'undefined') {
4466 this.menu.parentType = this.xtype;
4467 this.menu.triggerEl = this.el;
4468 this.menu = this.addxtype(Roo.apply({}, this.menu));
4471 this.el.select('a',true).on('click', this.onClick, this);
4473 if(this.tagtype == 'span'){
4474 this.el.select('span',true).on('click', this.onClick, this);
4477 // at this point parent should be available..
4478 this.parent().register(this);
4481 onClick : function(e)
4483 if (e.getTarget('.dropdown-menu-item')) {
4484 // did you click on a menu itemm.... - then don't trigger onclick..
4489 this.preventDefault ||
4492 Roo.log("NavItem - prevent Default?");
4496 if (this.disabled) {
4500 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4501 if (tg && tg.transition) {
4502 Roo.log("waiting for the transitionend");
4508 //Roo.log("fire event clicked");
4509 if(this.fireEvent('click', this, e) === false){
4513 if(this.tagtype == 'span'){
4517 //Roo.log(this.href);
4518 var ael = this.el.select('a',true).first();
4521 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4522 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4523 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4524 return; // ignore... - it's a 'hash' to another page.
4526 Roo.log("NavItem - prevent Default?");
4528 this.scrollToElement(e);
4532 var p = this.parent();
4534 if (['tabs','pills'].indexOf(p.type)!==-1) {
4535 if (typeof(p.setActiveItem) !== 'undefined') {
4536 p.setActiveItem(this);
4540 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4541 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4542 // remove the collapsed menu expand...
4543 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4547 isActive: function () {
4550 setActive : function(state, fire, is_was_active)
4552 if (this.active && !state && this.navId) {
4553 this.was_active = true;
4554 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4556 nv.clearWasActive(this);
4560 this.active = state;
4563 this.el.removeClass('active');
4564 } else if (!this.el.hasClass('active')) {
4565 this.el.addClass('active');
4568 this.fireEvent('changed', this, state);
4571 // show a panel if it's registered and related..
4573 if (!this.navId || !this.tabId || !state || is_was_active) {
4577 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4581 var pan = tg.getPanelByName(this.tabId);
4585 // if we can not flip to new panel - go back to old nav highlight..
4586 if (false == tg.showPanel(pan)) {
4587 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4589 var onav = nv.getWasActive();
4591 onav.setActive(true, false, true);
4600 // this should not be here...
4601 setDisabled : function(state)
4603 this.disabled = state;
4605 this.el.removeClass('disabled');
4606 } else if (!this.el.hasClass('disabled')) {
4607 this.el.addClass('disabled');
4613 * Fetch the element to display the tooltip on.
4614 * @return {Roo.Element} defaults to this.el
4616 tooltipEl : function()
4618 return this.el.select('' + this.tagtype + '', true).first();
4621 scrollToElement : function(e)
4623 var c = document.body;
4626 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4628 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4629 c = document.documentElement;
4632 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4638 var o = target.calcOffsetsTo(c);
4645 this.fireEvent('scrollto', this, options, e);
4647 Roo.get(c).scrollTo('top', options.value, true);
4660 * <span> icon </span>
4661 * <span> text </span>
4662 * <span>badge </span>
4666 * @class Roo.bootstrap.NavSidebarItem
4667 * @extends Roo.bootstrap.NavItem
4668 * Bootstrap Navbar.NavSidebarItem class
4669 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4670 * {Boolean} open is the menu open
4671 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4672 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4673 * {String} buttonSize (sm|md|lg)the extra classes for the button
4674 * {Boolean} showArrow show arrow next to the text (default true)
4676 * Create a new Navbar Button
4677 * @param {Object} config The config object
4679 Roo.bootstrap.NavSidebarItem = function(config){
4680 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4685 * The raw click event for the entire grid.
4686 * @param {Roo.EventObject} e
4691 * Fires when the active item active state changes
4692 * @param {Roo.bootstrap.NavSidebarItem} this
4693 * @param {boolean} state the new state
4701 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4703 badgeWeight : 'default',
4709 buttonWeight : 'default',
4715 getAutoCreate : function(){
4720 href : this.href || '#',
4726 if(this.buttonView){
4729 href : this.href || '#',
4730 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4743 cfg.cls += ' active';
4746 if (this.disabled) {
4747 cfg.cls += ' disabled';
4750 cfg.cls += ' open x-open';
4753 if (this.glyphicon || this.icon) {
4754 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4755 a.cn.push({ tag : 'i', cls : c }) ;
4758 if(!this.buttonView){
4761 html : this.html || ''
4768 if (this.badge !== '') {
4769 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4775 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4778 a.cls += ' dropdown-toggle treeview' ;
4784 initEvents : function()
4786 if (typeof (this.menu) != 'undefined') {
4787 this.menu.parentType = this.xtype;
4788 this.menu.triggerEl = this.el;
4789 this.menu = this.addxtype(Roo.apply({}, this.menu));
4792 this.el.on('click', this.onClick, this);
4794 if(this.badge !== ''){
4795 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4800 onClick : function(e)
4807 if(this.preventDefault){
4811 this.fireEvent('click', this);
4814 disable : function()
4816 this.setDisabled(true);
4821 this.setDisabled(false);
4824 setDisabled : function(state)
4826 if(this.disabled == state){
4830 this.disabled = state;
4833 this.el.addClass('disabled');
4837 this.el.removeClass('disabled');
4842 setActive : function(state)
4844 if(this.active == state){
4848 this.active = state;
4851 this.el.addClass('active');
4855 this.el.removeClass('active');
4860 isActive: function ()
4865 setBadge : function(str)
4871 this.badgeEl.dom.innerHTML = str;
4888 * @class Roo.bootstrap.Row
4889 * @extends Roo.bootstrap.Component
4890 * Bootstrap Row class (contains columns...)
4894 * @param {Object} config The config object
4897 Roo.bootstrap.Row = function(config){
4898 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4901 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4903 getAutoCreate : function(){
4922 * @class Roo.bootstrap.Element
4923 * @extends Roo.bootstrap.Component
4924 * Bootstrap Element class
4925 * @cfg {String} html contents of the element
4926 * @cfg {String} tag tag of the element
4927 * @cfg {String} cls class of the element
4928 * @cfg {Boolean} preventDefault (true|false) default false
4929 * @cfg {Boolean} clickable (true|false) default false
4932 * Create a new Element
4933 * @param {Object} config The config object
4936 Roo.bootstrap.Element = function(config){
4937 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4943 * When a element is chick
4944 * @param {Roo.bootstrap.Element} this
4945 * @param {Roo.EventObject} e
4951 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4956 preventDefault: false,
4959 getAutoCreate : function(){
4963 // cls: this.cls, double assign in parent class Component.js :: onRender
4970 initEvents: function()
4972 Roo.bootstrap.Element.superclass.initEvents.call(this);
4975 this.el.on('click', this.onClick, this);
4980 onClick : function(e)
4982 if(this.preventDefault){
4986 this.fireEvent('click', this, e);
4989 getValue : function()
4991 return this.el.dom.innerHTML;
4994 setValue : function(value)
4996 this.el.dom.innerHTML = value;
5011 * @class Roo.bootstrap.Pagination
5012 * @extends Roo.bootstrap.Component
5013 * Bootstrap Pagination class
5014 * @cfg {String} size xs | sm | md | lg
5015 * @cfg {Boolean} inverse false | true
5018 * Create a new Pagination
5019 * @param {Object} config The config object
5022 Roo.bootstrap.Pagination = function(config){
5023 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5026 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5032 getAutoCreate : function(){
5038 cfg.cls += ' inverse';
5044 cfg.cls += " " + this.cls;
5062 * @class Roo.bootstrap.PaginationItem
5063 * @extends Roo.bootstrap.Component
5064 * Bootstrap PaginationItem class
5065 * @cfg {String} html text
5066 * @cfg {String} href the link
5067 * @cfg {Boolean} preventDefault (true | false) default true
5068 * @cfg {Boolean} active (true | false) default false
5069 * @cfg {Boolean} disabled default false
5073 * Create a new PaginationItem
5074 * @param {Object} config The config object
5078 Roo.bootstrap.PaginationItem = function(config){
5079 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5084 * The raw click event for the entire grid.
5085 * @param {Roo.EventObject} e
5091 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5095 preventDefault: true,
5100 getAutoCreate : function(){
5106 href : this.href ? this.href : '#',
5107 html : this.html ? this.html : ''
5117 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5121 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5127 initEvents: function() {
5129 this.el.on('click', this.onClick, this);
5132 onClick : function(e)
5134 Roo.log('PaginationItem on click ');
5135 if(this.preventDefault){
5143 this.fireEvent('click', this, e);
5159 * @class Roo.bootstrap.Slider
5160 * @extends Roo.bootstrap.Component
5161 * Bootstrap Slider class
5164 * Create a new Slider
5165 * @param {Object} config The config object
5168 Roo.bootstrap.Slider = function(config){
5169 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5172 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5174 getAutoCreate : function(){
5178 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5182 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5194 * Ext JS Library 1.1.1
5195 * Copyright(c) 2006-2007, Ext JS, LLC.
5197 * Originally Released Under LGPL - original licence link has changed is not relivant.
5200 * <script type="text/javascript">
5205 * @class Roo.grid.ColumnModel
5206 * @extends Roo.util.Observable
5207 * This is the default implementation of a ColumnModel used by the Grid. It defines
5208 * the columns in the grid.
5211 var colModel = new Roo.grid.ColumnModel([
5212 {header: "Ticker", width: 60, sortable: true, locked: true},
5213 {header: "Company Name", width: 150, sortable: true},
5214 {header: "Market Cap.", width: 100, sortable: true},
5215 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5216 {header: "Employees", width: 100, sortable: true, resizable: false}
5221 * The config options listed for this class are options which may appear in each
5222 * individual column definition.
5223 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5225 * @param {Object} config An Array of column config objects. See this class's
5226 * config objects for details.
5228 Roo.grid.ColumnModel = function(config){
5230 * The config passed into the constructor
5232 this.config = config;
5235 // if no id, create one
5236 // if the column does not have a dataIndex mapping,
5237 // map it to the order it is in the config
5238 for(var i = 0, len = config.length; i < len; i++){
5240 if(typeof c.dataIndex == "undefined"){
5243 if(typeof c.renderer == "string"){
5244 c.renderer = Roo.util.Format[c.renderer];
5246 if(typeof c.id == "undefined"){
5249 if(c.editor && c.editor.xtype){
5250 c.editor = Roo.factory(c.editor, Roo.grid);
5252 if(c.editor && c.editor.isFormField){
5253 c.editor = new Roo.grid.GridEditor(c.editor);
5255 this.lookup[c.id] = c;
5259 * The width of columns which have no width specified (defaults to 100)
5262 this.defaultWidth = 100;
5265 * Default sortable of columns which have no sortable specified (defaults to false)
5268 this.defaultSortable = false;
5272 * @event widthchange
5273 * Fires when the width of a column changes.
5274 * @param {ColumnModel} this
5275 * @param {Number} columnIndex The column index
5276 * @param {Number} newWidth The new width
5278 "widthchange": true,
5280 * @event headerchange
5281 * Fires when the text of a header changes.
5282 * @param {ColumnModel} this
5283 * @param {Number} columnIndex The column index
5284 * @param {Number} newText The new header text
5286 "headerchange": true,
5288 * @event hiddenchange
5289 * Fires when a column is hidden or "unhidden".
5290 * @param {ColumnModel} this
5291 * @param {Number} columnIndex The column index
5292 * @param {Boolean} hidden true if hidden, false otherwise
5294 "hiddenchange": true,
5296 * @event columnmoved
5297 * Fires when a column is moved.
5298 * @param {ColumnModel} this
5299 * @param {Number} oldIndex
5300 * @param {Number} newIndex
5302 "columnmoved" : true,
5304 * @event columlockchange
5305 * Fires when a column's locked state is changed
5306 * @param {ColumnModel} this
5307 * @param {Number} colIndex
5308 * @param {Boolean} locked true if locked
5310 "columnlockchange" : true
5312 Roo.grid.ColumnModel.superclass.constructor.call(this);
5314 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5316 * @cfg {String} header The header text to display in the Grid view.
5319 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5320 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5321 * specified, the column's index is used as an index into the Record's data Array.
5324 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5325 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5328 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5329 * Defaults to the value of the {@link #defaultSortable} property.
5330 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5333 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5336 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5339 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5342 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5345 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5346 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5347 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5348 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5351 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5354 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5357 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5360 * @cfg {String} cursor (Optional)
5363 * @cfg {String} tooltip (Optional)
5366 * @cfg {Number} xs (Optional)
5369 * @cfg {Number} sm (Optional)
5372 * @cfg {Number} md (Optional)
5375 * @cfg {Number} lg (Optional)
5378 * Returns the id of the column at the specified index.
5379 * @param {Number} index The column index
5380 * @return {String} the id
5382 getColumnId : function(index){
5383 return this.config[index].id;
5387 * Returns the column for a specified id.
5388 * @param {String} id The column id
5389 * @return {Object} the column
5391 getColumnById : function(id){
5392 return this.lookup[id];
5397 * Returns the column for a specified dataIndex.
5398 * @param {String} dataIndex The column dataIndex
5399 * @return {Object|Boolean} the column or false if not found
5401 getColumnByDataIndex: function(dataIndex){
5402 var index = this.findColumnIndex(dataIndex);
5403 return index > -1 ? this.config[index] : false;
5407 * Returns the index for a specified column id.
5408 * @param {String} id The column id
5409 * @return {Number} the index, or -1 if not found
5411 getIndexById : function(id){
5412 for(var i = 0, len = this.config.length; i < len; i++){
5413 if(this.config[i].id == id){
5421 * Returns the index for a specified column dataIndex.
5422 * @param {String} dataIndex The column dataIndex
5423 * @return {Number} the index, or -1 if not found
5426 findColumnIndex : function(dataIndex){
5427 for(var i = 0, len = this.config.length; i < len; i++){
5428 if(this.config[i].dataIndex == dataIndex){
5436 moveColumn : function(oldIndex, newIndex){
5437 var c = this.config[oldIndex];
5438 this.config.splice(oldIndex, 1);
5439 this.config.splice(newIndex, 0, c);
5440 this.dataMap = null;
5441 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5444 isLocked : function(colIndex){
5445 return this.config[colIndex].locked === true;
5448 setLocked : function(colIndex, value, suppressEvent){
5449 if(this.isLocked(colIndex) == value){
5452 this.config[colIndex].locked = value;
5454 this.fireEvent("columnlockchange", this, colIndex, value);
5458 getTotalLockedWidth : function(){
5460 for(var i = 0; i < this.config.length; i++){
5461 if(this.isLocked(i) && !this.isHidden(i)){
5462 this.totalWidth += this.getColumnWidth(i);
5468 getLockedCount : function(){
5469 for(var i = 0, len = this.config.length; i < len; i++){
5470 if(!this.isLocked(i)){
5475 return this.config.length;
5479 * Returns the number of columns.
5482 getColumnCount : function(visibleOnly){
5483 if(visibleOnly === true){
5485 for(var i = 0, len = this.config.length; i < len; i++){
5486 if(!this.isHidden(i)){
5492 return this.config.length;
5496 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5497 * @param {Function} fn
5498 * @param {Object} scope (optional)
5499 * @return {Array} result
5501 getColumnsBy : function(fn, scope){
5503 for(var i = 0, len = this.config.length; i < len; i++){
5504 var c = this.config[i];
5505 if(fn.call(scope||this, c, i) === true){
5513 * Returns true if the specified column is sortable.
5514 * @param {Number} col The column index
5517 isSortable : function(col){
5518 if(typeof this.config[col].sortable == "undefined"){
5519 return this.defaultSortable;
5521 return this.config[col].sortable;
5525 * Returns the rendering (formatting) function defined for the column.
5526 * @param {Number} col The column index.
5527 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5529 getRenderer : function(col){
5530 if(!this.config[col].renderer){
5531 return Roo.grid.ColumnModel.defaultRenderer;
5533 return this.config[col].renderer;
5537 * Sets the rendering (formatting) function for a column.
5538 * @param {Number} col The column index
5539 * @param {Function} fn The function to use to process the cell's raw data
5540 * to return HTML markup for the grid view. The render function is called with
5541 * the following parameters:<ul>
5542 * <li>Data value.</li>
5543 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5544 * <li>css A CSS style string to apply to the table cell.</li>
5545 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5546 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5547 * <li>Row index</li>
5548 * <li>Column index</li>
5549 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5551 setRenderer : function(col, fn){
5552 this.config[col].renderer = fn;
5556 * Returns the width for the specified column.
5557 * @param {Number} col The column index
5560 getColumnWidth : function(col){
5561 return this.config[col].width * 1 || this.defaultWidth;
5565 * Sets the width for a column.
5566 * @param {Number} col The column index
5567 * @param {Number} width The new width
5569 setColumnWidth : function(col, width, suppressEvent){
5570 this.config[col].width = width;
5571 this.totalWidth = null;
5573 this.fireEvent("widthchange", this, col, width);
5578 * Returns the total width of all columns.
5579 * @param {Boolean} includeHidden True to include hidden column widths
5582 getTotalWidth : function(includeHidden){
5583 if(!this.totalWidth){
5584 this.totalWidth = 0;
5585 for(var i = 0, len = this.config.length; i < len; i++){
5586 if(includeHidden || !this.isHidden(i)){
5587 this.totalWidth += this.getColumnWidth(i);
5591 return this.totalWidth;
5595 * Returns the header for the specified column.
5596 * @param {Number} col The column index
5599 getColumnHeader : function(col){
5600 return this.config[col].header;
5604 * Sets the header for a column.
5605 * @param {Number} col The column index
5606 * @param {String} header The new header
5608 setColumnHeader : function(col, header){
5609 this.config[col].header = header;
5610 this.fireEvent("headerchange", this, col, header);
5614 * Returns the tooltip for the specified column.
5615 * @param {Number} col The column index
5618 getColumnTooltip : function(col){
5619 return this.config[col].tooltip;
5622 * Sets the tooltip for a column.
5623 * @param {Number} col The column index
5624 * @param {String} tooltip The new tooltip
5626 setColumnTooltip : function(col, tooltip){
5627 this.config[col].tooltip = tooltip;
5631 * Returns the dataIndex for the specified column.
5632 * @param {Number} col The column index
5635 getDataIndex : function(col){
5636 return this.config[col].dataIndex;
5640 * Sets the dataIndex for a column.
5641 * @param {Number} col The column index
5642 * @param {Number} dataIndex The new dataIndex
5644 setDataIndex : function(col, dataIndex){
5645 this.config[col].dataIndex = dataIndex;
5651 * Returns true if the cell is editable.
5652 * @param {Number} colIndex The column index
5653 * @param {Number} rowIndex The row index - this is nto actually used..?
5656 isCellEditable : function(colIndex, rowIndex){
5657 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5661 * Returns the editor defined for the cell/column.
5662 * return false or null to disable editing.
5663 * @param {Number} colIndex The column index
5664 * @param {Number} rowIndex The row index
5667 getCellEditor : function(colIndex, rowIndex){
5668 return this.config[colIndex].editor;
5672 * Sets if a column is editable.
5673 * @param {Number} col The column index
5674 * @param {Boolean} editable True if the column is editable
5676 setEditable : function(col, editable){
5677 this.config[col].editable = editable;
5682 * Returns true if the column is hidden.
5683 * @param {Number} colIndex The column index
5686 isHidden : function(colIndex){
5687 return this.config[colIndex].hidden;
5692 * Returns true if the column width cannot be changed
5694 isFixed : function(colIndex){
5695 return this.config[colIndex].fixed;
5699 * Returns true if the column can be resized
5702 isResizable : function(colIndex){
5703 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5706 * Sets if a column is hidden.
5707 * @param {Number} colIndex The column index
5708 * @param {Boolean} hidden True if the column is hidden
5710 setHidden : function(colIndex, hidden){
5711 this.config[colIndex].hidden = hidden;
5712 this.totalWidth = null;
5713 this.fireEvent("hiddenchange", this, colIndex, hidden);
5717 * Sets the editor for a column.
5718 * @param {Number} col The column index
5719 * @param {Object} editor The editor object
5721 setEditor : function(col, editor){
5722 this.config[col].editor = editor;
5726 Roo.grid.ColumnModel.defaultRenderer = function(value)
5728 if(typeof value == "object") {
5731 if(typeof value == "string" && value.length < 1){
5735 return String.format("{0}", value);
5738 // Alias for backwards compatibility
5739 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5742 * Ext JS Library 1.1.1
5743 * Copyright(c) 2006-2007, Ext JS, LLC.
5745 * Originally Released Under LGPL - original licence link has changed is not relivant.
5748 * <script type="text/javascript">
5752 * @class Roo.LoadMask
5753 * A simple utility class for generically masking elements while loading data. If the element being masked has
5754 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5755 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5756 * element's UpdateManager load indicator and will be destroyed after the initial load.
5758 * Create a new LoadMask
5759 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5760 * @param {Object} config The config object
5762 Roo.LoadMask = function(el, config){
5763 this.el = Roo.get(el);
5764 Roo.apply(this, config);
5766 this.store.on('beforeload', this.onBeforeLoad, this);
5767 this.store.on('load', this.onLoad, this);
5768 this.store.on('loadexception', this.onLoadException, this);
5769 this.removeMask = false;
5771 var um = this.el.getUpdateManager();
5772 um.showLoadIndicator = false; // disable the default indicator
5773 um.on('beforeupdate', this.onBeforeLoad, this);
5774 um.on('update', this.onLoad, this);
5775 um.on('failure', this.onLoad, this);
5776 this.removeMask = true;
5780 Roo.LoadMask.prototype = {
5782 * @cfg {Boolean} removeMask
5783 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5784 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5788 * The text to display in a centered loading message box (defaults to 'Loading...')
5792 * @cfg {String} msgCls
5793 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5795 msgCls : 'x-mask-loading',
5798 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5804 * Disables the mask to prevent it from being displayed
5806 disable : function(){
5807 this.disabled = true;
5811 * Enables the mask so that it can be displayed
5813 enable : function(){
5814 this.disabled = false;
5817 onLoadException : function()
5821 if (typeof(arguments[3]) != 'undefined') {
5822 Roo.MessageBox.alert("Error loading",arguments[3]);
5826 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5827 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5834 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5839 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5843 onBeforeLoad : function(){
5845 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5850 destroy : function(){
5852 this.store.un('beforeload', this.onBeforeLoad, this);
5853 this.store.un('load', this.onLoad, this);
5854 this.store.un('loadexception', this.onLoadException, this);
5856 var um = this.el.getUpdateManager();
5857 um.un('beforeupdate', this.onBeforeLoad, this);
5858 um.un('update', this.onLoad, this);
5859 um.un('failure', this.onLoad, this);
5870 * @class Roo.bootstrap.Table
5871 * @extends Roo.bootstrap.Component
5872 * Bootstrap Table class
5873 * @cfg {String} cls table class
5874 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5875 * @cfg {String} bgcolor Specifies the background color for a table
5876 * @cfg {Number} border Specifies whether the table cells should have borders or not
5877 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5878 * @cfg {Number} cellspacing Specifies the space between cells
5879 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5880 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5881 * @cfg {String} sortable Specifies that the table should be sortable
5882 * @cfg {String} summary Specifies a summary of the content of a table
5883 * @cfg {Number} width Specifies the width of a table
5884 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5886 * @cfg {boolean} striped Should the rows be alternative striped
5887 * @cfg {boolean} bordered Add borders to the table
5888 * @cfg {boolean} hover Add hover highlighting
5889 * @cfg {boolean} condensed Format condensed
5890 * @cfg {boolean} responsive Format condensed
5891 * @cfg {Boolean} loadMask (true|false) default false
5892 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5893 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5894 * @cfg {Boolean} rowSelection (true|false) default false
5895 * @cfg {Boolean} cellSelection (true|false) default false
5896 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5897 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5898 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5899 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5903 * Create a new Table
5904 * @param {Object} config The config object
5907 Roo.bootstrap.Table = function(config){
5908 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5913 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5914 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5915 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5916 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5918 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5920 this.sm.grid = this;
5921 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5922 this.sm = this.selModel;
5923 this.sm.xmodule = this.xmodule || false;
5926 if (this.cm && typeof(this.cm.config) == 'undefined') {
5927 this.colModel = new Roo.grid.ColumnModel(this.cm);
5928 this.cm = this.colModel;
5929 this.cm.xmodule = this.xmodule || false;
5932 this.store= Roo.factory(this.store, Roo.data);
5933 this.ds = this.store;
5934 this.ds.xmodule = this.xmodule || false;
5937 if (this.footer && this.store) {
5938 this.footer.dataSource = this.ds;
5939 this.footer = Roo.factory(this.footer);
5946 * Fires when a cell is clicked
5947 * @param {Roo.bootstrap.Table} this
5948 * @param {Roo.Element} el
5949 * @param {Number} rowIndex
5950 * @param {Number} columnIndex
5951 * @param {Roo.EventObject} e
5955 * @event celldblclick
5956 * Fires when a cell is double clicked
5957 * @param {Roo.bootstrap.Table} this
5958 * @param {Roo.Element} el
5959 * @param {Number} rowIndex
5960 * @param {Number} columnIndex
5961 * @param {Roo.EventObject} e
5963 "celldblclick" : true,
5966 * Fires when a row is clicked
5967 * @param {Roo.bootstrap.Table} this
5968 * @param {Roo.Element} el
5969 * @param {Number} rowIndex
5970 * @param {Roo.EventObject} e
5974 * @event rowdblclick
5975 * Fires when a row is double clicked
5976 * @param {Roo.bootstrap.Table} this
5977 * @param {Roo.Element} el
5978 * @param {Number} rowIndex
5979 * @param {Roo.EventObject} e
5981 "rowdblclick" : true,
5984 * Fires when a mouseover occur
5985 * @param {Roo.bootstrap.Table} this
5986 * @param {Roo.Element} el
5987 * @param {Number} rowIndex
5988 * @param {Number} columnIndex
5989 * @param {Roo.EventObject} e
5994 * Fires when a mouseout occur
5995 * @param {Roo.bootstrap.Table} this
5996 * @param {Roo.Element} el
5997 * @param {Number} rowIndex
5998 * @param {Number} columnIndex
5999 * @param {Roo.EventObject} e
6004 * Fires when a row is rendered, so you can change add a style to it.
6005 * @param {Roo.bootstrap.Table} this
6006 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6010 * @event rowsrendered
6011 * Fires when all the rows have been rendered
6012 * @param {Roo.bootstrap.Table} this
6014 'rowsrendered' : true,
6016 * @event contextmenu
6017 * The raw contextmenu event for the entire grid.
6018 * @param {Roo.EventObject} e
6020 "contextmenu" : true,
6022 * @event rowcontextmenu
6023 * Fires when a row is right clicked
6024 * @param {Roo.bootstrap.Table} this
6025 * @param {Number} rowIndex
6026 * @param {Roo.EventObject} e
6028 "rowcontextmenu" : true,
6030 * @event cellcontextmenu
6031 * Fires when a cell is right clicked
6032 * @param {Roo.bootstrap.Table} this
6033 * @param {Number} rowIndex
6034 * @param {Number} cellIndex
6035 * @param {Roo.EventObject} e
6037 "cellcontextmenu" : true,
6039 * @event headercontextmenu
6040 * Fires when a header is right clicked
6041 * @param {Roo.bootstrap.Table} this
6042 * @param {Number} columnIndex
6043 * @param {Roo.EventObject} e
6045 "headercontextmenu" : true
6049 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6075 rowSelection : false,
6076 cellSelection : false,
6079 // Roo.Element - the tbody
6081 // Roo.Element - thead element
6084 container: false, // used by gridpanel...
6090 auto_hide_footer : false,
6092 getAutoCreate : function()
6094 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6101 if (this.scrollBody) {
6102 cfg.cls += ' table-body-fixed';
6105 cfg.cls += ' table-striped';
6109 cfg.cls += ' table-hover';
6111 if (this.bordered) {
6112 cfg.cls += ' table-bordered';
6114 if (this.condensed) {
6115 cfg.cls += ' table-condensed';
6117 if (this.responsive) {
6118 cfg.cls += ' table-responsive';
6122 cfg.cls+= ' ' +this.cls;
6125 // this lot should be simplifed...
6138 ].forEach(function(k) {
6146 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6149 if(this.store || this.cm){
6150 if(this.headerShow){
6151 cfg.cn.push(this.renderHeader());
6154 cfg.cn.push(this.renderBody());
6156 if(this.footerShow){
6157 cfg.cn.push(this.renderFooter());
6159 // where does this come from?
6160 //cfg.cls+= ' TableGrid';
6163 return { cn : [ cfg ] };
6166 initEvents : function()
6168 if(!this.store || !this.cm){
6171 if (this.selModel) {
6172 this.selModel.initEvents();
6176 //Roo.log('initEvents with ds!!!!');
6178 this.mainBody = this.el.select('tbody', true).first();
6179 this.mainHead = this.el.select('thead', true).first();
6180 this.mainFoot = this.el.select('tfoot', true).first();
6186 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6187 e.on('click', _this.sort, _this);
6190 this.mainBody.on("click", this.onClick, this);
6191 this.mainBody.on("dblclick", this.onDblClick, this);
6193 // why is this done????? = it breaks dialogs??
6194 //this.parent().el.setStyle('position', 'relative');
6198 this.footer.parentId = this.id;
6199 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6202 this.el.select('tfoot tr td').first().addClass('hide');
6207 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6210 this.store.on('load', this.onLoad, this);
6211 this.store.on('beforeload', this.onBeforeLoad, this);
6212 this.store.on('update', this.onUpdate, this);
6213 this.store.on('add', this.onAdd, this);
6214 this.store.on("clear", this.clear, this);
6216 this.el.on("contextmenu", this.onContextMenu, this);
6218 this.mainBody.on('scroll', this.onBodyScroll, this);
6220 this.cm.on("headerchange", this.onHeaderChange, this);
6222 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6226 onContextMenu : function(e, t)
6228 this.processEvent("contextmenu", e);
6231 processEvent : function(name, e)
6233 if (name != 'touchstart' ) {
6234 this.fireEvent(name, e);
6237 var t = e.getTarget();
6239 var cell = Roo.get(t);
6245 if(cell.findParent('tfoot', false, true)){
6249 if(cell.findParent('thead', false, true)){
6251 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6252 cell = Roo.get(t).findParent('th', false, true);
6254 Roo.log("failed to find th in thead?");
6255 Roo.log(e.getTarget());
6260 var cellIndex = cell.dom.cellIndex;
6262 var ename = name == 'touchstart' ? 'click' : name;
6263 this.fireEvent("header" + ename, this, cellIndex, e);
6268 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6269 cell = Roo.get(t).findParent('td', false, true);
6271 Roo.log("failed to find th in tbody?");
6272 Roo.log(e.getTarget());
6277 var row = cell.findParent('tr', false, true);
6278 var cellIndex = cell.dom.cellIndex;
6279 var rowIndex = row.dom.rowIndex - 1;
6283 this.fireEvent("row" + name, this, rowIndex, e);
6287 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6293 onMouseover : function(e, el)
6295 var cell = Roo.get(el);
6301 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6302 cell = cell.findParent('td', false, true);
6305 var row = cell.findParent('tr', false, true);
6306 var cellIndex = cell.dom.cellIndex;
6307 var rowIndex = row.dom.rowIndex - 1; // start from 0
6309 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6313 onMouseout : function(e, el)
6315 var cell = Roo.get(el);
6321 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6322 cell = cell.findParent('td', false, true);
6325 var row = cell.findParent('tr', false, true);
6326 var cellIndex = cell.dom.cellIndex;
6327 var rowIndex = row.dom.rowIndex - 1; // start from 0
6329 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6333 onClick : function(e, el)
6335 var cell = Roo.get(el);
6337 if(!cell || (!this.cellSelection && !this.rowSelection)){
6341 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6342 cell = cell.findParent('td', false, true);
6345 if(!cell || typeof(cell) == 'undefined'){
6349 var row = cell.findParent('tr', false, true);
6351 if(!row || typeof(row) == 'undefined'){
6355 var cellIndex = cell.dom.cellIndex;
6356 var rowIndex = this.getRowIndex(row);
6358 // why??? - should these not be based on SelectionModel?
6359 if(this.cellSelection){
6360 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6363 if(this.rowSelection){
6364 this.fireEvent('rowclick', this, row, rowIndex, e);
6370 onDblClick : function(e,el)
6372 var cell = Roo.get(el);
6374 if(!cell || (!this.cellSelection && !this.rowSelection)){
6378 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6379 cell = cell.findParent('td', false, true);
6382 if(!cell || typeof(cell) == 'undefined'){
6386 var row = cell.findParent('tr', false, true);
6388 if(!row || typeof(row) == 'undefined'){
6392 var cellIndex = cell.dom.cellIndex;
6393 var rowIndex = this.getRowIndex(row);
6395 if(this.cellSelection){
6396 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6399 if(this.rowSelection){
6400 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6404 sort : function(e,el)
6406 var col = Roo.get(el);
6408 if(!col.hasClass('sortable')){
6412 var sort = col.attr('sort');
6415 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6419 this.store.sortInfo = {field : sort, direction : dir};
6422 Roo.log("calling footer first");
6423 this.footer.onClick('first');
6426 this.store.load({ params : { start : 0 } });
6430 renderHeader : function()
6438 this.totalWidth = 0;
6440 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6442 var config = cm.config[i];
6446 cls : 'x-hcol-' + i,
6448 html: cm.getColumnHeader(i)
6453 if(typeof(config.sortable) != 'undefined' && config.sortable){
6455 c.html = '<i class="glyphicon"></i>' + c.html;
6458 if(typeof(config.lgHeader) != 'undefined'){
6459 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6462 if(typeof(config.mdHeader) != 'undefined'){
6463 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6466 if(typeof(config.smHeader) != 'undefined'){
6467 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6470 if(typeof(config.xsHeader) != 'undefined'){
6471 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6478 if(typeof(config.tooltip) != 'undefined'){
6479 c.tooltip = config.tooltip;
6482 if(typeof(config.colspan) != 'undefined'){
6483 c.colspan = config.colspan;
6486 if(typeof(config.hidden) != 'undefined' && config.hidden){
6487 c.style += ' display:none;';
6490 if(typeof(config.dataIndex) != 'undefined'){
6491 c.sort = config.dataIndex;
6496 if(typeof(config.align) != 'undefined' && config.align.length){
6497 c.style += ' text-align:' + config.align + ';';
6500 if(typeof(config.width) != 'undefined'){
6501 c.style += ' width:' + config.width + 'px;';
6502 this.totalWidth += config.width;
6504 this.totalWidth += 100; // assume minimum of 100 per column?
6507 if(typeof(config.cls) != 'undefined'){
6508 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6511 ['xs','sm','md','lg'].map(function(size){
6513 if(typeof(config[size]) == 'undefined'){
6517 if (!config[size]) { // 0 = hidden
6518 c.cls += ' hidden-' + size;
6522 c.cls += ' col-' + size + '-' + config[size];
6532 renderBody : function()
6542 colspan : this.cm.getColumnCount()
6552 renderFooter : function()
6562 colspan : this.cm.getColumnCount()
6576 // Roo.log('ds onload');
6581 var ds = this.store;
6583 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6584 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6585 if (_this.store.sortInfo) {
6587 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6588 e.select('i', true).addClass(['glyphicon-arrow-up']);
6591 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6592 e.select('i', true).addClass(['glyphicon-arrow-down']);
6597 var tbody = this.mainBody;
6599 if(ds.getCount() > 0){
6600 ds.data.each(function(d,rowIndex){
6601 var row = this.renderRow(cm, ds, rowIndex);
6603 tbody.createChild(row);
6607 if(row.cellObjects.length){
6608 Roo.each(row.cellObjects, function(r){
6609 _this.renderCellObject(r);
6616 var tfoot = this.el.select('tfoot', true).first();
6618 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6620 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6622 var total = this.ds.getTotalCount();
6624 if(this.footer.pageSize < total){
6625 this.mainFoot.show();
6629 Roo.each(this.el.select('tbody td', true).elements, function(e){
6630 e.on('mouseover', _this.onMouseover, _this);
6633 Roo.each(this.el.select('tbody td', true).elements, function(e){
6634 e.on('mouseout', _this.onMouseout, _this);
6636 this.fireEvent('rowsrendered', this);
6642 onUpdate : function(ds,record)
6644 this.refreshRow(record);
6648 onRemove : function(ds, record, index, isUpdate){
6649 if(isUpdate !== true){
6650 this.fireEvent("beforerowremoved", this, index, record);
6652 var bt = this.mainBody.dom;
6654 var rows = this.el.select('tbody > tr', true).elements;
6656 if(typeof(rows[index]) != 'undefined'){
6657 bt.removeChild(rows[index].dom);
6660 // if(bt.rows[index]){
6661 // bt.removeChild(bt.rows[index]);
6664 if(isUpdate !== true){
6665 //this.stripeRows(index);
6666 //this.syncRowHeights(index, index);
6668 this.fireEvent("rowremoved", this, index, record);
6672 onAdd : function(ds, records, rowIndex)
6674 //Roo.log('on Add called');
6675 // - note this does not handle multiple adding very well..
6676 var bt = this.mainBody.dom;
6677 for (var i =0 ; i < records.length;i++) {
6678 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6679 //Roo.log(records[i]);
6680 //Roo.log(this.store.getAt(rowIndex+i));
6681 this.insertRow(this.store, rowIndex + i, false);
6688 refreshRow : function(record){
6689 var ds = this.store, index;
6690 if(typeof record == 'number'){
6692 record = ds.getAt(index);
6694 index = ds.indexOf(record);
6696 this.insertRow(ds, index, true);
6698 this.onRemove(ds, record, index+1, true);
6700 //this.syncRowHeights(index, index);
6702 this.fireEvent("rowupdated", this, index, record);
6705 insertRow : function(dm, rowIndex, isUpdate){
6708 this.fireEvent("beforerowsinserted", this, rowIndex);
6710 //var s = this.getScrollState();
6711 var row = this.renderRow(this.cm, this.store, rowIndex);
6712 // insert before rowIndex..
6713 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6717 if(row.cellObjects.length){
6718 Roo.each(row.cellObjects, function(r){
6719 _this.renderCellObject(r);
6724 this.fireEvent("rowsinserted", this, rowIndex);
6725 //this.syncRowHeights(firstRow, lastRow);
6726 //this.stripeRows(firstRow);
6733 getRowDom : function(rowIndex)
6735 var rows = this.el.select('tbody > tr', true).elements;
6737 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6740 // returns the object tree for a tr..
6743 renderRow : function(cm, ds, rowIndex)
6745 var d = ds.getAt(rowIndex);
6749 cls : 'x-row-' + rowIndex,
6753 var cellObjects = [];
6755 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6756 var config = cm.config[i];
6758 var renderer = cm.getRenderer(i);
6762 if(typeof(renderer) !== 'undefined'){
6763 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6765 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6766 // and are rendered into the cells after the row is rendered - using the id for the element.
6768 if(typeof(value) === 'object'){
6778 rowIndex : rowIndex,
6783 this.fireEvent('rowclass', this, rowcfg);
6787 cls : rowcfg.rowClass + ' x-col-' + i,
6789 html: (typeof(value) === 'object') ? '' : value
6796 if(typeof(config.colspan) != 'undefined'){
6797 td.colspan = config.colspan;
6800 if(typeof(config.hidden) != 'undefined' && config.hidden){
6801 td.style += ' display:none;';
6804 if(typeof(config.align) != 'undefined' && config.align.length){
6805 td.style += ' text-align:' + config.align + ';';
6807 if(typeof(config.valign) != 'undefined' && config.valign.length){
6808 td.style += ' vertical-align:' + config.valign + ';';
6811 if(typeof(config.width) != 'undefined'){
6812 td.style += ' width:' + config.width + 'px;';
6815 if(typeof(config.cursor) != 'undefined'){
6816 td.style += ' cursor:' + config.cursor + ';';
6819 if(typeof(config.cls) != 'undefined'){
6820 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6823 ['xs','sm','md','lg'].map(function(size){
6825 if(typeof(config[size]) == 'undefined'){
6829 if (!config[size]) { // 0 = hidden
6830 td.cls += ' hidden-' + size;
6834 td.cls += ' col-' + size + '-' + config[size];
6842 row.cellObjects = cellObjects;
6850 onBeforeLoad : function()
6859 this.el.select('tbody', true).first().dom.innerHTML = '';
6862 * Show or hide a row.
6863 * @param {Number} rowIndex to show or hide
6864 * @param {Boolean} state hide
6866 setRowVisibility : function(rowIndex, state)
6868 var bt = this.mainBody.dom;
6870 var rows = this.el.select('tbody > tr', true).elements;
6872 if(typeof(rows[rowIndex]) == 'undefined'){
6875 rows[rowIndex].dom.style.display = state ? '' : 'none';
6879 getSelectionModel : function(){
6881 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6883 return this.selModel;
6886 * Render the Roo.bootstrap object from renderder
6888 renderCellObject : function(r)
6892 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6894 var t = r.cfg.render(r.container);
6897 Roo.each(r.cfg.cn, function(c){
6899 container: t.getChildContainer(),
6902 _this.renderCellObject(child);
6907 getRowIndex : function(row)
6911 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6922 * Returns the grid's underlying element = used by panel.Grid
6923 * @return {Element} The element
6925 getGridEl : function(){
6929 * Forces a resize - used by panel.Grid
6930 * @return {Element} The element
6932 autoSize : function()
6934 //var ctr = Roo.get(this.container.dom.parentElement);
6935 var ctr = Roo.get(this.el.dom);
6937 var thd = this.getGridEl().select('thead',true).first();
6938 var tbd = this.getGridEl().select('tbody', true).first();
6939 var tfd = this.getGridEl().select('tfoot', true).first();
6941 var cw = ctr.getWidth();
6945 tbd.setSize(ctr.getWidth(),
6946 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6948 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6951 cw = Math.max(cw, this.totalWidth);
6952 this.getGridEl().select('tr',true).setWidth(cw);
6953 // resize 'expandable coloumn?
6955 return; // we doe not have a view in this design..
6958 onBodyScroll: function()
6960 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6962 this.mainHead.setStyle({
6963 'position' : 'relative',
6964 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6970 var scrollHeight = this.mainBody.dom.scrollHeight;
6972 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6974 var height = this.mainBody.getHeight();
6976 if(scrollHeight - height == scrollTop) {
6978 var total = this.ds.getTotalCount();
6980 if(this.footer.cursor + this.footer.pageSize < total){
6982 this.footer.ds.load({
6984 start : this.footer.cursor + this.footer.pageSize,
6985 limit : this.footer.pageSize
6995 onHeaderChange : function()
6997 var header = this.renderHeader();
6998 var table = this.el.select('table', true).first();
7000 this.mainHead.remove();
7001 this.mainHead = table.createChild(header, this.mainBody, false);
7004 onHiddenChange : function(colModel, colIndex, hidden)
7006 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7007 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7009 this.CSS.updateRule(thSelector, "display", "");
7010 this.CSS.updateRule(tdSelector, "display", "");
7013 this.CSS.updateRule(thSelector, "display", "none");
7014 this.CSS.updateRule(tdSelector, "display", "none");
7017 this.onHeaderChange();
7034 * @class Roo.bootstrap.TableCell
7035 * @extends Roo.bootstrap.Component
7036 * Bootstrap TableCell class
7037 * @cfg {String} html cell contain text
7038 * @cfg {String} cls cell class
7039 * @cfg {String} tag cell tag (td|th) default td
7040 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7041 * @cfg {String} align Aligns the content in a cell
7042 * @cfg {String} axis Categorizes cells
7043 * @cfg {String} bgcolor Specifies the background color of a cell
7044 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7045 * @cfg {Number} colspan Specifies the number of columns a cell should span
7046 * @cfg {String} headers Specifies one or more header cells a cell is related to
7047 * @cfg {Number} height Sets the height of a cell
7048 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7049 * @cfg {Number} rowspan Sets the number of rows a cell should span
7050 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7051 * @cfg {String} valign Vertical aligns the content in a cell
7052 * @cfg {Number} width Specifies the width of a cell
7055 * Create a new TableCell
7056 * @param {Object} config The config object
7059 Roo.bootstrap.TableCell = function(config){
7060 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7063 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7083 getAutoCreate : function(){
7084 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7104 cfg.align=this.align
7110 cfg.bgcolor=this.bgcolor
7113 cfg.charoff=this.charoff
7116 cfg.colspan=this.colspan
7119 cfg.headers=this.headers
7122 cfg.height=this.height
7125 cfg.nowrap=this.nowrap
7128 cfg.rowspan=this.rowspan
7131 cfg.scope=this.scope
7134 cfg.valign=this.valign
7137 cfg.width=this.width
7156 * @class Roo.bootstrap.TableRow
7157 * @extends Roo.bootstrap.Component
7158 * Bootstrap TableRow class
7159 * @cfg {String} cls row class
7160 * @cfg {String} align Aligns the content in a table row
7161 * @cfg {String} bgcolor Specifies a background color for a table row
7162 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7163 * @cfg {String} valign Vertical aligns the content in a table row
7166 * Create a new TableRow
7167 * @param {Object} config The config object
7170 Roo.bootstrap.TableRow = function(config){
7171 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7174 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7182 getAutoCreate : function(){
7183 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7193 cfg.align = this.align;
7196 cfg.bgcolor = this.bgcolor;
7199 cfg.charoff = this.charoff;
7202 cfg.valign = this.valign;
7220 * @class Roo.bootstrap.TableBody
7221 * @extends Roo.bootstrap.Component
7222 * Bootstrap TableBody class
7223 * @cfg {String} cls element class
7224 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7225 * @cfg {String} align Aligns the content inside the element
7226 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7227 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7230 * Create a new TableBody
7231 * @param {Object} config The config object
7234 Roo.bootstrap.TableBody = function(config){
7235 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7238 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7246 getAutoCreate : function(){
7247 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7261 cfg.align = this.align;
7264 cfg.charoff = this.charoff;
7267 cfg.valign = this.valign;
7274 // initEvents : function()
7281 // this.store = Roo.factory(this.store, Roo.data);
7282 // this.store.on('load', this.onLoad, this);
7284 // this.store.load();
7288 // onLoad: function ()
7290 // this.fireEvent('load', this);
7300 * Ext JS Library 1.1.1
7301 * Copyright(c) 2006-2007, Ext JS, LLC.
7303 * Originally Released Under LGPL - original licence link has changed is not relivant.
7306 * <script type="text/javascript">
7309 // as we use this in bootstrap.
7310 Roo.namespace('Roo.form');
7312 * @class Roo.form.Action
7313 * Internal Class used to handle form actions
7315 * @param {Roo.form.BasicForm} el The form element or its id
7316 * @param {Object} config Configuration options
7321 // define the action interface
7322 Roo.form.Action = function(form, options){
7324 this.options = options || {};
7327 * Client Validation Failed
7330 Roo.form.Action.CLIENT_INVALID = 'client';
7332 * Server Validation Failed
7335 Roo.form.Action.SERVER_INVALID = 'server';
7337 * Connect to Server Failed
7340 Roo.form.Action.CONNECT_FAILURE = 'connect';
7342 * Reading Data from Server Failed
7345 Roo.form.Action.LOAD_FAILURE = 'load';
7347 Roo.form.Action.prototype = {
7349 failureType : undefined,
7350 response : undefined,
7354 run : function(options){
7359 success : function(response){
7364 handleResponse : function(response){
7368 // default connection failure
7369 failure : function(response){
7371 this.response = response;
7372 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7373 this.form.afterAction(this, false);
7376 processResponse : function(response){
7377 this.response = response;
7378 if(!response.responseText){
7381 this.result = this.handleResponse(response);
7385 // utility functions used internally
7386 getUrl : function(appendParams){
7387 var url = this.options.url || this.form.url || this.form.el.dom.action;
7389 var p = this.getParams();
7391 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7397 getMethod : function(){
7398 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7401 getParams : function(){
7402 var bp = this.form.baseParams;
7403 var p = this.options.params;
7405 if(typeof p == "object"){
7406 p = Roo.urlEncode(Roo.applyIf(p, bp));
7407 }else if(typeof p == 'string' && bp){
7408 p += '&' + Roo.urlEncode(bp);
7411 p = Roo.urlEncode(bp);
7416 createCallback : function(){
7418 success: this.success,
7419 failure: this.failure,
7421 timeout: (this.form.timeout*1000),
7422 upload: this.form.fileUpload ? this.success : undefined
7427 Roo.form.Action.Submit = function(form, options){
7428 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7431 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7434 haveProgress : false,
7435 uploadComplete : false,
7437 // uploadProgress indicator.
7438 uploadProgress : function()
7440 if (!this.form.progressUrl) {
7444 if (!this.haveProgress) {
7445 Roo.MessageBox.progress("Uploading", "Uploading");
7447 if (this.uploadComplete) {
7448 Roo.MessageBox.hide();
7452 this.haveProgress = true;
7454 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7456 var c = new Roo.data.Connection();
7458 url : this.form.progressUrl,
7463 success : function(req){
7464 //console.log(data);
7468 rdata = Roo.decode(req.responseText)
7470 Roo.log("Invalid data from server..");
7474 if (!rdata || !rdata.success) {
7476 Roo.MessageBox.alert(Roo.encode(rdata));
7479 var data = rdata.data;
7481 if (this.uploadComplete) {
7482 Roo.MessageBox.hide();
7487 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7488 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7491 this.uploadProgress.defer(2000,this);
7494 failure: function(data) {
7495 Roo.log('progress url failed ');
7506 // run get Values on the form, so it syncs any secondary forms.
7507 this.form.getValues();
7509 var o = this.options;
7510 var method = this.getMethod();
7511 var isPost = method == 'POST';
7512 if(o.clientValidation === false || this.form.isValid()){
7514 if (this.form.progressUrl) {
7515 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7516 (new Date() * 1) + '' + Math.random());
7521 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7522 form:this.form.el.dom,
7523 url:this.getUrl(!isPost),
7525 params:isPost ? this.getParams() : null,
7526 isUpload: this.form.fileUpload
7529 this.uploadProgress();
7531 }else if (o.clientValidation !== false){ // client validation failed
7532 this.failureType = Roo.form.Action.CLIENT_INVALID;
7533 this.form.afterAction(this, false);
7537 success : function(response)
7539 this.uploadComplete= true;
7540 if (this.haveProgress) {
7541 Roo.MessageBox.hide();
7545 var result = this.processResponse(response);
7546 if(result === true || result.success){
7547 this.form.afterAction(this, true);
7551 this.form.markInvalid(result.errors);
7552 this.failureType = Roo.form.Action.SERVER_INVALID;
7554 this.form.afterAction(this, false);
7556 failure : function(response)
7558 this.uploadComplete= true;
7559 if (this.haveProgress) {
7560 Roo.MessageBox.hide();
7563 this.response = response;
7564 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7565 this.form.afterAction(this, false);
7568 handleResponse : function(response){
7569 if(this.form.errorReader){
7570 var rs = this.form.errorReader.read(response);
7573 for(var i = 0, len = rs.records.length; i < len; i++) {
7574 var r = rs.records[i];
7578 if(errors.length < 1){
7582 success : rs.success,
7588 ret = Roo.decode(response.responseText);
7592 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7602 Roo.form.Action.Load = function(form, options){
7603 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7604 this.reader = this.form.reader;
7607 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7612 Roo.Ajax.request(Roo.apply(
7613 this.createCallback(), {
7614 method:this.getMethod(),
7615 url:this.getUrl(false),
7616 params:this.getParams()
7620 success : function(response){
7622 var result = this.processResponse(response);
7623 if(result === true || !result.success || !result.data){
7624 this.failureType = Roo.form.Action.LOAD_FAILURE;
7625 this.form.afterAction(this, false);
7628 this.form.clearInvalid();
7629 this.form.setValues(result.data);
7630 this.form.afterAction(this, true);
7633 handleResponse : function(response){
7634 if(this.form.reader){
7635 var rs = this.form.reader.read(response);
7636 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7638 success : rs.success,
7642 return Roo.decode(response.responseText);
7646 Roo.form.Action.ACTION_TYPES = {
7647 'load' : Roo.form.Action.Load,
7648 'submit' : Roo.form.Action.Submit
7657 * @class Roo.bootstrap.Form
7658 * @extends Roo.bootstrap.Component
7659 * Bootstrap Form class
7660 * @cfg {String} method GET | POST (default POST)
7661 * @cfg {String} labelAlign top | left (default top)
7662 * @cfg {String} align left | right - for navbars
7663 * @cfg {Boolean} loadMask load mask when submit (default true)
7668 * @param {Object} config The config object
7672 Roo.bootstrap.Form = function(config){
7674 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7676 Roo.bootstrap.Form.popover.apply();
7680 * @event clientvalidation
7681 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7682 * @param {Form} this
7683 * @param {Boolean} valid true if the form has passed client-side validation
7685 clientvalidation: true,
7687 * @event beforeaction
7688 * Fires before any action is performed. Return false to cancel the action.
7689 * @param {Form} this
7690 * @param {Action} action The action to be performed
7694 * @event actionfailed
7695 * Fires when an action fails.
7696 * @param {Form} this
7697 * @param {Action} action The action that failed
7699 actionfailed : true,
7701 * @event actioncomplete
7702 * Fires when an action is completed.
7703 * @param {Form} this
7704 * @param {Action} action The action that completed
7706 actioncomplete : true
7710 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7713 * @cfg {String} method
7714 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7719 * The URL to use for form actions if one isn't supplied in the action options.
7722 * @cfg {Boolean} fileUpload
7723 * Set to true if this form is a file upload.
7727 * @cfg {Object} baseParams
7728 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7732 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7736 * @cfg {Sting} align (left|right) for navbar forms
7741 activeAction : null,
7744 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7745 * element by passing it or its id or mask the form itself by passing in true.
7748 waitMsgTarget : false,
7753 * @cfg {Boolean} errorMask (true|false) default false
7758 * @cfg {Number} maskOffset Default 100
7763 * @cfg {Boolean} maskBody
7767 getAutoCreate : function(){
7771 method : this.method || 'POST',
7772 id : this.id || Roo.id(),
7775 if (this.parent().xtype.match(/^Nav/)) {
7776 cfg.cls = 'navbar-form navbar-' + this.align;
7780 if (this.labelAlign == 'left' ) {
7781 cfg.cls += ' form-horizontal';
7787 initEvents : function()
7789 this.el.on('submit', this.onSubmit, this);
7790 // this was added as random key presses on the form where triggering form submit.
7791 this.el.on('keypress', function(e) {
7792 if (e.getCharCode() != 13) {
7795 // we might need to allow it for textareas.. and some other items.
7796 // check e.getTarget().
7798 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7802 Roo.log("keypress blocked");
7810 onSubmit : function(e){
7815 * Returns true if client-side validation on the form is successful.
7818 isValid : function(){
7819 var items = this.getItems();
7823 items.each(function(f){
7831 if(!target && f.el.isVisible(true)){
7837 if(this.errorMask && !valid){
7838 Roo.bootstrap.Form.popover.mask(this, target);
7845 * Returns true if any fields in this form have changed since their original load.
7848 isDirty : function(){
7850 var items = this.getItems();
7851 items.each(function(f){
7861 * Performs a predefined action (submit or load) or custom actions you define on this form.
7862 * @param {String} actionName The name of the action type
7863 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7864 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7865 * accept other config options):
7867 Property Type Description
7868 ---------------- --------------- ----------------------------------------------------------------------------------
7869 url String The url for the action (defaults to the form's url)
7870 method String The form method to use (defaults to the form's method, or POST if not defined)
7871 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7872 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7873 validate the form on the client (defaults to false)
7875 * @return {BasicForm} this
7877 doAction : function(action, options){
7878 if(typeof action == 'string'){
7879 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7881 if(this.fireEvent('beforeaction', this, action) !== false){
7882 this.beforeAction(action);
7883 action.run.defer(100, action);
7889 beforeAction : function(action){
7890 var o = action.options;
7895 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7897 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7900 // not really supported yet.. ??
7902 //if(this.waitMsgTarget === true){
7903 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7904 //}else if(this.waitMsgTarget){
7905 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7906 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7908 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7914 afterAction : function(action, success){
7915 this.activeAction = null;
7916 var o = action.options;
7921 Roo.get(document.body).unmask();
7927 //if(this.waitMsgTarget === true){
7928 // this.el.unmask();
7929 //}else if(this.waitMsgTarget){
7930 // this.waitMsgTarget.unmask();
7932 // Roo.MessageBox.updateProgress(1);
7933 // Roo.MessageBox.hide();
7940 Roo.callback(o.success, o.scope, [this, action]);
7941 this.fireEvent('actioncomplete', this, action);
7945 // failure condition..
7946 // we have a scenario where updates need confirming.
7947 // eg. if a locking scenario exists..
7948 // we look for { errors : { needs_confirm : true }} in the response.
7950 (typeof(action.result) != 'undefined') &&
7951 (typeof(action.result.errors) != 'undefined') &&
7952 (typeof(action.result.errors.needs_confirm) != 'undefined')
7955 Roo.log("not supported yet");
7958 Roo.MessageBox.confirm(
7959 "Change requires confirmation",
7960 action.result.errorMsg,
7965 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7975 Roo.callback(o.failure, o.scope, [this, action]);
7976 // show an error message if no failed handler is set..
7977 if (!this.hasListener('actionfailed')) {
7978 Roo.log("need to add dialog support");
7980 Roo.MessageBox.alert("Error",
7981 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7982 action.result.errorMsg :
7983 "Saving Failed, please check your entries or try again"
7988 this.fireEvent('actionfailed', this, action);
7993 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7994 * @param {String} id The value to search for
7997 findField : function(id){
7998 var items = this.getItems();
7999 var field = items.get(id);
8001 items.each(function(f){
8002 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8009 return field || null;
8012 * Mark fields in this form invalid in bulk.
8013 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8014 * @return {BasicForm} this
8016 markInvalid : function(errors){
8017 if(errors instanceof Array){
8018 for(var i = 0, len = errors.length; i < len; i++){
8019 var fieldError = errors[i];
8020 var f = this.findField(fieldError.id);
8022 f.markInvalid(fieldError.msg);
8028 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8029 field.markInvalid(errors[id]);
8033 //Roo.each(this.childForms || [], function (f) {
8034 // f.markInvalid(errors);
8041 * Set values for fields in this form in bulk.
8042 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8043 * @return {BasicForm} this
8045 setValues : function(values){
8046 if(values instanceof Array){ // array of objects
8047 for(var i = 0, len = values.length; i < len; i++){
8049 var f = this.findField(v.id);
8051 f.setValue(v.value);
8052 if(this.trackResetOnLoad){
8053 f.originalValue = f.getValue();
8057 }else{ // object hash
8060 if(typeof values[id] != 'function' && (field = this.findField(id))){
8062 if (field.setFromData &&
8064 field.displayField &&
8065 // combos' with local stores can
8066 // be queried via setValue()
8067 // to set their value..
8068 (field.store && !field.store.isLocal)
8072 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8073 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8074 field.setFromData(sd);
8076 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8078 field.setFromData(values);
8081 field.setValue(values[id]);
8085 if(this.trackResetOnLoad){
8086 field.originalValue = field.getValue();
8092 //Roo.each(this.childForms || [], function (f) {
8093 // f.setValues(values);
8100 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8101 * they are returned as an array.
8102 * @param {Boolean} asString
8105 getValues : function(asString){
8106 //if (this.childForms) {
8107 // copy values from the child forms
8108 // Roo.each(this.childForms, function (f) {
8109 // this.setValues(f.getValues());
8115 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8116 if(asString === true){
8119 return Roo.urlDecode(fs);
8123 * Returns the fields in this form as an object with key/value pairs.
8124 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8127 getFieldValues : function(with_hidden)
8129 var items = this.getItems();
8131 items.each(function(f){
8137 var v = f.getValue();
8139 if (f.inputType =='radio') {
8140 if (typeof(ret[f.getName()]) == 'undefined') {
8141 ret[f.getName()] = ''; // empty..
8144 if (!f.el.dom.checked) {
8152 if(f.xtype == 'MoneyField'){
8153 ret[f.currencyName] = f.getCurrency();
8156 // not sure if this supported any more..
8157 if ((typeof(v) == 'object') && f.getRawValue) {
8158 v = f.getRawValue() ; // dates..
8160 // combo boxes where name != hiddenName...
8161 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8162 ret[f.name] = f.getRawValue();
8164 ret[f.getName()] = v;
8171 * Clears all invalid messages in this form.
8172 * @return {BasicForm} this
8174 clearInvalid : function(){
8175 var items = this.getItems();
8177 items.each(function(f){
8186 * @return {BasicForm} this
8189 var items = this.getItems();
8190 items.each(function(f){
8194 Roo.each(this.childForms || [], function (f) {
8202 getItems : function()
8204 var r=new Roo.util.MixedCollection(false, function(o){
8205 return o.id || (o.id = Roo.id());
8207 var iter = function(el) {
8214 Roo.each(el.items,function(e) {
8223 hideFields : function(items)
8225 Roo.each(items, function(i){
8227 var f = this.findField(i);
8233 if(f.xtype == 'DateField'){
8234 f.setVisible(false);
8243 showFields : function(items)
8245 Roo.each(items, function(i){
8247 var f = this.findField(i);
8253 if(f.xtype == 'DateField'){
8265 Roo.apply(Roo.bootstrap.Form, {
8292 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8293 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8294 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8295 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8298 this.maskEl.top.enableDisplayMode("block");
8299 this.maskEl.left.enableDisplayMode("block");
8300 this.maskEl.bottom.enableDisplayMode("block");
8301 this.maskEl.right.enableDisplayMode("block");
8303 this.toolTip = new Roo.bootstrap.Tooltip({
8304 cls : 'roo-form-error-popover',
8306 'left' : ['r-l', [-2,0], 'right'],
8307 'right' : ['l-r', [2,0], 'left'],
8308 'bottom' : ['tl-bl', [0,2], 'top'],
8309 'top' : [ 'bl-tl', [0,-2], 'bottom']
8313 this.toolTip.render(Roo.get(document.body));
8315 this.toolTip.el.enableDisplayMode("block");
8317 Roo.get(document.body).on('click', function(){
8321 Roo.get(document.body).on('touchstart', function(){
8325 this.isApplied = true
8328 mask : function(form, target)
8332 this.target = target;
8334 if(!this.form.errorMask || !target.el){
8338 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8340 Roo.log(scrollable);
8342 var ot = this.target.el.calcOffsetsTo(scrollable);
8344 var scrollTo = ot[1] - this.form.maskOffset;
8346 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8348 scrollable.scrollTo('top', scrollTo);
8350 var box = this.target.el.getBox();
8352 var zIndex = Roo.bootstrap.Modal.zIndex++;
8355 this.maskEl.top.setStyle('position', 'absolute');
8356 this.maskEl.top.setStyle('z-index', zIndex);
8357 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8358 this.maskEl.top.setLeft(0);
8359 this.maskEl.top.setTop(0);
8360 this.maskEl.top.show();
8362 this.maskEl.left.setStyle('position', 'absolute');
8363 this.maskEl.left.setStyle('z-index', zIndex);
8364 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8365 this.maskEl.left.setLeft(0);
8366 this.maskEl.left.setTop(box.y - this.padding);
8367 this.maskEl.left.show();
8369 this.maskEl.bottom.setStyle('position', 'absolute');
8370 this.maskEl.bottom.setStyle('z-index', zIndex);
8371 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8372 this.maskEl.bottom.setLeft(0);
8373 this.maskEl.bottom.setTop(box.bottom + this.padding);
8374 this.maskEl.bottom.show();
8376 this.maskEl.right.setStyle('position', 'absolute');
8377 this.maskEl.right.setStyle('z-index', zIndex);
8378 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8379 this.maskEl.right.setLeft(box.right + this.padding);
8380 this.maskEl.right.setTop(box.y - this.padding);
8381 this.maskEl.right.show();
8383 this.toolTip.bindEl = this.target.el;
8385 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8387 var tip = this.target.blankText;
8389 if(this.target.getValue() !== '' ) {
8391 if (this.target.invalidText.length) {
8392 tip = this.target.invalidText;
8393 } else if (this.target.regexText.length){
8394 tip = this.target.regexText;
8398 this.toolTip.show(tip);
8400 this.intervalID = window.setInterval(function() {
8401 Roo.bootstrap.Form.popover.unmask();
8404 window.onwheel = function(){ return false;};
8406 (function(){ this.isMasked = true; }).defer(500, this);
8412 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8416 this.maskEl.top.setStyle('position', 'absolute');
8417 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8418 this.maskEl.top.hide();
8420 this.maskEl.left.setStyle('position', 'absolute');
8421 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8422 this.maskEl.left.hide();
8424 this.maskEl.bottom.setStyle('position', 'absolute');
8425 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8426 this.maskEl.bottom.hide();
8428 this.maskEl.right.setStyle('position', 'absolute');
8429 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8430 this.maskEl.right.hide();
8432 this.toolTip.hide();
8434 this.toolTip.el.hide();
8436 window.onwheel = function(){ return true;};
8438 if(this.intervalID){
8439 window.clearInterval(this.intervalID);
8440 this.intervalID = false;
8443 this.isMasked = false;
8453 * Ext JS Library 1.1.1
8454 * Copyright(c) 2006-2007, Ext JS, LLC.
8456 * Originally Released Under LGPL - original licence link has changed is not relivant.
8459 * <script type="text/javascript">
8462 * @class Roo.form.VTypes
8463 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8466 Roo.form.VTypes = function(){
8467 // closure these in so they are only created once.
8468 var alpha = /^[a-zA-Z_]+$/;
8469 var alphanum = /^[a-zA-Z0-9_]+$/;
8470 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8471 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8473 // All these messages and functions are configurable
8476 * The function used to validate email addresses
8477 * @param {String} value The email address
8479 'email' : function(v){
8480 return email.test(v);
8483 * The error text to display when the email validation function returns false
8486 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8488 * The keystroke filter mask to be applied on email input
8491 'emailMask' : /[a-z0-9_\.\-@]/i,
8494 * The function used to validate URLs
8495 * @param {String} value The URL
8497 'url' : function(v){
8501 * The error text to display when the url validation function returns false
8504 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8507 * The function used to validate alpha values
8508 * @param {String} value The value
8510 'alpha' : function(v){
8511 return alpha.test(v);
8514 * The error text to display when the alpha validation function returns false
8517 'alphaText' : 'This field should only contain letters and _',
8519 * The keystroke filter mask to be applied on alpha input
8522 'alphaMask' : /[a-z_]/i,
8525 * The function used to validate alphanumeric values
8526 * @param {String} value The value
8528 'alphanum' : function(v){
8529 return alphanum.test(v);
8532 * The error text to display when the alphanumeric validation function returns false
8535 'alphanumText' : 'This field should only contain letters, numbers and _',
8537 * The keystroke filter mask to be applied on alphanumeric input
8540 'alphanumMask' : /[a-z0-9_]/i
8550 * @class Roo.bootstrap.Input
8551 * @extends Roo.bootstrap.Component
8552 * Bootstrap Input class
8553 * @cfg {Boolean} disabled is it disabled
8554 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8555 * @cfg {String} name name of the input
8556 * @cfg {string} fieldLabel - the label associated
8557 * @cfg {string} placeholder - placeholder to put in text.
8558 * @cfg {string} before - input group add on before
8559 * @cfg {string} after - input group add on after
8560 * @cfg {string} size - (lg|sm) or leave empty..
8561 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8562 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8563 * @cfg {Number} md colspan out of 12 for computer-sized screens
8564 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8565 * @cfg {string} value default value of the input
8566 * @cfg {Number} labelWidth set the width of label
8567 * @cfg {Number} labellg set the width of label (1-12)
8568 * @cfg {Number} labelmd set the width of label (1-12)
8569 * @cfg {Number} labelsm set the width of label (1-12)
8570 * @cfg {Number} labelxs set the width of label (1-12)
8571 * @cfg {String} labelAlign (top|left)
8572 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8573 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8574 * @cfg {String} indicatorpos (left|right) default left
8575 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8576 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8578 * @cfg {String} align (left|center|right) Default left
8579 * @cfg {Boolean} forceFeedback (true|false) Default false
8582 * Create a new Input
8583 * @param {Object} config The config object
8586 Roo.bootstrap.Input = function(config){
8588 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8593 * Fires when this field receives input focus.
8594 * @param {Roo.form.Field} this
8599 * Fires when this field loses input focus.
8600 * @param {Roo.form.Field} this
8605 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8606 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8607 * @param {Roo.form.Field} this
8608 * @param {Roo.EventObject} e The event object
8613 * Fires just before the field blurs if the field value has changed.
8614 * @param {Roo.form.Field} this
8615 * @param {Mixed} newValue The new value
8616 * @param {Mixed} oldValue The original value
8621 * Fires after the field has been marked as invalid.
8622 * @param {Roo.form.Field} this
8623 * @param {String} msg The validation message
8628 * Fires after the field has been validated with no errors.
8629 * @param {Roo.form.Field} this
8634 * Fires after the key up
8635 * @param {Roo.form.Field} this
8636 * @param {Roo.EventObject} e The event Object
8642 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8644 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8645 automatic validation (defaults to "keyup").
8647 validationEvent : "keyup",
8649 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8651 validateOnBlur : true,
8653 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8655 validationDelay : 250,
8657 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8659 focusClass : "x-form-focus", // not needed???
8663 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8665 invalidClass : "has-warning",
8668 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8670 validClass : "has-success",
8673 * @cfg {Boolean} hasFeedback (true|false) default true
8678 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8680 invalidFeedbackClass : "glyphicon-warning-sign",
8683 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8685 validFeedbackClass : "glyphicon-ok",
8688 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8690 selectOnFocus : false,
8693 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8697 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8702 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8704 disableKeyFilter : false,
8707 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8711 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8715 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8717 blankText : "Please complete this mandatory field",
8720 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8724 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8726 maxLength : Number.MAX_VALUE,
8728 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8730 minLengthText : "The minimum length for this field is {0}",
8732 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8734 maxLengthText : "The maximum length for this field is {0}",
8738 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8739 * If available, this function will be called only after the basic validators all return true, and will be passed the
8740 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8744 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8745 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8746 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8750 * @cfg {String} regexText -- Depricated - use Invalid Text
8755 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8761 autocomplete: false,
8780 formatedValue : false,
8781 forceFeedback : false,
8783 indicatorpos : 'left',
8793 parentLabelAlign : function()
8796 while (parent.parent()) {
8797 parent = parent.parent();
8798 if (typeof(parent.labelAlign) !='undefined') {
8799 return parent.labelAlign;
8806 getAutoCreate : function()
8808 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8814 if(this.inputType != 'hidden'){
8815 cfg.cls = 'form-group' //input-group
8821 type : this.inputType,
8823 cls : 'form-control',
8824 placeholder : this.placeholder || '',
8825 autocomplete : this.autocomplete || 'new-password'
8828 if(this.capture.length){
8829 input.capture = this.capture;
8832 if(this.accept.length){
8833 input.accept = this.accept + "/*";
8837 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8840 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8841 input.maxLength = this.maxLength;
8844 if (this.disabled) {
8845 input.disabled=true;
8848 if (this.readOnly) {
8849 input.readonly=true;
8853 input.name = this.name;
8857 input.cls += ' input-' + this.size;
8861 ['xs','sm','md','lg'].map(function(size){
8862 if (settings[size]) {
8863 cfg.cls += ' col-' + size + '-' + settings[size];
8867 var inputblock = input;
8871 cls: 'glyphicon form-control-feedback'
8874 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8877 cls : 'has-feedback',
8885 if (this.before || this.after) {
8888 cls : 'input-group',
8892 if (this.before && typeof(this.before) == 'string') {
8894 inputblock.cn.push({
8896 cls : 'roo-input-before input-group-addon',
8900 if (this.before && typeof(this.before) == 'object') {
8901 this.before = Roo.factory(this.before);
8903 inputblock.cn.push({
8905 cls : 'roo-input-before input-group-' +
8906 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8910 inputblock.cn.push(input);
8912 if (this.after && typeof(this.after) == 'string') {
8913 inputblock.cn.push({
8915 cls : 'roo-input-after input-group-addon',
8919 if (this.after && typeof(this.after) == 'object') {
8920 this.after = Roo.factory(this.after);
8922 inputblock.cn.push({
8924 cls : 'roo-input-after input-group-' +
8925 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8929 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8930 inputblock.cls += ' has-feedback';
8931 inputblock.cn.push(feedback);
8935 if (align ==='left' && this.fieldLabel.length) {
8937 cfg.cls += ' roo-form-group-label-left';
8942 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8943 tooltip : 'This field is required'
8948 cls : 'control-label',
8949 html : this.fieldLabel
8960 var labelCfg = cfg.cn[1];
8961 var contentCfg = cfg.cn[2];
8963 if(this.indicatorpos == 'right'){
8968 cls : 'control-label',
8972 html : this.fieldLabel
8976 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8977 tooltip : 'This field is required'
8990 labelCfg = cfg.cn[0];
8991 contentCfg = cfg.cn[1];
8995 if(this.labelWidth > 12){
8996 labelCfg.style = "width: " + this.labelWidth + 'px';
8999 if(this.labelWidth < 13 && this.labelmd == 0){
9000 this.labelmd = this.labelWidth;
9003 if(this.labellg > 0){
9004 labelCfg.cls += ' col-lg-' + this.labellg;
9005 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9008 if(this.labelmd > 0){
9009 labelCfg.cls += ' col-md-' + this.labelmd;
9010 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9013 if(this.labelsm > 0){
9014 labelCfg.cls += ' col-sm-' + this.labelsm;
9015 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9018 if(this.labelxs > 0){
9019 labelCfg.cls += ' col-xs-' + this.labelxs;
9020 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9024 } else if ( this.fieldLabel.length) {
9029 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9030 tooltip : 'This field is required'
9034 //cls : 'input-group-addon',
9035 html : this.fieldLabel
9043 if(this.indicatorpos == 'right'){
9048 //cls : 'input-group-addon',
9049 html : this.fieldLabel
9054 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9055 tooltip : 'This field is required'
9075 if (this.parentType === 'Navbar' && this.parent().bar) {
9076 cfg.cls += ' navbar-form';
9079 if (this.parentType === 'NavGroup') {
9080 cfg.cls += ' navbar-form';
9088 * return the real input element.
9090 inputEl: function ()
9092 return this.el.select('input.form-control',true).first();
9095 tooltipEl : function()
9097 return this.inputEl();
9100 indicatorEl : function()
9102 var indicator = this.el.select('i.roo-required-indicator',true).first();
9112 setDisabled : function(v)
9114 var i = this.inputEl().dom;
9116 i.removeAttribute('disabled');
9120 i.setAttribute('disabled','true');
9122 initEvents : function()
9125 this.inputEl().on("keydown" , this.fireKey, this);
9126 this.inputEl().on("focus", this.onFocus, this);
9127 this.inputEl().on("blur", this.onBlur, this);
9129 this.inputEl().relayEvent('keyup', this);
9131 this.indicator = this.indicatorEl();
9134 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9137 // reference to original value for reset
9138 this.originalValue = this.getValue();
9139 //Roo.form.TextField.superclass.initEvents.call(this);
9140 if(this.validationEvent == 'keyup'){
9141 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9142 this.inputEl().on('keyup', this.filterValidation, this);
9144 else if(this.validationEvent !== false){
9145 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9148 if(this.selectOnFocus){
9149 this.on("focus", this.preFocus, this);
9152 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9153 this.inputEl().on("keypress", this.filterKeys, this);
9155 this.inputEl().relayEvent('keypress', this);
9158 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9159 this.el.on("click", this.autoSize, this);
9162 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9163 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9166 if (typeof(this.before) == 'object') {
9167 this.before.render(this.el.select('.roo-input-before',true).first());
9169 if (typeof(this.after) == 'object') {
9170 this.after.render(this.el.select('.roo-input-after',true).first());
9173 this.inputEl().on('change', this.onChange, this);
9176 filterValidation : function(e){
9177 if(!e.isNavKeyPress()){
9178 this.validationTask.delay(this.validationDelay);
9182 * Validates the field value
9183 * @return {Boolean} True if the value is valid, else false
9185 validate : function(){
9186 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9187 if(this.disabled || this.validateValue(this.getRawValue())){
9198 * Validates a value according to the field's validation rules and marks the field as invalid
9199 * if the validation fails
9200 * @param {Mixed} value The value to validate
9201 * @return {Boolean} True if the value is valid, else false
9203 validateValue : function(value)
9205 if(this.getVisibilityEl().hasClass('hidden')){
9209 if(value.length < 1) { // if it's blank
9210 if(this.allowBlank){
9216 if(value.length < this.minLength){
9219 if(value.length > this.maxLength){
9223 var vt = Roo.form.VTypes;
9224 if(!vt[this.vtype](value, this)){
9228 if(typeof this.validator == "function"){
9229 var msg = this.validator(value);
9233 if (typeof(msg) == 'string') {
9234 this.invalidText = msg;
9238 if(this.regex && !this.regex.test(value)){
9246 fireKey : function(e){
9247 //Roo.log('field ' + e.getKey());
9248 if(e.isNavKeyPress()){
9249 this.fireEvent("specialkey", this, e);
9252 focus : function (selectText){
9254 this.inputEl().focus();
9255 if(selectText === true){
9256 this.inputEl().dom.select();
9262 onFocus : function(){
9263 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9264 // this.el.addClass(this.focusClass);
9267 this.hasFocus = true;
9268 this.startValue = this.getValue();
9269 this.fireEvent("focus", this);
9273 beforeBlur : Roo.emptyFn,
9277 onBlur : function(){
9279 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9280 //this.el.removeClass(this.focusClass);
9282 this.hasFocus = false;
9283 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9286 var v = this.getValue();
9287 if(String(v) !== String(this.startValue)){
9288 this.fireEvent('change', this, v, this.startValue);
9290 this.fireEvent("blur", this);
9293 onChange : function(e)
9295 var v = this.getValue();
9296 if(String(v) !== String(this.startValue)){
9297 this.fireEvent('change', this, v, this.startValue);
9303 * Resets the current field value to the originally loaded value and clears any validation messages
9306 this.setValue(this.originalValue);
9310 * Returns the name of the field
9311 * @return {Mixed} name The name field
9313 getName: function(){
9317 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9318 * @return {Mixed} value The field value
9320 getValue : function(){
9322 var v = this.inputEl().getValue();
9327 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9328 * @return {Mixed} value The field value
9330 getRawValue : function(){
9331 var v = this.inputEl().getValue();
9337 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9338 * @param {Mixed} value The value to set
9340 setRawValue : function(v){
9341 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9344 selectText : function(start, end){
9345 var v = this.getRawValue();
9347 start = start === undefined ? 0 : start;
9348 end = end === undefined ? v.length : end;
9349 var d = this.inputEl().dom;
9350 if(d.setSelectionRange){
9351 d.setSelectionRange(start, end);
9352 }else if(d.createTextRange){
9353 var range = d.createTextRange();
9354 range.moveStart("character", start);
9355 range.moveEnd("character", v.length-end);
9362 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9363 * @param {Mixed} value The value to set
9365 setValue : function(v){
9368 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9374 processValue : function(value){
9375 if(this.stripCharsRe){
9376 var newValue = value.replace(this.stripCharsRe, '');
9377 if(newValue !== value){
9378 this.setRawValue(newValue);
9385 preFocus : function(){
9387 if(this.selectOnFocus){
9388 this.inputEl().dom.select();
9391 filterKeys : function(e){
9393 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9396 var c = e.getCharCode(), cc = String.fromCharCode(c);
9397 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9400 if(!this.maskRe.test(cc)){
9405 * Clear any invalid styles/messages for this field
9407 clearInvalid : function(){
9409 if(!this.el || this.preventMark){ // not rendered
9414 this.el.removeClass(this.invalidClass);
9416 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9418 var feedback = this.el.select('.form-control-feedback', true).first();
9421 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9427 this.indicator.removeClass('visible');
9428 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9431 this.fireEvent('valid', this);
9435 * Mark this field as valid
9437 markValid : function()
9439 if(!this.el || this.preventMark){ // not rendered...
9443 this.el.removeClass([this.invalidClass, this.validClass]);
9445 var feedback = this.el.select('.form-control-feedback', true).first();
9448 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9452 this.indicator.removeClass('visible');
9453 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9460 if(this.allowBlank && !this.getRawValue().length){
9464 this.el.addClass(this.validClass);
9466 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9468 var feedback = this.el.select('.form-control-feedback', true).first();
9471 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9472 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9477 this.fireEvent('valid', this);
9481 * Mark this field as invalid
9482 * @param {String} msg The validation message
9484 markInvalid : function(msg)
9486 if(!this.el || this.preventMark){ // not rendered
9490 this.el.removeClass([this.invalidClass, this.validClass]);
9492 var feedback = this.el.select('.form-control-feedback', true).first();
9495 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9502 if(this.allowBlank && !this.getRawValue().length){
9507 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9508 this.indicator.addClass('visible');
9511 this.el.addClass(this.invalidClass);
9513 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9515 var feedback = this.el.select('.form-control-feedback', true).first();
9518 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9520 if(this.getValue().length || this.forceFeedback){
9521 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9528 this.fireEvent('invalid', this, msg);
9531 SafariOnKeyDown : function(event)
9533 // this is a workaround for a password hang bug on chrome/ webkit.
9534 if (this.inputEl().dom.type != 'password') {
9538 var isSelectAll = false;
9540 if(this.inputEl().dom.selectionEnd > 0){
9541 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9543 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9544 event.preventDefault();
9549 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9551 event.preventDefault();
9552 // this is very hacky as keydown always get's upper case.
9554 var cc = String.fromCharCode(event.getCharCode());
9555 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9559 adjustWidth : function(tag, w){
9560 tag = tag.toLowerCase();
9561 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9562 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9566 if(tag == 'textarea'){
9569 }else if(Roo.isOpera){
9573 if(tag == 'textarea'){
9581 setFieldLabel : function(v)
9588 var ar = this.el.select('label > span',true);
9590 if (ar.elements.length) {
9591 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9592 this.fieldLabel = v;
9596 var br = this.el.select('label',true);
9598 if(br.elements.length) {
9599 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9600 this.fieldLabel = v;
9604 Roo.log('Cannot Found any of label > span || label in input');
9608 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9609 this.fieldLabel = v;
9624 * @class Roo.bootstrap.TextArea
9625 * @extends Roo.bootstrap.Input
9626 * Bootstrap TextArea class
9627 * @cfg {Number} cols Specifies the visible width of a text area
9628 * @cfg {Number} rows Specifies the visible number of lines in a text area
9629 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9630 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9631 * @cfg {string} html text
9634 * Create a new TextArea
9635 * @param {Object} config The config object
9638 Roo.bootstrap.TextArea = function(config){
9639 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9643 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9653 getAutoCreate : function(){
9655 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9661 if(this.inputType != 'hidden'){
9662 cfg.cls = 'form-group' //input-group
9670 value : this.value || '',
9671 html: this.html || '',
9672 cls : 'form-control',
9673 placeholder : this.placeholder || ''
9677 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9678 input.maxLength = this.maxLength;
9682 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9686 input.cols = this.cols;
9689 if (this.readOnly) {
9690 input.readonly = true;
9694 input.name = this.name;
9698 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9702 ['xs','sm','md','lg'].map(function(size){
9703 if (settings[size]) {
9704 cfg.cls += ' col-' + size + '-' + settings[size];
9708 var inputblock = input;
9710 if(this.hasFeedback && !this.allowBlank){
9714 cls: 'glyphicon form-control-feedback'
9718 cls : 'has-feedback',
9727 if (this.before || this.after) {
9730 cls : 'input-group',
9734 inputblock.cn.push({
9736 cls : 'input-group-addon',
9741 inputblock.cn.push(input);
9743 if(this.hasFeedback && !this.allowBlank){
9744 inputblock.cls += ' has-feedback';
9745 inputblock.cn.push(feedback);
9749 inputblock.cn.push({
9751 cls : 'input-group-addon',
9758 if (align ==='left' && this.fieldLabel.length) {
9763 cls : 'control-label',
9764 html : this.fieldLabel
9775 if(this.labelWidth > 12){
9776 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9779 if(this.labelWidth < 13 && this.labelmd == 0){
9780 this.labelmd = this.labelWidth;
9783 if(this.labellg > 0){
9784 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9785 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9788 if(this.labelmd > 0){
9789 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9790 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9793 if(this.labelsm > 0){
9794 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9795 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9798 if(this.labelxs > 0){
9799 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9800 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9803 } else if ( this.fieldLabel.length) {
9808 //cls : 'input-group-addon',
9809 html : this.fieldLabel
9827 if (this.disabled) {
9828 input.disabled=true;
9835 * return the real textarea element.
9837 inputEl: function ()
9839 return this.el.select('textarea.form-control',true).first();
9843 * Clear any invalid styles/messages for this field
9845 clearInvalid : function()
9848 if(!this.el || this.preventMark){ // not rendered
9852 var label = this.el.select('label', true).first();
9853 var icon = this.el.select('i.fa-star', true).first();
9859 this.el.removeClass(this.invalidClass);
9861 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9863 var feedback = this.el.select('.form-control-feedback', true).first();
9866 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9871 this.fireEvent('valid', this);
9875 * Mark this field as valid
9877 markValid : function()
9879 if(!this.el || this.preventMark){ // not rendered
9883 this.el.removeClass([this.invalidClass, this.validClass]);
9885 var feedback = this.el.select('.form-control-feedback', true).first();
9888 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9891 if(this.disabled || this.allowBlank){
9895 var label = this.el.select('label', true).first();
9896 var icon = this.el.select('i.fa-star', true).first();
9902 this.el.addClass(this.validClass);
9904 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9906 var feedback = this.el.select('.form-control-feedback', true).first();
9909 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9910 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9915 this.fireEvent('valid', this);
9919 * Mark this field as invalid
9920 * @param {String} msg The validation message
9922 markInvalid : function(msg)
9924 if(!this.el || this.preventMark){ // not rendered
9928 this.el.removeClass([this.invalidClass, this.validClass]);
9930 var feedback = this.el.select('.form-control-feedback', true).first();
9933 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9936 if(this.disabled || this.allowBlank){
9940 var label = this.el.select('label', true).first();
9941 var icon = this.el.select('i.fa-star', true).first();
9943 if(!this.getValue().length && label && !icon){
9944 this.el.createChild({
9946 cls : 'text-danger fa fa-lg fa-star',
9947 tooltip : 'This field is required',
9948 style : 'margin-right:5px;'
9952 this.el.addClass(this.invalidClass);
9954 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9956 var feedback = this.el.select('.form-control-feedback', true).first();
9959 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9961 if(this.getValue().length || this.forceFeedback){
9962 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9969 this.fireEvent('invalid', this, msg);
9977 * trigger field - base class for combo..
9982 * @class Roo.bootstrap.TriggerField
9983 * @extends Roo.bootstrap.Input
9984 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9985 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9986 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9987 * for which you can provide a custom implementation. For example:
9989 var trigger = new Roo.bootstrap.TriggerField();
9990 trigger.onTriggerClick = myTriggerFn;
9991 trigger.applyTo('my-field');
9994 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9995 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9996 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9997 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9998 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10001 * Create a new TriggerField.
10002 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10003 * to the base TextField)
10005 Roo.bootstrap.TriggerField = function(config){
10006 this.mimicing = false;
10007 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10010 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10012 * @cfg {String} triggerClass A CSS class to apply to the trigger
10015 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10020 * @cfg {Boolean} removable (true|false) special filter default false
10024 /** @cfg {Boolean} grow @hide */
10025 /** @cfg {Number} growMin @hide */
10026 /** @cfg {Number} growMax @hide */
10032 autoSize: Roo.emptyFn,
10036 deferHeight : true,
10039 actionMode : 'wrap',
10044 getAutoCreate : function(){
10046 var align = this.labelAlign || this.parentLabelAlign();
10051 cls: 'form-group' //input-group
10058 type : this.inputType,
10059 cls : 'form-control',
10060 autocomplete: 'new-password',
10061 placeholder : this.placeholder || ''
10065 input.name = this.name;
10068 input.cls += ' input-' + this.size;
10071 if (this.disabled) {
10072 input.disabled=true;
10075 var inputblock = input;
10077 if(this.hasFeedback && !this.allowBlank){
10081 cls: 'glyphicon form-control-feedback'
10084 if(this.removable && !this.editable && !this.tickable){
10086 cls : 'has-feedback',
10092 cls : 'roo-combo-removable-btn close'
10099 cls : 'has-feedback',
10108 if(this.removable && !this.editable && !this.tickable){
10110 cls : 'roo-removable',
10116 cls : 'roo-combo-removable-btn close'
10123 if (this.before || this.after) {
10126 cls : 'input-group',
10130 inputblock.cn.push({
10132 cls : 'input-group-addon',
10137 inputblock.cn.push(input);
10139 if(this.hasFeedback && !this.allowBlank){
10140 inputblock.cls += ' has-feedback';
10141 inputblock.cn.push(feedback);
10145 inputblock.cn.push({
10147 cls : 'input-group-addon',
10160 cls: 'form-hidden-field'
10174 cls: 'form-hidden-field'
10178 cls: 'roo-select2-choices',
10182 cls: 'roo-select2-search-field',
10195 cls: 'roo-select2-container input-group',
10200 // cls: 'typeahead typeahead-long dropdown-menu',
10201 // style: 'display:none'
10206 if(!this.multiple && this.showToggleBtn){
10212 if (this.caret != false) {
10215 cls: 'fa fa-' + this.caret
10222 cls : 'input-group-addon btn dropdown-toggle',
10227 cls: 'combobox-clear',
10241 combobox.cls += ' roo-select2-container-multi';
10244 if (align ==='left' && this.fieldLabel.length) {
10246 cfg.cls += ' roo-form-group-label-left';
10251 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10252 tooltip : 'This field is required'
10257 cls : 'control-label',
10258 html : this.fieldLabel
10270 var labelCfg = cfg.cn[1];
10271 var contentCfg = cfg.cn[2];
10273 if(this.indicatorpos == 'right'){
10278 cls : 'control-label',
10282 html : this.fieldLabel
10286 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10287 tooltip : 'This field is required'
10300 labelCfg = cfg.cn[0];
10301 contentCfg = cfg.cn[1];
10304 if(this.labelWidth > 12){
10305 labelCfg.style = "width: " + this.labelWidth + 'px';
10308 if(this.labelWidth < 13 && this.labelmd == 0){
10309 this.labelmd = this.labelWidth;
10312 if(this.labellg > 0){
10313 labelCfg.cls += ' col-lg-' + this.labellg;
10314 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10317 if(this.labelmd > 0){
10318 labelCfg.cls += ' col-md-' + this.labelmd;
10319 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10322 if(this.labelsm > 0){
10323 labelCfg.cls += ' col-sm-' + this.labelsm;
10324 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10327 if(this.labelxs > 0){
10328 labelCfg.cls += ' col-xs-' + this.labelxs;
10329 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10332 } else if ( this.fieldLabel.length) {
10333 // Roo.log(" label");
10337 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10338 tooltip : 'This field is required'
10342 //cls : 'input-group-addon',
10343 html : this.fieldLabel
10351 if(this.indicatorpos == 'right'){
10359 html : this.fieldLabel
10363 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10364 tooltip : 'This field is required'
10377 // Roo.log(" no label && no align");
10384 ['xs','sm','md','lg'].map(function(size){
10385 if (settings[size]) {
10386 cfg.cls += ' col-' + size + '-' + settings[size];
10397 onResize : function(w, h){
10398 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10399 // if(typeof w == 'number'){
10400 // var x = w - this.trigger.getWidth();
10401 // this.inputEl().setWidth(this.adjustWidth('input', x));
10402 // this.trigger.setStyle('left', x+'px');
10407 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10410 getResizeEl : function(){
10411 return this.inputEl();
10415 getPositionEl : function(){
10416 return this.inputEl();
10420 alignErrorIcon : function(){
10421 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10425 initEvents : function(){
10429 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10430 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10431 if(!this.multiple && this.showToggleBtn){
10432 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10433 if(this.hideTrigger){
10434 this.trigger.setDisplayed(false);
10436 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10440 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10443 if(this.removable && !this.editable && !this.tickable){
10444 var close = this.closeTriggerEl();
10447 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10448 close.on('click', this.removeBtnClick, this, close);
10452 //this.trigger.addClassOnOver('x-form-trigger-over');
10453 //this.trigger.addClassOnClick('x-form-trigger-click');
10456 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10460 closeTriggerEl : function()
10462 var close = this.el.select('.roo-combo-removable-btn', true).first();
10463 return close ? close : false;
10466 removeBtnClick : function(e, h, el)
10468 e.preventDefault();
10470 if(this.fireEvent("remove", this) !== false){
10472 this.fireEvent("afterremove", this)
10476 createList : function()
10478 this.list = Roo.get(document.body).createChild({
10480 cls: 'typeahead typeahead-long dropdown-menu',
10481 style: 'display:none'
10484 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10489 initTrigger : function(){
10494 onDestroy : function(){
10496 this.trigger.removeAllListeners();
10497 // this.trigger.remove();
10500 // this.wrap.remove();
10502 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10506 onFocus : function(){
10507 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10509 if(!this.mimicing){
10510 this.wrap.addClass('x-trigger-wrap-focus');
10511 this.mimicing = true;
10512 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10513 if(this.monitorTab){
10514 this.el.on("keydown", this.checkTab, this);
10521 checkTab : function(e){
10522 if(e.getKey() == e.TAB){
10523 this.triggerBlur();
10528 onBlur : function(){
10533 mimicBlur : function(e, t){
10535 if(!this.wrap.contains(t) && this.validateBlur()){
10536 this.triggerBlur();
10542 triggerBlur : function(){
10543 this.mimicing = false;
10544 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10545 if(this.monitorTab){
10546 this.el.un("keydown", this.checkTab, this);
10548 //this.wrap.removeClass('x-trigger-wrap-focus');
10549 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10553 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10554 validateBlur : function(e, t){
10559 onDisable : function(){
10560 this.inputEl().dom.disabled = true;
10561 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10563 // this.wrap.addClass('x-item-disabled');
10568 onEnable : function(){
10569 this.inputEl().dom.disabled = false;
10570 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10572 // this.el.removeClass('x-item-disabled');
10577 onShow : function(){
10578 var ae = this.getActionEl();
10581 ae.dom.style.display = '';
10582 ae.dom.style.visibility = 'visible';
10588 onHide : function(){
10589 var ae = this.getActionEl();
10590 ae.dom.style.display = 'none';
10594 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10595 * by an implementing function.
10597 * @param {EventObject} e
10599 onTriggerClick : Roo.emptyFn
10603 * Ext JS Library 1.1.1
10604 * Copyright(c) 2006-2007, Ext JS, LLC.
10606 * Originally Released Under LGPL - original licence link has changed is not relivant.
10609 * <script type="text/javascript">
10614 * @class Roo.data.SortTypes
10616 * Defines the default sorting (casting?) comparison functions used when sorting data.
10618 Roo.data.SortTypes = {
10620 * Default sort that does nothing
10621 * @param {Mixed} s The value being converted
10622 * @return {Mixed} The comparison value
10624 none : function(s){
10629 * The regular expression used to strip tags
10633 stripTagsRE : /<\/?[^>]+>/gi,
10636 * Strips all HTML tags to sort on text only
10637 * @param {Mixed} s The value being converted
10638 * @return {String} The comparison value
10640 asText : function(s){
10641 return String(s).replace(this.stripTagsRE, "");
10645 * Strips all HTML tags to sort on text only - Case insensitive
10646 * @param {Mixed} s The value being converted
10647 * @return {String} The comparison value
10649 asUCText : function(s){
10650 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10654 * Case insensitive string
10655 * @param {Mixed} s The value being converted
10656 * @return {String} The comparison value
10658 asUCString : function(s) {
10659 return String(s).toUpperCase();
10664 * @param {Mixed} s The value being converted
10665 * @return {Number} The comparison value
10667 asDate : function(s) {
10671 if(s instanceof Date){
10672 return s.getTime();
10674 return Date.parse(String(s));
10679 * @param {Mixed} s The value being converted
10680 * @return {Float} The comparison value
10682 asFloat : function(s) {
10683 var val = parseFloat(String(s).replace(/,/g, ""));
10692 * @param {Mixed} s The value being converted
10693 * @return {Number} The comparison value
10695 asInt : function(s) {
10696 var val = parseInt(String(s).replace(/,/g, ""));
10704 * Ext JS Library 1.1.1
10705 * Copyright(c) 2006-2007, Ext JS, LLC.
10707 * Originally Released Under LGPL - original licence link has changed is not relivant.
10710 * <script type="text/javascript">
10714 * @class Roo.data.Record
10715 * Instances of this class encapsulate both record <em>definition</em> information, and record
10716 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10717 * to access Records cached in an {@link Roo.data.Store} object.<br>
10719 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10720 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10723 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10725 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10726 * {@link #create}. The parameters are the same.
10727 * @param {Array} data An associative Array of data values keyed by the field name.
10728 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10729 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10730 * not specified an integer id is generated.
10732 Roo.data.Record = function(data, id){
10733 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10738 * Generate a constructor for a specific record layout.
10739 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10740 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10741 * Each field definition object may contain the following properties: <ul>
10742 * <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,
10743 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10744 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10745 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10746 * is being used, then this is a string containing the javascript expression to reference the data relative to
10747 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10748 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10749 * this may be omitted.</p></li>
10750 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10751 * <ul><li>auto (Default, implies no conversion)</li>
10756 * <li>date</li></ul></p></li>
10757 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10758 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10759 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10760 * by the Reader into an object that will be stored in the Record. It is passed the
10761 * following parameters:<ul>
10762 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10764 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10766 * <br>usage:<br><pre><code>
10767 var TopicRecord = Roo.data.Record.create(
10768 {name: 'title', mapping: 'topic_title'},
10769 {name: 'author', mapping: 'username'},
10770 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10771 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10772 {name: 'lastPoster', mapping: 'user2'},
10773 {name: 'excerpt', mapping: 'post_text'}
10776 var myNewRecord = new TopicRecord({
10777 title: 'Do my job please',
10780 lastPost: new Date(),
10781 lastPoster: 'Animal',
10782 excerpt: 'No way dude!'
10784 myStore.add(myNewRecord);
10789 Roo.data.Record.create = function(o){
10790 var f = function(){
10791 f.superclass.constructor.apply(this, arguments);
10793 Roo.extend(f, Roo.data.Record);
10794 var p = f.prototype;
10795 p.fields = new Roo.util.MixedCollection(false, function(field){
10798 for(var i = 0, len = o.length; i < len; i++){
10799 p.fields.add(new Roo.data.Field(o[i]));
10801 f.getField = function(name){
10802 return p.fields.get(name);
10807 Roo.data.Record.AUTO_ID = 1000;
10808 Roo.data.Record.EDIT = 'edit';
10809 Roo.data.Record.REJECT = 'reject';
10810 Roo.data.Record.COMMIT = 'commit';
10812 Roo.data.Record.prototype = {
10814 * Readonly flag - true if this record has been modified.
10823 join : function(store){
10824 this.store = store;
10828 * Set the named field to the specified value.
10829 * @param {String} name The name of the field to set.
10830 * @param {Object} value The value to set the field to.
10832 set : function(name, value){
10833 if(this.data[name] == value){
10837 if(!this.modified){
10838 this.modified = {};
10840 if(typeof this.modified[name] == 'undefined'){
10841 this.modified[name] = this.data[name];
10843 this.data[name] = value;
10844 if(!this.editing && this.store){
10845 this.store.afterEdit(this);
10850 * Get the value of the named field.
10851 * @param {String} name The name of the field to get the value of.
10852 * @return {Object} The value of the field.
10854 get : function(name){
10855 return this.data[name];
10859 beginEdit : function(){
10860 this.editing = true;
10861 this.modified = {};
10865 cancelEdit : function(){
10866 this.editing = false;
10867 delete this.modified;
10871 endEdit : function(){
10872 this.editing = false;
10873 if(this.dirty && this.store){
10874 this.store.afterEdit(this);
10879 * Usually called by the {@link Roo.data.Store} which owns the Record.
10880 * Rejects all changes made to the Record since either creation, or the last commit operation.
10881 * Modified fields are reverted to their original values.
10883 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10884 * of reject operations.
10886 reject : function(){
10887 var m = this.modified;
10889 if(typeof m[n] != "function"){
10890 this.data[n] = m[n];
10893 this.dirty = false;
10894 delete this.modified;
10895 this.editing = false;
10897 this.store.afterReject(this);
10902 * Usually called by the {@link Roo.data.Store} which owns the Record.
10903 * Commits all changes made to the Record since either creation, or the last commit operation.
10905 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10906 * of commit operations.
10908 commit : function(){
10909 this.dirty = false;
10910 delete this.modified;
10911 this.editing = false;
10913 this.store.afterCommit(this);
10918 hasError : function(){
10919 return this.error != null;
10923 clearError : function(){
10928 * Creates a copy of this record.
10929 * @param {String} id (optional) A new record id if you don't want to use this record's id
10932 copy : function(newId) {
10933 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10937 * Ext JS Library 1.1.1
10938 * Copyright(c) 2006-2007, Ext JS, LLC.
10940 * Originally Released Under LGPL - original licence link has changed is not relivant.
10943 * <script type="text/javascript">
10949 * @class Roo.data.Store
10950 * @extends Roo.util.Observable
10951 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10952 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10954 * 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
10955 * has no knowledge of the format of the data returned by the Proxy.<br>
10957 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10958 * instances from the data object. These records are cached and made available through accessor functions.
10960 * Creates a new Store.
10961 * @param {Object} config A config object containing the objects needed for the Store to access data,
10962 * and read the data into Records.
10964 Roo.data.Store = function(config){
10965 this.data = new Roo.util.MixedCollection(false);
10966 this.data.getKey = function(o){
10969 this.baseParams = {};
10971 this.paramNames = {
10976 "multisort" : "_multisort"
10979 if(config && config.data){
10980 this.inlineData = config.data;
10981 delete config.data;
10984 Roo.apply(this, config);
10986 if(this.reader){ // reader passed
10987 this.reader = Roo.factory(this.reader, Roo.data);
10988 this.reader.xmodule = this.xmodule || false;
10989 if(!this.recordType){
10990 this.recordType = this.reader.recordType;
10992 if(this.reader.onMetaChange){
10993 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10997 if(this.recordType){
10998 this.fields = this.recordType.prototype.fields;
11000 this.modified = [];
11004 * @event datachanged
11005 * Fires when the data cache has changed, and a widget which is using this Store
11006 * as a Record cache should refresh its view.
11007 * @param {Store} this
11009 datachanged : true,
11011 * @event metachange
11012 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11013 * @param {Store} this
11014 * @param {Object} meta The JSON metadata
11019 * Fires when Records have been added to the Store
11020 * @param {Store} this
11021 * @param {Roo.data.Record[]} records The array of Records added
11022 * @param {Number} index The index at which the record(s) were added
11027 * Fires when a Record has been removed from the Store
11028 * @param {Store} this
11029 * @param {Roo.data.Record} record The Record that was removed
11030 * @param {Number} index The index at which the record was removed
11035 * Fires when a Record has been updated
11036 * @param {Store} this
11037 * @param {Roo.data.Record} record The Record that was updated
11038 * @param {String} operation The update operation being performed. Value may be one of:
11040 Roo.data.Record.EDIT
11041 Roo.data.Record.REJECT
11042 Roo.data.Record.COMMIT
11048 * Fires when the data cache has been cleared.
11049 * @param {Store} this
11053 * @event beforeload
11054 * Fires before a request is made for a new data object. If the beforeload handler returns false
11055 * the load action will be canceled.
11056 * @param {Store} this
11057 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11061 * @event beforeloadadd
11062 * Fires after a new set of Records has been loaded.
11063 * @param {Store} this
11064 * @param {Roo.data.Record[]} records The Records that were loaded
11065 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11067 beforeloadadd : true,
11070 * Fires after a new set of Records has been loaded, before they are added to the store.
11071 * @param {Store} this
11072 * @param {Roo.data.Record[]} records The Records that were loaded
11073 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11074 * @params {Object} return from reader
11078 * @event loadexception
11079 * Fires if an exception occurs in the Proxy during loading.
11080 * Called with the signature of the Proxy's "loadexception" event.
11081 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11084 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11085 * @param {Object} load options
11086 * @param {Object} jsonData from your request (normally this contains the Exception)
11088 loadexception : true
11092 this.proxy = Roo.factory(this.proxy, Roo.data);
11093 this.proxy.xmodule = this.xmodule || false;
11094 this.relayEvents(this.proxy, ["loadexception"]);
11096 this.sortToggle = {};
11097 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11099 Roo.data.Store.superclass.constructor.call(this);
11101 if(this.inlineData){
11102 this.loadData(this.inlineData);
11103 delete this.inlineData;
11107 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11109 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11110 * without a remote query - used by combo/forms at present.
11114 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11117 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11120 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11121 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11124 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11125 * on any HTTP request
11128 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11131 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11135 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11136 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11138 remoteSort : false,
11141 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11142 * loaded or when a record is removed. (defaults to false).
11144 pruneModifiedRecords : false,
11147 lastOptions : null,
11150 * Add Records to the Store and fires the add event.
11151 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11153 add : function(records){
11154 records = [].concat(records);
11155 for(var i = 0, len = records.length; i < len; i++){
11156 records[i].join(this);
11158 var index = this.data.length;
11159 this.data.addAll(records);
11160 this.fireEvent("add", this, records, index);
11164 * Remove a Record from the Store and fires the remove event.
11165 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11167 remove : function(record){
11168 var index = this.data.indexOf(record);
11169 this.data.removeAt(index);
11171 if(this.pruneModifiedRecords){
11172 this.modified.remove(record);
11174 this.fireEvent("remove", this, record, index);
11178 * Remove all Records from the Store and fires the clear event.
11180 removeAll : function(){
11182 if(this.pruneModifiedRecords){
11183 this.modified = [];
11185 this.fireEvent("clear", this);
11189 * Inserts Records to the Store at the given index and fires the add event.
11190 * @param {Number} index The start index at which to insert the passed Records.
11191 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11193 insert : function(index, records){
11194 records = [].concat(records);
11195 for(var i = 0, len = records.length; i < len; i++){
11196 this.data.insert(index, records[i]);
11197 records[i].join(this);
11199 this.fireEvent("add", this, records, index);
11203 * Get the index within the cache of the passed Record.
11204 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11205 * @return {Number} The index of the passed Record. Returns -1 if not found.
11207 indexOf : function(record){
11208 return this.data.indexOf(record);
11212 * Get the index within the cache of the Record with the passed id.
11213 * @param {String} id The id of the Record to find.
11214 * @return {Number} The index of the Record. Returns -1 if not found.
11216 indexOfId : function(id){
11217 return this.data.indexOfKey(id);
11221 * Get the Record with the specified id.
11222 * @param {String} id The id of the Record to find.
11223 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11225 getById : function(id){
11226 return this.data.key(id);
11230 * Get the Record at the specified index.
11231 * @param {Number} index The index of the Record to find.
11232 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11234 getAt : function(index){
11235 return this.data.itemAt(index);
11239 * Returns a range of Records between specified indices.
11240 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11241 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11242 * @return {Roo.data.Record[]} An array of Records
11244 getRange : function(start, end){
11245 return this.data.getRange(start, end);
11249 storeOptions : function(o){
11250 o = Roo.apply({}, o);
11253 this.lastOptions = o;
11257 * Loads the Record cache from the configured Proxy using the configured Reader.
11259 * If using remote paging, then the first load call must specify the <em>start</em>
11260 * and <em>limit</em> properties in the options.params property to establish the initial
11261 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11263 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11264 * and this call will return before the new data has been loaded. Perform any post-processing
11265 * in a callback function, or in a "load" event handler.</strong>
11267 * @param {Object} options An object containing properties which control loading options:<ul>
11268 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11269 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11270 * passed the following arguments:<ul>
11271 * <li>r : Roo.data.Record[]</li>
11272 * <li>options: Options object from the load call</li>
11273 * <li>success: Boolean success indicator</li></ul></li>
11274 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11275 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11278 load : function(options){
11279 options = options || {};
11280 if(this.fireEvent("beforeload", this, options) !== false){
11281 this.storeOptions(options);
11282 var p = Roo.apply(options.params || {}, this.baseParams);
11283 // if meta was not loaded from remote source.. try requesting it.
11284 if (!this.reader.metaFromRemote) {
11285 p._requestMeta = 1;
11287 if(this.sortInfo && this.remoteSort){
11288 var pn = this.paramNames;
11289 p[pn["sort"]] = this.sortInfo.field;
11290 p[pn["dir"]] = this.sortInfo.direction;
11292 if (this.multiSort) {
11293 var pn = this.paramNames;
11294 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11297 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11302 * Reloads the Record cache from the configured Proxy using the configured Reader and
11303 * the options from the last load operation performed.
11304 * @param {Object} options (optional) An object containing properties which may override the options
11305 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11306 * the most recently used options are reused).
11308 reload : function(options){
11309 this.load(Roo.applyIf(options||{}, this.lastOptions));
11313 // Called as a callback by the Reader during a load operation.
11314 loadRecords : function(o, options, success){
11315 if(!o || success === false){
11316 if(success !== false){
11317 this.fireEvent("load", this, [], options, o);
11319 if(options.callback){
11320 options.callback.call(options.scope || this, [], options, false);
11324 // if data returned failure - throw an exception.
11325 if (o.success === false) {
11326 // show a message if no listener is registered.
11327 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11328 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11330 // loadmask wil be hooked into this..
11331 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11334 var r = o.records, t = o.totalRecords || r.length;
11336 this.fireEvent("beforeloadadd", this, r, options, o);
11338 if(!options || options.add !== true){
11339 if(this.pruneModifiedRecords){
11340 this.modified = [];
11342 for(var i = 0, len = r.length; i < len; i++){
11346 this.data = this.snapshot;
11347 delete this.snapshot;
11350 this.data.addAll(r);
11351 this.totalLength = t;
11353 this.fireEvent("datachanged", this);
11355 this.totalLength = Math.max(t, this.data.length+r.length);
11359 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11361 var e = new Roo.data.Record({});
11363 e.set(this.parent.displayField, this.parent.emptyTitle);
11364 e.set(this.parent.valueField, '');
11369 this.fireEvent("load", this, r, options, o);
11370 if(options.callback){
11371 options.callback.call(options.scope || this, r, options, true);
11377 * Loads data from a passed data block. A Reader which understands the format of the data
11378 * must have been configured in the constructor.
11379 * @param {Object} data The data block from which to read the Records. The format of the data expected
11380 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11381 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11383 loadData : function(o, append){
11384 var r = this.reader.readRecords(o);
11385 this.loadRecords(r, {add: append}, true);
11389 * Gets the number of cached records.
11391 * <em>If using paging, this may not be the total size of the dataset. If the data object
11392 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11393 * the data set size</em>
11395 getCount : function(){
11396 return this.data.length || 0;
11400 * Gets the total number of records in the dataset as returned by the server.
11402 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11403 * the dataset size</em>
11405 getTotalCount : function(){
11406 return this.totalLength || 0;
11410 * Returns the sort state of the Store as an object with two properties:
11412 field {String} The name of the field by which the Records are sorted
11413 direction {String} The sort order, "ASC" or "DESC"
11416 getSortState : function(){
11417 return this.sortInfo;
11421 applySort : function(){
11422 if(this.sortInfo && !this.remoteSort){
11423 var s = this.sortInfo, f = s.field;
11424 var st = this.fields.get(f).sortType;
11425 var fn = function(r1, r2){
11426 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11427 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11429 this.data.sort(s.direction, fn);
11430 if(this.snapshot && this.snapshot != this.data){
11431 this.snapshot.sort(s.direction, fn);
11437 * Sets the default sort column and order to be used by the next load operation.
11438 * @param {String} fieldName The name of the field to sort by.
11439 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11441 setDefaultSort : function(field, dir){
11442 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11446 * Sort the Records.
11447 * If remote sorting is used, the sort is performed on the server, and the cache is
11448 * reloaded. If local sorting is used, the cache is sorted internally.
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 sort : function(fieldName, dir){
11453 var f = this.fields.get(fieldName);
11455 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11457 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11458 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11463 this.sortToggle[f.name] = dir;
11464 this.sortInfo = {field: f.name, direction: dir};
11465 if(!this.remoteSort){
11467 this.fireEvent("datachanged", this);
11469 this.load(this.lastOptions);
11474 * Calls the specified function for each of the Records in the cache.
11475 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11476 * Returning <em>false</em> aborts and exits the iteration.
11477 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11479 each : function(fn, scope){
11480 this.data.each(fn, scope);
11484 * Gets all records modified since the last commit. Modified records are persisted across load operations
11485 * (e.g., during paging).
11486 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11488 getModifiedRecords : function(){
11489 return this.modified;
11493 createFilterFn : function(property, value, anyMatch){
11494 if(!value.exec){ // not a regex
11495 value = String(value);
11496 if(value.length == 0){
11499 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11501 return function(r){
11502 return value.test(r.data[property]);
11507 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11508 * @param {String} property A field on your records
11509 * @param {Number} start The record index to start at (defaults to 0)
11510 * @param {Number} end The last record index to include (defaults to length - 1)
11511 * @return {Number} The sum
11513 sum : function(property, start, end){
11514 var rs = this.data.items, v = 0;
11515 start = start || 0;
11516 end = (end || end === 0) ? end : rs.length-1;
11518 for(var i = start; i <= end; i++){
11519 v += (rs[i].data[property] || 0);
11525 * Filter the records by a specified property.
11526 * @param {String} field A field on your records
11527 * @param {String/RegExp} value Either a string that the field
11528 * should start with or a RegExp to test against the field
11529 * @param {Boolean} anyMatch True to match any part not just the beginning
11531 filter : function(property, value, anyMatch){
11532 var fn = this.createFilterFn(property, value, anyMatch);
11533 return fn ? this.filterBy(fn) : this.clearFilter();
11537 * Filter by a function. The specified function will be called with each
11538 * record in this data source. If the function returns true the record is included,
11539 * otherwise it is filtered.
11540 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11541 * @param {Object} scope (optional) The scope of the function (defaults to this)
11543 filterBy : function(fn, scope){
11544 this.snapshot = this.snapshot || this.data;
11545 this.data = this.queryBy(fn, scope||this);
11546 this.fireEvent("datachanged", this);
11550 * Query the records by a specified property.
11551 * @param {String} field A field on your records
11552 * @param {String/RegExp} value Either a string that the field
11553 * should start with or a RegExp to test against the field
11554 * @param {Boolean} anyMatch True to match any part not just the beginning
11555 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11557 query : function(property, value, anyMatch){
11558 var fn = this.createFilterFn(property, value, anyMatch);
11559 return fn ? this.queryBy(fn) : this.data.clone();
11563 * Query by a function. The specified function will be called with each
11564 * record in this data source. If the function returns true the record is included
11566 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11567 * @param {Object} scope (optional) The scope of the function (defaults to this)
11568 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11570 queryBy : function(fn, scope){
11571 var data = this.snapshot || this.data;
11572 return data.filterBy(fn, scope||this);
11576 * Collects unique values for a particular dataIndex from this store.
11577 * @param {String} dataIndex The property to collect
11578 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11579 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11580 * @return {Array} An array of the unique values
11582 collect : function(dataIndex, allowNull, bypassFilter){
11583 var d = (bypassFilter === true && this.snapshot) ?
11584 this.snapshot.items : this.data.items;
11585 var v, sv, r = [], l = {};
11586 for(var i = 0, len = d.length; i < len; i++){
11587 v = d[i].data[dataIndex];
11589 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11598 * Revert to a view of the Record cache with no filtering applied.
11599 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11601 clearFilter : function(suppressEvent){
11602 if(this.snapshot && this.snapshot != this.data){
11603 this.data = this.snapshot;
11604 delete this.snapshot;
11605 if(suppressEvent !== true){
11606 this.fireEvent("datachanged", this);
11612 afterEdit : function(record){
11613 if(this.modified.indexOf(record) == -1){
11614 this.modified.push(record);
11616 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11620 afterReject : function(record){
11621 this.modified.remove(record);
11622 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11626 afterCommit : function(record){
11627 this.modified.remove(record);
11628 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11632 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11633 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11635 commitChanges : function(){
11636 var m = this.modified.slice(0);
11637 this.modified = [];
11638 for(var i = 0, len = m.length; i < len; i++){
11644 * Cancel outstanding changes on all changed records.
11646 rejectChanges : function(){
11647 var m = this.modified.slice(0);
11648 this.modified = [];
11649 for(var i = 0, len = m.length; i < len; i++){
11654 onMetaChange : function(meta, rtype, o){
11655 this.recordType = rtype;
11656 this.fields = rtype.prototype.fields;
11657 delete this.snapshot;
11658 this.sortInfo = meta.sortInfo || this.sortInfo;
11659 this.modified = [];
11660 this.fireEvent('metachange', this, this.reader.meta);
11663 moveIndex : function(data, type)
11665 var index = this.indexOf(data);
11667 var newIndex = index + type;
11671 this.insert(newIndex, data);
11676 * Ext JS Library 1.1.1
11677 * Copyright(c) 2006-2007, Ext JS, LLC.
11679 * Originally Released Under LGPL - original licence link has changed is not relivant.
11682 * <script type="text/javascript">
11686 * @class Roo.data.SimpleStore
11687 * @extends Roo.data.Store
11688 * Small helper class to make creating Stores from Array data easier.
11689 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11690 * @cfg {Array} fields An array of field definition objects, or field name strings.
11691 * @cfg {Array} data The multi-dimensional array of data
11693 * @param {Object} config
11695 Roo.data.SimpleStore = function(config){
11696 Roo.data.SimpleStore.superclass.constructor.call(this, {
11698 reader: new Roo.data.ArrayReader({
11701 Roo.data.Record.create(config.fields)
11703 proxy : new Roo.data.MemoryProxy(config.data)
11707 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11709 * Ext JS Library 1.1.1
11710 * Copyright(c) 2006-2007, Ext JS, LLC.
11712 * Originally Released Under LGPL - original licence link has changed is not relivant.
11715 * <script type="text/javascript">
11720 * @extends Roo.data.Store
11721 * @class Roo.data.JsonStore
11722 * Small helper class to make creating Stores for JSON data easier. <br/>
11724 var store = new Roo.data.JsonStore({
11725 url: 'get-images.php',
11727 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11730 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11731 * JsonReader and HttpProxy (unless inline data is provided).</b>
11732 * @cfg {Array} fields An array of field definition objects, or field name strings.
11734 * @param {Object} config
11736 Roo.data.JsonStore = function(c){
11737 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11738 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11739 reader: new Roo.data.JsonReader(c, c.fields)
11742 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11744 * Ext JS Library 1.1.1
11745 * Copyright(c) 2006-2007, Ext JS, LLC.
11747 * Originally Released Under LGPL - original licence link has changed is not relivant.
11750 * <script type="text/javascript">
11754 Roo.data.Field = function(config){
11755 if(typeof config == "string"){
11756 config = {name: config};
11758 Roo.apply(this, config);
11761 this.type = "auto";
11764 var st = Roo.data.SortTypes;
11765 // named sortTypes are supported, here we look them up
11766 if(typeof this.sortType == "string"){
11767 this.sortType = st[this.sortType];
11770 // set default sortType for strings and dates
11771 if(!this.sortType){
11774 this.sortType = st.asUCString;
11777 this.sortType = st.asDate;
11780 this.sortType = st.none;
11785 var stripRe = /[\$,%]/g;
11787 // prebuilt conversion function for this field, instead of
11788 // switching every time we're reading a value
11790 var cv, dateFormat = this.dateFormat;
11795 cv = function(v){ return v; };
11798 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11802 return v !== undefined && v !== null && v !== '' ?
11803 parseInt(String(v).replace(stripRe, ""), 10) : '';
11808 return v !== undefined && v !== null && v !== '' ?
11809 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11814 cv = function(v){ return v === true || v === "true" || v == 1; };
11821 if(v instanceof Date){
11825 if(dateFormat == "timestamp"){
11826 return new Date(v*1000);
11828 return Date.parseDate(v, dateFormat);
11830 var parsed = Date.parse(v);
11831 return parsed ? new Date(parsed) : null;
11840 Roo.data.Field.prototype = {
11848 * Ext JS Library 1.1.1
11849 * Copyright(c) 2006-2007, Ext JS, LLC.
11851 * Originally Released Under LGPL - original licence link has changed is not relivant.
11854 * <script type="text/javascript">
11857 // Base class for reading structured data from a data source. This class is intended to be
11858 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11861 * @class Roo.data.DataReader
11862 * Base class for reading structured data from a data source. This class is intended to be
11863 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11866 Roo.data.DataReader = function(meta, recordType){
11870 this.recordType = recordType instanceof Array ?
11871 Roo.data.Record.create(recordType) : recordType;
11874 Roo.data.DataReader.prototype = {
11876 * Create an empty record
11877 * @param {Object} data (optional) - overlay some values
11878 * @return {Roo.data.Record} record created.
11880 newRow : function(d) {
11882 this.recordType.prototype.fields.each(function(c) {
11884 case 'int' : da[c.name] = 0; break;
11885 case 'date' : da[c.name] = new Date(); break;
11886 case 'float' : da[c.name] = 0.0; break;
11887 case 'boolean' : da[c.name] = false; break;
11888 default : da[c.name] = ""; break;
11892 return new this.recordType(Roo.apply(da, d));
11897 * Ext JS Library 1.1.1
11898 * Copyright(c) 2006-2007, Ext JS, LLC.
11900 * Originally Released Under LGPL - original licence link has changed is not relivant.
11903 * <script type="text/javascript">
11907 * @class Roo.data.DataProxy
11908 * @extends Roo.data.Observable
11909 * This class is an abstract base class for implementations which provide retrieval of
11910 * unformatted data objects.<br>
11912 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11913 * (of the appropriate type which knows how to parse the data object) to provide a block of
11914 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11916 * Custom implementations must implement the load method as described in
11917 * {@link Roo.data.HttpProxy#load}.
11919 Roo.data.DataProxy = function(){
11922 * @event beforeload
11923 * Fires before a network request is made to retrieve a data object.
11924 * @param {Object} This DataProxy object.
11925 * @param {Object} params The params parameter to the load function.
11930 * Fires before the load method's callback is called.
11931 * @param {Object} This DataProxy object.
11932 * @param {Object} o The data object.
11933 * @param {Object} arg The callback argument object passed to the load function.
11937 * @event loadexception
11938 * Fires if an Exception occurs during data retrieval.
11939 * @param {Object} This DataProxy object.
11940 * @param {Object} o The data object.
11941 * @param {Object} arg The callback argument object passed to the load function.
11942 * @param {Object} e The Exception.
11944 loadexception : true
11946 Roo.data.DataProxy.superclass.constructor.call(this);
11949 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11952 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11956 * Ext JS Library 1.1.1
11957 * Copyright(c) 2006-2007, Ext JS, LLC.
11959 * Originally Released Under LGPL - original licence link has changed is not relivant.
11962 * <script type="text/javascript">
11965 * @class Roo.data.MemoryProxy
11966 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11967 * to the Reader when its load method is called.
11969 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11971 Roo.data.MemoryProxy = function(data){
11975 Roo.data.MemoryProxy.superclass.constructor.call(this);
11979 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11982 * Load data from the requested source (in this case an in-memory
11983 * data object passed to the constructor), read the data object into
11984 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11985 * process that block using the passed callback.
11986 * @param {Object} params This parameter is not used by the MemoryProxy class.
11987 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11988 * object into a block of Roo.data.Records.
11989 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11990 * The function must be passed <ul>
11991 * <li>The Record block object</li>
11992 * <li>The "arg" argument from the load function</li>
11993 * <li>A boolean success indicator</li>
11995 * @param {Object} scope The scope in which to call the callback
11996 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11998 load : function(params, reader, callback, scope, arg){
11999 params = params || {};
12002 result = reader.readRecords(this.data);
12004 this.fireEvent("loadexception", this, arg, null, e);
12005 callback.call(scope, null, arg, false);
12008 callback.call(scope, result, arg, true);
12012 update : function(params, records){
12017 * Ext JS Library 1.1.1
12018 * Copyright(c) 2006-2007, Ext JS, LLC.
12020 * Originally Released Under LGPL - original licence link has changed is not relivant.
12023 * <script type="text/javascript">
12026 * @class Roo.data.HttpProxy
12027 * @extends Roo.data.DataProxy
12028 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12029 * configured to reference a certain URL.<br><br>
12031 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12032 * from which the running page was served.<br><br>
12034 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12036 * Be aware that to enable the browser to parse an XML document, the server must set
12037 * the Content-Type header in the HTTP response to "text/xml".
12039 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12040 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12041 * will be used to make the request.
12043 Roo.data.HttpProxy = function(conn){
12044 Roo.data.HttpProxy.superclass.constructor.call(this);
12045 // is conn a conn config or a real conn?
12047 this.useAjax = !conn || !conn.events;
12051 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12052 // thse are take from connection...
12055 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12058 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12059 * extra parameters to each request made by this object. (defaults to undefined)
12062 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12063 * to each request made by this object. (defaults to undefined)
12066 * @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)
12069 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12072 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12078 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12082 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12083 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12084 * a finer-grained basis than the DataProxy events.
12086 getConnection : function(){
12087 return this.useAjax ? Roo.Ajax : this.conn;
12091 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12092 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12093 * process that block using the passed callback.
12094 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12095 * for the request to the remote server.
12096 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12097 * object into a block of Roo.data.Records.
12098 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12099 * The function must be passed <ul>
12100 * <li>The Record block object</li>
12101 * <li>The "arg" argument from the load function</li>
12102 * <li>A boolean success indicator</li>
12104 * @param {Object} scope The scope in which to call the callback
12105 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12107 load : function(params, reader, callback, scope, arg){
12108 if(this.fireEvent("beforeload", this, params) !== false){
12110 params : params || {},
12112 callback : callback,
12117 callback : this.loadResponse,
12121 Roo.applyIf(o, this.conn);
12122 if(this.activeRequest){
12123 Roo.Ajax.abort(this.activeRequest);
12125 this.activeRequest = Roo.Ajax.request(o);
12127 this.conn.request(o);
12130 callback.call(scope||this, null, arg, false);
12135 loadResponse : function(o, success, response){
12136 delete this.activeRequest;
12138 this.fireEvent("loadexception", this, o, response);
12139 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12144 result = o.reader.read(response);
12146 this.fireEvent("loadexception", this, o, response, e);
12147 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12151 this.fireEvent("load", this, o, o.request.arg);
12152 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12156 update : function(dataSet){
12161 updateResponse : function(dataSet){
12166 * Ext JS Library 1.1.1
12167 * Copyright(c) 2006-2007, Ext JS, LLC.
12169 * Originally Released Under LGPL - original licence link has changed is not relivant.
12172 * <script type="text/javascript">
12176 * @class Roo.data.ScriptTagProxy
12177 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12178 * other than the originating domain of the running page.<br><br>
12180 * <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
12181 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12183 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12184 * source code that is used as the source inside a <script> tag.<br><br>
12186 * In order for the browser to process the returned data, the server must wrap the data object
12187 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12188 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12189 * depending on whether the callback name was passed:
12192 boolean scriptTag = false;
12193 String cb = request.getParameter("callback");
12196 response.setContentType("text/javascript");
12198 response.setContentType("application/x-json");
12200 Writer out = response.getWriter();
12202 out.write(cb + "(");
12204 out.print(dataBlock.toJsonString());
12211 * @param {Object} config A configuration object.
12213 Roo.data.ScriptTagProxy = function(config){
12214 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12215 Roo.apply(this, config);
12216 this.head = document.getElementsByTagName("head")[0];
12219 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12221 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12223 * @cfg {String} url The URL from which to request the data object.
12226 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12230 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12231 * the server the name of the callback function set up by the load call to process the returned data object.
12232 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12233 * javascript output which calls this named function passing the data object as its only parameter.
12235 callbackParam : "callback",
12237 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12238 * name to the request.
12243 * Load data from the configured URL, read the data object into
12244 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12245 * process that block using the passed callback.
12246 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12247 * for the request to the remote server.
12248 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12249 * object into a block of Roo.data.Records.
12250 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12251 * The function must be passed <ul>
12252 * <li>The Record block object</li>
12253 * <li>The "arg" argument from the load function</li>
12254 * <li>A boolean success indicator</li>
12256 * @param {Object} scope The scope in which to call the callback
12257 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12259 load : function(params, reader, callback, scope, arg){
12260 if(this.fireEvent("beforeload", this, params) !== false){
12262 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12264 var url = this.url;
12265 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12267 url += "&_dc=" + (new Date().getTime());
12269 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12272 cb : "stcCallback"+transId,
12273 scriptId : "stcScript"+transId,
12277 callback : callback,
12283 window[trans.cb] = function(o){
12284 conn.handleResponse(o, trans);
12287 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12289 if(this.autoAbort !== false){
12293 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12295 var script = document.createElement("script");
12296 script.setAttribute("src", url);
12297 script.setAttribute("type", "text/javascript");
12298 script.setAttribute("id", trans.scriptId);
12299 this.head.appendChild(script);
12301 this.trans = trans;
12303 callback.call(scope||this, null, arg, false);
12308 isLoading : function(){
12309 return this.trans ? true : false;
12313 * Abort the current server request.
12315 abort : function(){
12316 if(this.isLoading()){
12317 this.destroyTrans(this.trans);
12322 destroyTrans : function(trans, isLoaded){
12323 this.head.removeChild(document.getElementById(trans.scriptId));
12324 clearTimeout(trans.timeoutId);
12326 window[trans.cb] = undefined;
12328 delete window[trans.cb];
12331 // if hasn't been loaded, wait for load to remove it to prevent script error
12332 window[trans.cb] = function(){
12333 window[trans.cb] = undefined;
12335 delete window[trans.cb];
12342 handleResponse : function(o, trans){
12343 this.trans = false;
12344 this.destroyTrans(trans, true);
12347 result = trans.reader.readRecords(o);
12349 this.fireEvent("loadexception", this, o, trans.arg, e);
12350 trans.callback.call(trans.scope||window, null, trans.arg, false);
12353 this.fireEvent("load", this, o, trans.arg);
12354 trans.callback.call(trans.scope||window, result, trans.arg, true);
12358 handleFailure : function(trans){
12359 this.trans = false;
12360 this.destroyTrans(trans, false);
12361 this.fireEvent("loadexception", this, null, trans.arg);
12362 trans.callback.call(trans.scope||window, null, trans.arg, false);
12366 * Ext JS Library 1.1.1
12367 * Copyright(c) 2006-2007, Ext JS, LLC.
12369 * Originally Released Under LGPL - original licence link has changed is not relivant.
12372 * <script type="text/javascript">
12376 * @class Roo.data.JsonReader
12377 * @extends Roo.data.DataReader
12378 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12379 * based on mappings in a provided Roo.data.Record constructor.
12381 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12382 * in the reply previously.
12387 var RecordDef = Roo.data.Record.create([
12388 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12389 {name: 'occupation'} // This field will use "occupation" as the mapping.
12391 var myReader = new Roo.data.JsonReader({
12392 totalProperty: "results", // The property which contains the total dataset size (optional)
12393 root: "rows", // The property which contains an Array of row objects
12394 id: "id" // The property within each row object that provides an ID for the record (optional)
12398 * This would consume a JSON file like this:
12400 { 'results': 2, 'rows': [
12401 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12402 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12405 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12406 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12407 * paged from the remote server.
12408 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12409 * @cfg {String} root name of the property which contains the Array of row objects.
12410 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12411 * @cfg {Array} fields Array of field definition objects
12413 * Create a new JsonReader
12414 * @param {Object} meta Metadata configuration options
12415 * @param {Object} recordType Either an Array of field definition objects,
12416 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12418 Roo.data.JsonReader = function(meta, recordType){
12421 // set some defaults:
12422 Roo.applyIf(meta, {
12423 totalProperty: 'total',
12424 successProperty : 'success',
12429 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12431 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12434 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12435 * Used by Store query builder to append _requestMeta to params.
12438 metaFromRemote : false,
12440 * This method is only used by a DataProxy which has retrieved data from a remote server.
12441 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12442 * @return {Object} data A data block which is used by an Roo.data.Store object as
12443 * a cache of Roo.data.Records.
12445 read : function(response){
12446 var json = response.responseText;
12448 var o = /* eval:var:o */ eval("("+json+")");
12450 throw {message: "JsonReader.read: Json object not found"};
12456 this.metaFromRemote = true;
12457 this.meta = o.metaData;
12458 this.recordType = Roo.data.Record.create(o.metaData.fields);
12459 this.onMetaChange(this.meta, this.recordType, o);
12461 return this.readRecords(o);
12464 // private function a store will implement
12465 onMetaChange : function(meta, recordType, o){
12472 simpleAccess: function(obj, subsc) {
12479 getJsonAccessor: function(){
12481 return function(expr) {
12483 return(re.test(expr))
12484 ? new Function("obj", "return obj." + expr)
12489 return Roo.emptyFn;
12494 * Create a data block containing Roo.data.Records from an XML document.
12495 * @param {Object} o An object which contains an Array of row objects in the property specified
12496 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12497 * which contains the total size of the dataset.
12498 * @return {Object} data A data block which is used by an Roo.data.Store object as
12499 * a cache of Roo.data.Records.
12501 readRecords : function(o){
12503 * After any data loads, the raw JSON data is available for further custom processing.
12507 var s = this.meta, Record = this.recordType,
12508 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12510 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12512 if(s.totalProperty) {
12513 this.getTotal = this.getJsonAccessor(s.totalProperty);
12515 if(s.successProperty) {
12516 this.getSuccess = this.getJsonAccessor(s.successProperty);
12518 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12520 var g = this.getJsonAccessor(s.id);
12521 this.getId = function(rec) {
12523 return (r === undefined || r === "") ? null : r;
12526 this.getId = function(){return null;};
12529 for(var jj = 0; jj < fl; jj++){
12531 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12532 this.ef[jj] = this.getJsonAccessor(map);
12536 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12537 if(s.totalProperty){
12538 var vt = parseInt(this.getTotal(o), 10);
12543 if(s.successProperty){
12544 var vs = this.getSuccess(o);
12545 if(vs === false || vs === 'false'){
12550 for(var i = 0; i < c; i++){
12553 var id = this.getId(n);
12554 for(var j = 0; j < fl; j++){
12556 var v = this.ef[j](n);
12558 Roo.log('missing convert for ' + f.name);
12562 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12564 var record = new Record(values, id);
12566 records[i] = record;
12572 totalRecords : totalRecords
12577 * Ext JS Library 1.1.1
12578 * Copyright(c) 2006-2007, Ext JS, LLC.
12580 * Originally Released Under LGPL - original licence link has changed is not relivant.
12583 * <script type="text/javascript">
12587 * @class Roo.data.ArrayReader
12588 * @extends Roo.data.DataReader
12589 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12590 * Each element of that Array represents a row of data fields. The
12591 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12592 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12596 var RecordDef = Roo.data.Record.create([
12597 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12598 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12600 var myReader = new Roo.data.ArrayReader({
12601 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12605 * This would consume an Array like this:
12607 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12609 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12611 * Create a new JsonReader
12612 * @param {Object} meta Metadata configuration options.
12613 * @param {Object} recordType Either an Array of field definition objects
12614 * as specified to {@link Roo.data.Record#create},
12615 * or an {@link Roo.data.Record} object
12616 * created using {@link Roo.data.Record#create}.
12618 Roo.data.ArrayReader = function(meta, recordType){
12619 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12622 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12624 * Create a data block containing Roo.data.Records from an XML document.
12625 * @param {Object} o An Array of row objects which represents the dataset.
12626 * @return {Object} data A data block which is used by an Roo.data.Store object as
12627 * a cache of Roo.data.Records.
12629 readRecords : function(o){
12630 var sid = this.meta ? this.meta.id : null;
12631 var recordType = this.recordType, fields = recordType.prototype.fields;
12634 for(var i = 0; i < root.length; i++){
12637 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12638 for(var j = 0, jlen = fields.length; j < jlen; j++){
12639 var f = fields.items[j];
12640 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12641 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12643 values[f.name] = v;
12645 var record = new recordType(values, id);
12647 records[records.length] = record;
12651 totalRecords : records.length
12660 * @class Roo.bootstrap.ComboBox
12661 * @extends Roo.bootstrap.TriggerField
12662 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12663 * @cfg {Boolean} append (true|false) default false
12664 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12665 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12666 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12667 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12668 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12669 * @cfg {Boolean} animate default true
12670 * @cfg {Boolean} emptyResultText only for touch device
12671 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12672 * @cfg {String} emptyTitle default ''
12674 * Create a new ComboBox.
12675 * @param {Object} config Configuration options
12677 Roo.bootstrap.ComboBox = function(config){
12678 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12682 * Fires when the dropdown list is expanded
12683 * @param {Roo.bootstrap.ComboBox} combo This combo box
12688 * Fires when the dropdown list is collapsed
12689 * @param {Roo.bootstrap.ComboBox} combo This combo box
12693 * @event beforeselect
12694 * Fires before a list item is selected. Return false to cancel the selection.
12695 * @param {Roo.bootstrap.ComboBox} combo This combo box
12696 * @param {Roo.data.Record} record The data record returned from the underlying store
12697 * @param {Number} index The index of the selected item in the dropdown list
12699 'beforeselect' : true,
12702 * Fires when a list item is selected
12703 * @param {Roo.bootstrap.ComboBox} combo This combo box
12704 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12705 * @param {Number} index The index of the selected item in the dropdown list
12709 * @event beforequery
12710 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12711 * The event object passed has these properties:
12712 * @param {Roo.bootstrap.ComboBox} combo This combo box
12713 * @param {String} query The query
12714 * @param {Boolean} forceAll true to force "all" query
12715 * @param {Boolean} cancel true to cancel the query
12716 * @param {Object} e The query event object
12718 'beforequery': true,
12721 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12722 * @param {Roo.bootstrap.ComboBox} combo This combo box
12727 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12728 * @param {Roo.bootstrap.ComboBox} combo This combo box
12729 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12734 * Fires when the remove value from the combobox array
12735 * @param {Roo.bootstrap.ComboBox} combo This combo box
12739 * @event afterremove
12740 * Fires when the remove value from the combobox array
12741 * @param {Roo.bootstrap.ComboBox} combo This combo box
12743 'afterremove' : true,
12745 * @event specialfilter
12746 * Fires when specialfilter
12747 * @param {Roo.bootstrap.ComboBox} combo This combo box
12749 'specialfilter' : true,
12752 * Fires when tick the element
12753 * @param {Roo.bootstrap.ComboBox} combo This combo box
12757 * @event touchviewdisplay
12758 * Fires when touch view require special display (default is using displayField)
12759 * @param {Roo.bootstrap.ComboBox} combo This combo box
12760 * @param {Object} cfg set html .
12762 'touchviewdisplay' : true
12767 this.tickItems = [];
12769 this.selectedIndex = -1;
12770 if(this.mode == 'local'){
12771 if(config.queryDelay === undefined){
12772 this.queryDelay = 10;
12774 if(config.minChars === undefined){
12780 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12783 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12784 * rendering into an Roo.Editor, defaults to false)
12787 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12788 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12791 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12794 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12795 * the dropdown list (defaults to undefined, with no header element)
12799 * @cfg {String/Roo.Template} tpl The template to use to render the output
12803 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12805 listWidth: undefined,
12807 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12808 * mode = 'remote' or 'text' if mode = 'local')
12810 displayField: undefined,
12813 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12814 * mode = 'remote' or 'value' if mode = 'local').
12815 * Note: use of a valueField requires the user make a selection
12816 * in order for a value to be mapped.
12818 valueField: undefined,
12820 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12825 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12826 * field's data value (defaults to the underlying DOM element's name)
12828 hiddenName: undefined,
12830 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12834 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12836 selectedClass: 'active',
12839 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12843 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12844 * anchor positions (defaults to 'tl-bl')
12846 listAlign: 'tl-bl?',
12848 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12852 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12853 * query specified by the allQuery config option (defaults to 'query')
12855 triggerAction: 'query',
12857 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12858 * (defaults to 4, does not apply if editable = false)
12862 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12863 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12867 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12868 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12872 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12873 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12877 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12878 * when editable = true (defaults to false)
12880 selectOnFocus:false,
12882 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12884 queryParam: 'query',
12886 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12887 * when mode = 'remote' (defaults to 'Loading...')
12889 loadingText: 'Loading...',
12891 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12895 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12899 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12900 * traditional select (defaults to true)
12904 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12908 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12912 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12913 * listWidth has a higher value)
12917 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12918 * allow the user to set arbitrary text into the field (defaults to false)
12920 forceSelection:false,
12922 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12923 * if typeAhead = true (defaults to 250)
12925 typeAheadDelay : 250,
12927 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12928 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12930 valueNotFoundText : undefined,
12932 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12934 blockFocus : false,
12937 * @cfg {Boolean} disableClear Disable showing of clear button.
12939 disableClear : false,
12941 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12943 alwaysQuery : false,
12946 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12951 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12953 invalidClass : "has-warning",
12956 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12958 validClass : "has-success",
12961 * @cfg {Boolean} specialFilter (true|false) special filter default false
12963 specialFilter : false,
12966 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12968 mobileTouchView : true,
12971 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12973 useNativeIOS : false,
12975 ios_options : false,
12987 btnPosition : 'right',
12988 triggerList : true,
12989 showToggleBtn : true,
12991 emptyResultText: 'Empty',
12992 triggerText : 'Select',
12995 // element that contains real text value.. (when hidden is used..)
12997 getAutoCreate : function()
13002 * Render classic select for iso
13005 if(Roo.isIOS && this.useNativeIOS){
13006 cfg = this.getAutoCreateNativeIOS();
13014 if(Roo.isTouch && this.mobileTouchView){
13015 cfg = this.getAutoCreateTouchView();
13022 if(!this.tickable){
13023 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13028 * ComboBox with tickable selections
13031 var align = this.labelAlign || this.parentLabelAlign();
13034 cls : 'form-group roo-combobox-tickable' //input-group
13037 var btn_text_select = '';
13038 var btn_text_done = '';
13039 var btn_text_cancel = '';
13041 if (this.btn_text_show) {
13042 btn_text_select = 'Select';
13043 btn_text_done = 'Done';
13044 btn_text_cancel = 'Cancel';
13049 cls : 'tickable-buttons',
13054 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13055 //html : this.triggerText
13056 html: btn_text_select
13062 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13064 html: btn_text_done
13070 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13072 html: btn_text_cancel
13078 buttons.cn.unshift({
13080 cls: 'roo-select2-search-field-input'
13086 Roo.each(buttons.cn, function(c){
13088 c.cls += ' btn-' + _this.size;
13091 if (_this.disabled) {
13102 cls: 'form-hidden-field'
13106 cls: 'roo-select2-choices',
13110 cls: 'roo-select2-search-field',
13121 cls: 'roo-select2-container input-group roo-select2-container-multi',
13126 // cls: 'typeahead typeahead-long dropdown-menu',
13127 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13132 if(this.hasFeedback && !this.allowBlank){
13136 cls: 'glyphicon form-control-feedback'
13139 combobox.cn.push(feedback);
13143 if (align ==='left' && this.fieldLabel.length) {
13145 cfg.cls += ' roo-form-group-label-left';
13150 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13151 tooltip : 'This field is required'
13156 cls : 'control-label',
13157 html : this.fieldLabel
13169 var labelCfg = cfg.cn[1];
13170 var contentCfg = cfg.cn[2];
13173 if(this.indicatorpos == 'right'){
13179 cls : 'control-label',
13183 html : this.fieldLabel
13187 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13188 tooltip : 'This field is required'
13203 labelCfg = cfg.cn[0];
13204 contentCfg = cfg.cn[1];
13208 if(this.labelWidth > 12){
13209 labelCfg.style = "width: " + this.labelWidth + 'px';
13212 if(this.labelWidth < 13 && this.labelmd == 0){
13213 this.labelmd = this.labelWidth;
13216 if(this.labellg > 0){
13217 labelCfg.cls += ' col-lg-' + this.labellg;
13218 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13221 if(this.labelmd > 0){
13222 labelCfg.cls += ' col-md-' + this.labelmd;
13223 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13226 if(this.labelsm > 0){
13227 labelCfg.cls += ' col-sm-' + this.labelsm;
13228 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13231 if(this.labelxs > 0){
13232 labelCfg.cls += ' col-xs-' + this.labelxs;
13233 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13237 } else if ( this.fieldLabel.length) {
13238 // Roo.log(" label");
13242 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13243 tooltip : 'This field is required'
13247 //cls : 'input-group-addon',
13248 html : this.fieldLabel
13253 if(this.indicatorpos == 'right'){
13257 //cls : 'input-group-addon',
13258 html : this.fieldLabel
13262 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13263 tooltip : 'This field is required'
13272 // Roo.log(" no label && no align");
13279 ['xs','sm','md','lg'].map(function(size){
13280 if (settings[size]) {
13281 cfg.cls += ' col-' + size + '-' + settings[size];
13289 _initEventsCalled : false,
13292 initEvents: function()
13294 if (this._initEventsCalled) { // as we call render... prevent looping...
13297 this._initEventsCalled = true;
13300 throw "can not find store for combo";
13303 this.indicator = this.indicatorEl();
13305 this.store = Roo.factory(this.store, Roo.data);
13306 this.store.parent = this;
13308 // if we are building from html. then this element is so complex, that we can not really
13309 // use the rendered HTML.
13310 // so we have to trash and replace the previous code.
13311 if (Roo.XComponent.build_from_html) {
13312 // remove this element....
13313 var e = this.el.dom, k=0;
13314 while (e ) { e = e.previousSibling; ++k;}
13319 this.rendered = false;
13321 this.render(this.parent().getChildContainer(true), k);
13324 if(Roo.isIOS && this.useNativeIOS){
13325 this.initIOSView();
13333 if(Roo.isTouch && this.mobileTouchView){
13334 this.initTouchView();
13339 this.initTickableEvents();
13343 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13345 if(this.hiddenName){
13347 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13349 this.hiddenField.dom.value =
13350 this.hiddenValue !== undefined ? this.hiddenValue :
13351 this.value !== undefined ? this.value : '';
13353 // prevent input submission
13354 this.el.dom.removeAttribute('name');
13355 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13360 // this.el.dom.setAttribute('autocomplete', 'off');
13363 var cls = 'x-combo-list';
13365 //this.list = new Roo.Layer({
13366 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13372 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13373 _this.list.setWidth(lw);
13376 this.list.on('mouseover', this.onViewOver, this);
13377 this.list.on('mousemove', this.onViewMove, this);
13378 this.list.on('scroll', this.onViewScroll, this);
13381 this.list.swallowEvent('mousewheel');
13382 this.assetHeight = 0;
13385 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13386 this.assetHeight += this.header.getHeight();
13389 this.innerList = this.list.createChild({cls:cls+'-inner'});
13390 this.innerList.on('mouseover', this.onViewOver, this);
13391 this.innerList.on('mousemove', this.onViewMove, this);
13392 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13394 if(this.allowBlank && !this.pageSize && !this.disableClear){
13395 this.footer = this.list.createChild({cls:cls+'-ft'});
13396 this.pageTb = new Roo.Toolbar(this.footer);
13400 this.footer = this.list.createChild({cls:cls+'-ft'});
13401 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13402 {pageSize: this.pageSize});
13406 if (this.pageTb && this.allowBlank && !this.disableClear) {
13408 this.pageTb.add(new Roo.Toolbar.Fill(), {
13409 cls: 'x-btn-icon x-btn-clear',
13411 handler: function()
13414 _this.clearValue();
13415 _this.onSelect(false, -1);
13420 this.assetHeight += this.footer.getHeight();
13425 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13428 this.view = new Roo.View(this.list, this.tpl, {
13429 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13431 //this.view.wrapEl.setDisplayed(false);
13432 this.view.on('click', this.onViewClick, this);
13435 this.store.on('beforeload', this.onBeforeLoad, this);
13436 this.store.on('load', this.onLoad, this);
13437 this.store.on('loadexception', this.onLoadException, this);
13439 if(this.resizable){
13440 this.resizer = new Roo.Resizable(this.list, {
13441 pinned:true, handles:'se'
13443 this.resizer.on('resize', function(r, w, h){
13444 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13445 this.listWidth = w;
13446 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13447 this.restrictHeight();
13449 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13452 if(!this.editable){
13453 this.editable = true;
13454 this.setEditable(false);
13459 if (typeof(this.events.add.listeners) != 'undefined') {
13461 this.addicon = this.wrap.createChild(
13462 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13464 this.addicon.on('click', function(e) {
13465 this.fireEvent('add', this);
13468 if (typeof(this.events.edit.listeners) != 'undefined') {
13470 this.editicon = this.wrap.createChild(
13471 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13472 if (this.addicon) {
13473 this.editicon.setStyle('margin-left', '40px');
13475 this.editicon.on('click', function(e) {
13477 // we fire even if inothing is selected..
13478 this.fireEvent('edit', this, this.lastData );
13484 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13485 "up" : function(e){
13486 this.inKeyMode = true;
13490 "down" : function(e){
13491 if(!this.isExpanded()){
13492 this.onTriggerClick();
13494 this.inKeyMode = true;
13499 "enter" : function(e){
13500 // this.onViewClick();
13504 if(this.fireEvent("specialkey", this, e)){
13505 this.onViewClick(false);
13511 "esc" : function(e){
13515 "tab" : function(e){
13518 if(this.fireEvent("specialkey", this, e)){
13519 this.onViewClick(false);
13527 doRelay : function(foo, bar, hname){
13528 if(hname == 'down' || this.scope.isExpanded()){
13529 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13538 this.queryDelay = Math.max(this.queryDelay || 10,
13539 this.mode == 'local' ? 10 : 250);
13542 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13544 if(this.typeAhead){
13545 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13547 if(this.editable !== false){
13548 this.inputEl().on("keyup", this.onKeyUp, this);
13550 if(this.forceSelection){
13551 this.inputEl().on('blur', this.doForce, this);
13555 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13556 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13560 initTickableEvents: function()
13564 if(this.hiddenName){
13566 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13568 this.hiddenField.dom.value =
13569 this.hiddenValue !== undefined ? this.hiddenValue :
13570 this.value !== undefined ? this.value : '';
13572 // prevent input submission
13573 this.el.dom.removeAttribute('name');
13574 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13579 // this.list = this.el.select('ul.dropdown-menu',true).first();
13581 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13582 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13583 if(this.triggerList){
13584 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13587 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13588 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13590 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13591 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13593 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13594 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13596 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13597 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13598 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13601 this.cancelBtn.hide();
13606 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13607 _this.list.setWidth(lw);
13610 this.list.on('mouseover', this.onViewOver, this);
13611 this.list.on('mousemove', this.onViewMove, this);
13613 this.list.on('scroll', this.onViewScroll, this);
13616 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13617 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13620 this.view = new Roo.View(this.list, this.tpl, {
13625 selectedClass: this.selectedClass
13628 //this.view.wrapEl.setDisplayed(false);
13629 this.view.on('click', this.onViewClick, this);
13633 this.store.on('beforeload', this.onBeforeLoad, this);
13634 this.store.on('load', this.onLoad, this);
13635 this.store.on('loadexception', this.onLoadException, this);
13638 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13639 "up" : function(e){
13640 this.inKeyMode = true;
13644 "down" : function(e){
13645 this.inKeyMode = true;
13649 "enter" : function(e){
13650 if(this.fireEvent("specialkey", this, e)){
13651 this.onViewClick(false);
13657 "esc" : function(e){
13658 this.onTickableFooterButtonClick(e, false, false);
13661 "tab" : function(e){
13662 this.fireEvent("specialkey", this, e);
13664 this.onTickableFooterButtonClick(e, false, false);
13671 doRelay : function(e, fn, key){
13672 if(this.scope.isExpanded()){
13673 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13682 this.queryDelay = Math.max(this.queryDelay || 10,
13683 this.mode == 'local' ? 10 : 250);
13686 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13688 if(this.typeAhead){
13689 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13692 if(this.editable !== false){
13693 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13696 this.indicator = this.indicatorEl();
13698 if(this.indicator){
13699 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13700 this.indicator.hide();
13705 onDestroy : function(){
13707 this.view.setStore(null);
13708 this.view.el.removeAllListeners();
13709 this.view.el.remove();
13710 this.view.purgeListeners();
13713 this.list.dom.innerHTML = '';
13717 this.store.un('beforeload', this.onBeforeLoad, this);
13718 this.store.un('load', this.onLoad, this);
13719 this.store.un('loadexception', this.onLoadException, this);
13721 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13725 fireKey : function(e){
13726 if(e.isNavKeyPress() && !this.list.isVisible()){
13727 this.fireEvent("specialkey", this, e);
13732 onResize: function(w, h){
13733 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13735 // if(typeof w != 'number'){
13736 // // we do not handle it!?!?
13739 // var tw = this.trigger.getWidth();
13740 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13741 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13743 // this.inputEl().setWidth( this.adjustWidth('input', x));
13745 // //this.trigger.setStyle('left', x+'px');
13747 // if(this.list && this.listWidth === undefined){
13748 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13749 // this.list.setWidth(lw);
13750 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13758 * Allow or prevent the user from directly editing the field text. If false is passed,
13759 * the user will only be able to select from the items defined in the dropdown list. This method
13760 * is the runtime equivalent of setting the 'editable' config option at config time.
13761 * @param {Boolean} value True to allow the user to directly edit the field text
13763 setEditable : function(value){
13764 if(value == this.editable){
13767 this.editable = value;
13769 this.inputEl().dom.setAttribute('readOnly', true);
13770 this.inputEl().on('mousedown', this.onTriggerClick, this);
13771 this.inputEl().addClass('x-combo-noedit');
13773 this.inputEl().dom.setAttribute('readOnly', false);
13774 this.inputEl().un('mousedown', this.onTriggerClick, this);
13775 this.inputEl().removeClass('x-combo-noedit');
13781 onBeforeLoad : function(combo,opts){
13782 if(!this.hasFocus){
13786 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13788 this.restrictHeight();
13789 this.selectedIndex = -1;
13793 onLoad : function(){
13795 this.hasQuery = false;
13797 if(!this.hasFocus){
13801 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13802 this.loading.hide();
13805 if(this.store.getCount() > 0){
13808 this.restrictHeight();
13809 if(this.lastQuery == this.allQuery){
13810 if(this.editable && !this.tickable){
13811 this.inputEl().dom.select();
13815 !this.selectByValue(this.value, true) &&
13818 !this.store.lastOptions ||
13819 typeof(this.store.lastOptions.add) == 'undefined' ||
13820 this.store.lastOptions.add != true
13823 this.select(0, true);
13826 if(this.autoFocus){
13829 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13830 this.taTask.delay(this.typeAheadDelay);
13834 this.onEmptyResults();
13840 onLoadException : function()
13842 this.hasQuery = false;
13844 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13845 this.loading.hide();
13848 if(this.tickable && this.editable){
13853 // only causes errors at present
13854 //Roo.log(this.store.reader.jsonData);
13855 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13857 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13863 onTypeAhead : function(){
13864 if(this.store.getCount() > 0){
13865 var r = this.store.getAt(0);
13866 var newValue = r.data[this.displayField];
13867 var len = newValue.length;
13868 var selStart = this.getRawValue().length;
13870 if(selStart != len){
13871 this.setRawValue(newValue);
13872 this.selectText(selStart, newValue.length);
13878 onSelect : function(record, index){
13880 if(this.fireEvent('beforeselect', this, record, index) !== false){
13882 this.setFromData(index > -1 ? record.data : false);
13885 this.fireEvent('select', this, record, index);
13890 * Returns the currently selected field value or empty string if no value is set.
13891 * @return {String} value The selected value
13893 getValue : function()
13895 if(Roo.isIOS && this.useNativeIOS){
13896 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13900 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13903 if(this.valueField){
13904 return typeof this.value != 'undefined' ? this.value : '';
13906 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13910 getRawValue : function()
13912 if(Roo.isIOS && this.useNativeIOS){
13913 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13916 var v = this.inputEl().getValue();
13922 * Clears any text/value currently set in the field
13924 clearValue : function(){
13926 if(this.hiddenField){
13927 this.hiddenField.dom.value = '';
13930 this.setRawValue('');
13931 this.lastSelectionText = '';
13932 this.lastData = false;
13934 var close = this.closeTriggerEl();
13945 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13946 * will be displayed in the field. If the value does not match the data value of an existing item,
13947 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13948 * Otherwise the field will be blank (although the value will still be set).
13949 * @param {String} value The value to match
13951 setValue : function(v)
13953 if(Roo.isIOS && this.useNativeIOS){
13954 this.setIOSValue(v);
13964 if(this.valueField){
13965 var r = this.findRecord(this.valueField, v);
13967 text = r.data[this.displayField];
13968 }else if(this.valueNotFoundText !== undefined){
13969 text = this.valueNotFoundText;
13972 this.lastSelectionText = text;
13973 if(this.hiddenField){
13974 this.hiddenField.dom.value = v;
13976 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13979 var close = this.closeTriggerEl();
13982 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13988 * @property {Object} the last set data for the element
13993 * Sets the value of the field based on a object which is related to the record format for the store.
13994 * @param {Object} value the value to set as. or false on reset?
13996 setFromData : function(o){
14003 var dv = ''; // display value
14004 var vv = ''; // value value..
14006 if (this.displayField) {
14007 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14009 // this is an error condition!!!
14010 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14013 if(this.valueField){
14014 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14017 var close = this.closeTriggerEl();
14020 if(dv.length || vv * 1 > 0){
14022 this.blockFocus=true;
14028 if(this.hiddenField){
14029 this.hiddenField.dom.value = vv;
14031 this.lastSelectionText = dv;
14032 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14036 // no hidden field.. - we store the value in 'value', but still display
14037 // display field!!!!
14038 this.lastSelectionText = dv;
14039 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14046 reset : function(){
14047 // overridden so that last data is reset..
14054 this.setValue(this.originalValue);
14055 //this.clearInvalid();
14056 this.lastData = false;
14058 this.view.clearSelections();
14064 findRecord : function(prop, value){
14066 if(this.store.getCount() > 0){
14067 this.store.each(function(r){
14068 if(r.data[prop] == value){
14078 getName: function()
14080 // returns hidden if it's set..
14081 if (!this.rendered) {return ''};
14082 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14086 onViewMove : function(e, t){
14087 this.inKeyMode = false;
14091 onViewOver : function(e, t){
14092 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14095 var item = this.view.findItemFromChild(t);
14098 var index = this.view.indexOf(item);
14099 this.select(index, false);
14104 onViewClick : function(view, doFocus, el, e)
14106 var index = this.view.getSelectedIndexes()[0];
14108 var r = this.store.getAt(index);
14112 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14119 Roo.each(this.tickItems, function(v,k){
14121 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14123 _this.tickItems.splice(k, 1);
14125 if(typeof(e) == 'undefined' && view == false){
14126 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14138 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14139 this.tickItems.push(r.data);
14142 if(typeof(e) == 'undefined' && view == false){
14143 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14150 this.onSelect(r, index);
14152 if(doFocus !== false && !this.blockFocus){
14153 this.inputEl().focus();
14158 restrictHeight : function(){
14159 //this.innerList.dom.style.height = '';
14160 //var inner = this.innerList.dom;
14161 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14162 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14163 //this.list.beginUpdate();
14164 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14165 this.list.alignTo(this.inputEl(), this.listAlign);
14166 this.list.alignTo(this.inputEl(), this.listAlign);
14167 //this.list.endUpdate();
14171 onEmptyResults : function(){
14173 if(this.tickable && this.editable){
14174 this.hasFocus = false;
14175 this.restrictHeight();
14183 * Returns true if the dropdown list is expanded, else false.
14185 isExpanded : function(){
14186 return this.list.isVisible();
14190 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14191 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14192 * @param {String} value The data value of the item to select
14193 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14194 * selected item if it is not currently in view (defaults to true)
14195 * @return {Boolean} True if the value matched an item in the list, else false
14197 selectByValue : function(v, scrollIntoView){
14198 if(v !== undefined && v !== null){
14199 var r = this.findRecord(this.valueField || this.displayField, v);
14201 this.select(this.store.indexOf(r), scrollIntoView);
14209 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14210 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14211 * @param {Number} index The zero-based index of the list item to select
14212 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14213 * selected item if it is not currently in view (defaults to true)
14215 select : function(index, scrollIntoView){
14216 this.selectedIndex = index;
14217 this.view.select(index);
14218 if(scrollIntoView !== false){
14219 var el = this.view.getNode(index);
14221 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14224 this.list.scrollChildIntoView(el, false);
14230 selectNext : function(){
14231 var ct = this.store.getCount();
14233 if(this.selectedIndex == -1){
14235 }else if(this.selectedIndex < ct-1){
14236 this.select(this.selectedIndex+1);
14242 selectPrev : function(){
14243 var ct = this.store.getCount();
14245 if(this.selectedIndex == -1){
14247 }else if(this.selectedIndex != 0){
14248 this.select(this.selectedIndex-1);
14254 onKeyUp : function(e){
14255 if(this.editable !== false && !e.isSpecialKey()){
14256 this.lastKey = e.getKey();
14257 this.dqTask.delay(this.queryDelay);
14262 validateBlur : function(){
14263 return !this.list || !this.list.isVisible();
14267 initQuery : function(){
14269 var v = this.getRawValue();
14271 if(this.tickable && this.editable){
14272 v = this.tickableInputEl().getValue();
14279 doForce : function(){
14280 if(this.inputEl().dom.value.length > 0){
14281 this.inputEl().dom.value =
14282 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14288 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14289 * query allowing the query action to be canceled if needed.
14290 * @param {String} query The SQL query to execute
14291 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14292 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14293 * saved in the current store (defaults to false)
14295 doQuery : function(q, forceAll){
14297 if(q === undefined || q === null){
14302 forceAll: forceAll,
14306 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14311 forceAll = qe.forceAll;
14312 if(forceAll === true || (q.length >= this.minChars)){
14314 this.hasQuery = true;
14316 if(this.lastQuery != q || this.alwaysQuery){
14317 this.lastQuery = q;
14318 if(this.mode == 'local'){
14319 this.selectedIndex = -1;
14321 this.store.clearFilter();
14324 if(this.specialFilter){
14325 this.fireEvent('specialfilter', this);
14330 this.store.filter(this.displayField, q);
14333 this.store.fireEvent("datachanged", this.store);
14340 this.store.baseParams[this.queryParam] = q;
14342 var options = {params : this.getParams(q)};
14345 options.add = true;
14346 options.params.start = this.page * this.pageSize;
14349 this.store.load(options);
14352 * this code will make the page width larger, at the beginning, the list not align correctly,
14353 * we should expand the list on onLoad
14354 * so command out it
14359 this.selectedIndex = -1;
14364 this.loadNext = false;
14368 getParams : function(q){
14370 //p[this.queryParam] = q;
14374 p.limit = this.pageSize;
14380 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14382 collapse : function(){
14383 if(!this.isExpanded()){
14389 this.hasFocus = false;
14393 this.cancelBtn.hide();
14394 this.trigger.show();
14397 this.tickableInputEl().dom.value = '';
14398 this.tickableInputEl().blur();
14403 Roo.get(document).un('mousedown', this.collapseIf, this);
14404 Roo.get(document).un('mousewheel', this.collapseIf, this);
14405 if (!this.editable) {
14406 Roo.get(document).un('keydown', this.listKeyPress, this);
14408 this.fireEvent('collapse', this);
14414 collapseIf : function(e){
14415 var in_combo = e.within(this.el);
14416 var in_list = e.within(this.list);
14417 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14419 if (in_combo || in_list || is_list) {
14420 //e.stopPropagation();
14425 this.onTickableFooterButtonClick(e, false, false);
14433 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14435 expand : function(){
14437 if(this.isExpanded() || !this.hasFocus){
14441 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14442 this.list.setWidth(lw);
14448 this.restrictHeight();
14452 this.tickItems = Roo.apply([], this.item);
14455 this.cancelBtn.show();
14456 this.trigger.hide();
14459 this.tickableInputEl().focus();
14464 Roo.get(document).on('mousedown', this.collapseIf, this);
14465 Roo.get(document).on('mousewheel', this.collapseIf, this);
14466 if (!this.editable) {
14467 Roo.get(document).on('keydown', this.listKeyPress, this);
14470 this.fireEvent('expand', this);
14474 // Implements the default empty TriggerField.onTriggerClick function
14475 onTriggerClick : function(e)
14477 Roo.log('trigger click');
14479 if(this.disabled || !this.triggerList){
14484 this.loadNext = false;
14486 if(this.isExpanded()){
14488 if (!this.blockFocus) {
14489 this.inputEl().focus();
14493 this.hasFocus = true;
14494 if(this.triggerAction == 'all') {
14495 this.doQuery(this.allQuery, true);
14497 this.doQuery(this.getRawValue());
14499 if (!this.blockFocus) {
14500 this.inputEl().focus();
14505 onTickableTriggerClick : function(e)
14512 this.loadNext = false;
14513 this.hasFocus = true;
14515 if(this.triggerAction == 'all') {
14516 this.doQuery(this.allQuery, true);
14518 this.doQuery(this.getRawValue());
14522 onSearchFieldClick : function(e)
14524 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14525 this.onTickableFooterButtonClick(e, false, false);
14529 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14534 this.loadNext = false;
14535 this.hasFocus = true;
14537 if(this.triggerAction == 'all') {
14538 this.doQuery(this.allQuery, true);
14540 this.doQuery(this.getRawValue());
14544 listKeyPress : function(e)
14546 //Roo.log('listkeypress');
14547 // scroll to first matching element based on key pres..
14548 if (e.isSpecialKey()) {
14551 var k = String.fromCharCode(e.getKey()).toUpperCase();
14554 var csel = this.view.getSelectedNodes();
14555 var cselitem = false;
14557 var ix = this.view.indexOf(csel[0]);
14558 cselitem = this.store.getAt(ix);
14559 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14565 this.store.each(function(v) {
14567 // start at existing selection.
14568 if (cselitem.id == v.id) {
14574 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14575 match = this.store.indexOf(v);
14581 if (match === false) {
14582 return true; // no more action?
14585 this.view.select(match);
14586 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14587 sn.scrollIntoView(sn.dom.parentNode, false);
14590 onViewScroll : function(e, t){
14592 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){
14596 this.hasQuery = true;
14598 this.loading = this.list.select('.loading', true).first();
14600 if(this.loading === null){
14601 this.list.createChild({
14603 cls: 'loading roo-select2-more-results roo-select2-active',
14604 html: 'Loading more results...'
14607 this.loading = this.list.select('.loading', true).first();
14609 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14611 this.loading.hide();
14614 this.loading.show();
14619 this.loadNext = true;
14621 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14626 addItem : function(o)
14628 var dv = ''; // display value
14630 if (this.displayField) {
14631 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14633 // this is an error condition!!!
14634 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14641 var choice = this.choices.createChild({
14643 cls: 'roo-select2-search-choice',
14652 cls: 'roo-select2-search-choice-close fa fa-times',
14657 }, this.searchField);
14659 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14661 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14669 this.inputEl().dom.value = '';
14674 onRemoveItem : function(e, _self, o)
14676 e.preventDefault();
14678 this.lastItem = Roo.apply([], this.item);
14680 var index = this.item.indexOf(o.data) * 1;
14683 Roo.log('not this item?!');
14687 this.item.splice(index, 1);
14692 this.fireEvent('remove', this, e);
14698 syncValue : function()
14700 if(!this.item.length){
14707 Roo.each(this.item, function(i){
14708 if(_this.valueField){
14709 value.push(i[_this.valueField]);
14716 this.value = value.join(',');
14718 if(this.hiddenField){
14719 this.hiddenField.dom.value = this.value;
14722 this.store.fireEvent("datachanged", this.store);
14727 clearItem : function()
14729 if(!this.multiple){
14735 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14743 if(this.tickable && !Roo.isTouch){
14744 this.view.refresh();
14748 inputEl: function ()
14750 if(Roo.isIOS && this.useNativeIOS){
14751 return this.el.select('select.roo-ios-select', true).first();
14754 if(Roo.isTouch && this.mobileTouchView){
14755 return this.el.select('input.form-control',true).first();
14759 return this.searchField;
14762 return this.el.select('input.form-control',true).first();
14765 onTickableFooterButtonClick : function(e, btn, el)
14767 e.preventDefault();
14769 this.lastItem = Roo.apply([], this.item);
14771 if(btn && btn.name == 'cancel'){
14772 this.tickItems = Roo.apply([], this.item);
14781 Roo.each(this.tickItems, function(o){
14789 validate : function()
14791 if(this.getVisibilityEl().hasClass('hidden')){
14795 var v = this.getRawValue();
14798 v = this.getValue();
14801 if(this.disabled || this.allowBlank || v.length){
14806 this.markInvalid();
14810 tickableInputEl : function()
14812 if(!this.tickable || !this.editable){
14813 return this.inputEl();
14816 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14820 getAutoCreateTouchView : function()
14825 cls: 'form-group' //input-group
14831 type : this.inputType,
14832 cls : 'form-control x-combo-noedit',
14833 autocomplete: 'new-password',
14834 placeholder : this.placeholder || '',
14839 input.name = this.name;
14843 input.cls += ' input-' + this.size;
14846 if (this.disabled) {
14847 input.disabled = true;
14858 inputblock.cls += ' input-group';
14860 inputblock.cn.unshift({
14862 cls : 'input-group-addon',
14867 if(this.removable && !this.multiple){
14868 inputblock.cls += ' roo-removable';
14870 inputblock.cn.push({
14873 cls : 'roo-combo-removable-btn close'
14877 if(this.hasFeedback && !this.allowBlank){
14879 inputblock.cls += ' has-feedback';
14881 inputblock.cn.push({
14883 cls: 'glyphicon form-control-feedback'
14890 inputblock.cls += (this.before) ? '' : ' input-group';
14892 inputblock.cn.push({
14894 cls : 'input-group-addon',
14905 cls: 'form-hidden-field'
14919 cls: 'form-hidden-field'
14923 cls: 'roo-select2-choices',
14927 cls: 'roo-select2-search-field',
14940 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14946 if(!this.multiple && this.showToggleBtn){
14953 if (this.caret != false) {
14956 cls: 'fa fa-' + this.caret
14963 cls : 'input-group-addon btn dropdown-toggle',
14968 cls: 'combobox-clear',
14982 combobox.cls += ' roo-select2-container-multi';
14985 var align = this.labelAlign || this.parentLabelAlign();
14987 if (align ==='left' && this.fieldLabel.length) {
14992 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14993 tooltip : 'This field is required'
14997 cls : 'control-label',
14998 html : this.fieldLabel
15009 var labelCfg = cfg.cn[1];
15010 var contentCfg = cfg.cn[2];
15013 if(this.indicatorpos == 'right'){
15018 cls : 'control-label',
15022 html : this.fieldLabel
15026 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15027 tooltip : 'This field is required'
15040 labelCfg = cfg.cn[0];
15041 contentCfg = cfg.cn[1];
15046 if(this.labelWidth > 12){
15047 labelCfg.style = "width: " + this.labelWidth + 'px';
15050 if(this.labelWidth < 13 && this.labelmd == 0){
15051 this.labelmd = this.labelWidth;
15054 if(this.labellg > 0){
15055 labelCfg.cls += ' col-lg-' + this.labellg;
15056 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15059 if(this.labelmd > 0){
15060 labelCfg.cls += ' col-md-' + this.labelmd;
15061 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15064 if(this.labelsm > 0){
15065 labelCfg.cls += ' col-sm-' + this.labelsm;
15066 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15069 if(this.labelxs > 0){
15070 labelCfg.cls += ' col-xs-' + this.labelxs;
15071 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15075 } else if ( this.fieldLabel.length) {
15079 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15080 tooltip : 'This field is required'
15084 cls : 'control-label',
15085 html : this.fieldLabel
15096 if(this.indicatorpos == 'right'){
15100 cls : 'control-label',
15101 html : this.fieldLabel,
15105 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15106 tooltip : 'This field is required'
15123 var settings = this;
15125 ['xs','sm','md','lg'].map(function(size){
15126 if (settings[size]) {
15127 cfg.cls += ' col-' + size + '-' + settings[size];
15134 initTouchView : function()
15136 this.renderTouchView();
15138 this.touchViewEl.on('scroll', function(){
15139 this.el.dom.scrollTop = 0;
15142 this.originalValue = this.getValue();
15144 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15146 this.inputEl().on("click", this.showTouchView, this);
15147 if (this.triggerEl) {
15148 this.triggerEl.on("click", this.showTouchView, this);
15152 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15153 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15155 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15157 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15158 this.store.on('load', this.onTouchViewLoad, this);
15159 this.store.on('loadexception', this.onTouchViewLoadException, this);
15161 if(this.hiddenName){
15163 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15165 this.hiddenField.dom.value =
15166 this.hiddenValue !== undefined ? this.hiddenValue :
15167 this.value !== undefined ? this.value : '';
15169 this.el.dom.removeAttribute('name');
15170 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15174 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15175 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15178 if(this.removable && !this.multiple){
15179 var close = this.closeTriggerEl();
15181 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15182 close.on('click', this.removeBtnClick, this, close);
15186 * fix the bug in Safari iOS8
15188 this.inputEl().on("focus", function(e){
15189 document.activeElement.blur();
15197 renderTouchView : function()
15199 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15200 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15202 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15203 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15205 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15206 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15207 this.touchViewBodyEl.setStyle('overflow', 'auto');
15209 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15210 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15212 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15213 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15217 showTouchView : function()
15223 this.touchViewHeaderEl.hide();
15225 if(this.modalTitle.length){
15226 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15227 this.touchViewHeaderEl.show();
15230 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15231 this.touchViewEl.show();
15233 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15235 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15236 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15238 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15240 if(this.modalTitle.length){
15241 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15244 this.touchViewBodyEl.setHeight(bodyHeight);
15248 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15250 this.touchViewEl.addClass('in');
15253 this.doTouchViewQuery();
15257 hideTouchView : function()
15259 this.touchViewEl.removeClass('in');
15263 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15265 this.touchViewEl.setStyle('display', 'none');
15270 setTouchViewValue : function()
15277 Roo.each(this.tickItems, function(o){
15282 this.hideTouchView();
15285 doTouchViewQuery : function()
15294 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15298 if(!this.alwaysQuery || this.mode == 'local'){
15299 this.onTouchViewLoad();
15306 onTouchViewBeforeLoad : function(combo,opts)
15312 onTouchViewLoad : function()
15314 if(this.store.getCount() < 1){
15315 this.onTouchViewEmptyResults();
15319 this.clearTouchView();
15321 var rawValue = this.getRawValue();
15323 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15325 this.tickItems = [];
15327 this.store.data.each(function(d, rowIndex){
15328 var row = this.touchViewListGroup.createChild(template);
15330 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15331 row.addClass(d.data.cls);
15334 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15337 html : d.data[this.displayField]
15340 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15341 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15344 row.removeClass('selected');
15345 if(!this.multiple && this.valueField &&
15346 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15349 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15350 row.addClass('selected');
15353 if(this.multiple && this.valueField &&
15354 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15358 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15359 this.tickItems.push(d.data);
15362 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15366 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15368 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15370 if(this.modalTitle.length){
15371 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15374 var listHeight = this.touchViewListGroup.getHeight();
15378 if(firstChecked && listHeight > bodyHeight){
15379 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15384 onTouchViewLoadException : function()
15386 this.hideTouchView();
15389 onTouchViewEmptyResults : function()
15391 this.clearTouchView();
15393 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15395 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15399 clearTouchView : function()
15401 this.touchViewListGroup.dom.innerHTML = '';
15404 onTouchViewClick : function(e, el, o)
15406 e.preventDefault();
15409 var rowIndex = o.rowIndex;
15411 var r = this.store.getAt(rowIndex);
15413 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15415 if(!this.multiple){
15416 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15417 c.dom.removeAttribute('checked');
15420 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15422 this.setFromData(r.data);
15424 var close = this.closeTriggerEl();
15430 this.hideTouchView();
15432 this.fireEvent('select', this, r, rowIndex);
15437 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15438 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15439 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15443 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15444 this.addItem(r.data);
15445 this.tickItems.push(r.data);
15449 getAutoCreateNativeIOS : function()
15452 cls: 'form-group' //input-group,
15457 cls : 'roo-ios-select'
15461 combobox.name = this.name;
15464 if (this.disabled) {
15465 combobox.disabled = true;
15468 var settings = this;
15470 ['xs','sm','md','lg'].map(function(size){
15471 if (settings[size]) {
15472 cfg.cls += ' col-' + size + '-' + settings[size];
15482 initIOSView : function()
15484 this.store.on('load', this.onIOSViewLoad, this);
15489 onIOSViewLoad : function()
15491 if(this.store.getCount() < 1){
15495 this.clearIOSView();
15497 if(this.allowBlank) {
15499 var default_text = '-- SELECT --';
15501 if(this.placeholder.length){
15502 default_text = this.placeholder;
15505 if(this.emptyTitle.length){
15506 default_text += ' - ' + this.emptyTitle + ' -';
15509 var opt = this.inputEl().createChild({
15512 html : default_text
15516 o[this.valueField] = 0;
15517 o[this.displayField] = default_text;
15519 this.ios_options.push({
15526 this.store.data.each(function(d, rowIndex){
15530 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15531 html = d.data[this.displayField];
15536 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15537 value = d.data[this.valueField];
15546 if(this.value == d.data[this.valueField]){
15547 option['selected'] = true;
15550 var opt = this.inputEl().createChild(option);
15552 this.ios_options.push({
15559 this.inputEl().on('change', function(){
15560 this.fireEvent('select', this);
15565 clearIOSView: function()
15567 this.inputEl().dom.innerHTML = '';
15569 this.ios_options = [];
15572 setIOSValue: function(v)
15576 if(!this.ios_options){
15580 Roo.each(this.ios_options, function(opts){
15582 opts.el.dom.removeAttribute('selected');
15584 if(opts.data[this.valueField] != v){
15588 opts.el.dom.setAttribute('selected', true);
15594 * @cfg {Boolean} grow
15598 * @cfg {Number} growMin
15602 * @cfg {Number} growMax
15611 Roo.apply(Roo.bootstrap.ComboBox, {
15615 cls: 'modal-header',
15637 cls: 'list-group-item',
15641 cls: 'roo-combobox-list-group-item-value'
15645 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15659 listItemCheckbox : {
15661 cls: 'list-group-item',
15665 cls: 'roo-combobox-list-group-item-value'
15669 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15685 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15690 cls: 'modal-footer',
15698 cls: 'col-xs-6 text-left',
15701 cls: 'btn btn-danger roo-touch-view-cancel',
15707 cls: 'col-xs-6 text-right',
15710 cls: 'btn btn-success roo-touch-view-ok',
15721 Roo.apply(Roo.bootstrap.ComboBox, {
15723 touchViewTemplate : {
15725 cls: 'modal fade roo-combobox-touch-view',
15729 cls: 'modal-dialog',
15730 style : 'position:fixed', // we have to fix position....
15734 cls: 'modal-content',
15736 Roo.bootstrap.ComboBox.header,
15737 Roo.bootstrap.ComboBox.body,
15738 Roo.bootstrap.ComboBox.footer
15747 * Ext JS Library 1.1.1
15748 * Copyright(c) 2006-2007, Ext JS, LLC.
15750 * Originally Released Under LGPL - original licence link has changed is not relivant.
15753 * <script type="text/javascript">
15758 * @extends Roo.util.Observable
15759 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15760 * This class also supports single and multi selection modes. <br>
15761 * Create a data model bound view:
15763 var store = new Roo.data.Store(...);
15765 var view = new Roo.View({
15767 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15769 singleSelect: true,
15770 selectedClass: "ydataview-selected",
15774 // listen for node click?
15775 view.on("click", function(vw, index, node, e){
15776 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15780 dataModel.load("foobar.xml");
15782 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15784 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15785 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15787 * Note: old style constructor is still suported (container, template, config)
15790 * Create a new View
15791 * @param {Object} config The config object
15794 Roo.View = function(config, depreciated_tpl, depreciated_config){
15796 this.parent = false;
15798 if (typeof(depreciated_tpl) == 'undefined') {
15799 // new way.. - universal constructor.
15800 Roo.apply(this, config);
15801 this.el = Roo.get(this.el);
15804 this.el = Roo.get(config);
15805 this.tpl = depreciated_tpl;
15806 Roo.apply(this, depreciated_config);
15808 this.wrapEl = this.el.wrap().wrap();
15809 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15812 if(typeof(this.tpl) == "string"){
15813 this.tpl = new Roo.Template(this.tpl);
15815 // support xtype ctors..
15816 this.tpl = new Roo.factory(this.tpl, Roo);
15820 this.tpl.compile();
15825 * @event beforeclick
15826 * Fires before a click is processed. Returns false to cancel the default action.
15827 * @param {Roo.View} this
15828 * @param {Number} index The index of the target node
15829 * @param {HTMLElement} node The target node
15830 * @param {Roo.EventObject} e The raw event object
15832 "beforeclick" : true,
15835 * Fires when a template node is clicked.
15836 * @param {Roo.View} this
15837 * @param {Number} index The index of the target node
15838 * @param {HTMLElement} node The target node
15839 * @param {Roo.EventObject} e The raw event object
15844 * Fires when a template node is double clicked.
15845 * @param {Roo.View} this
15846 * @param {Number} index The index of the target node
15847 * @param {HTMLElement} node The target node
15848 * @param {Roo.EventObject} e The raw event object
15852 * @event contextmenu
15853 * Fires when a template node is right clicked.
15854 * @param {Roo.View} this
15855 * @param {Number} index The index of the target node
15856 * @param {HTMLElement} node The target node
15857 * @param {Roo.EventObject} e The raw event object
15859 "contextmenu" : true,
15861 * @event selectionchange
15862 * Fires when the selected nodes change.
15863 * @param {Roo.View} this
15864 * @param {Array} selections Array of the selected nodes
15866 "selectionchange" : true,
15869 * @event beforeselect
15870 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15871 * @param {Roo.View} this
15872 * @param {HTMLElement} node The node to be selected
15873 * @param {Array} selections Array of currently selected nodes
15875 "beforeselect" : true,
15877 * @event preparedata
15878 * Fires on every row to render, to allow you to change the data.
15879 * @param {Roo.View} this
15880 * @param {Object} data to be rendered (change this)
15882 "preparedata" : true
15890 "click": this.onClick,
15891 "dblclick": this.onDblClick,
15892 "contextmenu": this.onContextMenu,
15896 this.selections = [];
15898 this.cmp = new Roo.CompositeElementLite([]);
15900 this.store = Roo.factory(this.store, Roo.data);
15901 this.setStore(this.store, true);
15904 if ( this.footer && this.footer.xtype) {
15906 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15908 this.footer.dataSource = this.store;
15909 this.footer.container = fctr;
15910 this.footer = Roo.factory(this.footer, Roo);
15911 fctr.insertFirst(this.el);
15913 // this is a bit insane - as the paging toolbar seems to detach the el..
15914 // dom.parentNode.parentNode.parentNode
15915 // they get detached?
15919 Roo.View.superclass.constructor.call(this);
15924 Roo.extend(Roo.View, Roo.util.Observable, {
15927 * @cfg {Roo.data.Store} store Data store to load data from.
15932 * @cfg {String|Roo.Element} el The container element.
15937 * @cfg {String|Roo.Template} tpl The template used by this View
15941 * @cfg {String} dataName the named area of the template to use as the data area
15942 * Works with domtemplates roo-name="name"
15946 * @cfg {String} selectedClass The css class to add to selected nodes
15948 selectedClass : "x-view-selected",
15950 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15955 * @cfg {String} text to display on mask (default Loading)
15959 * @cfg {Boolean} multiSelect Allow multiple selection
15961 multiSelect : false,
15963 * @cfg {Boolean} singleSelect Allow single selection
15965 singleSelect: false,
15968 * @cfg {Boolean} toggleSelect - selecting
15970 toggleSelect : false,
15973 * @cfg {Boolean} tickable - selecting
15978 * Returns the element this view is bound to.
15979 * @return {Roo.Element}
15981 getEl : function(){
15982 return this.wrapEl;
15988 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15990 refresh : function(){
15991 //Roo.log('refresh');
15994 // if we are using something like 'domtemplate', then
15995 // the what gets used is:
15996 // t.applySubtemplate(NAME, data, wrapping data..)
15997 // the outer template then get' applied with
15998 // the store 'extra data'
15999 // and the body get's added to the
16000 // roo-name="data" node?
16001 // <span class='roo-tpl-{name}'></span> ?????
16005 this.clearSelections();
16006 this.el.update("");
16008 var records = this.store.getRange();
16009 if(records.length < 1) {
16011 // is this valid?? = should it render a template??
16013 this.el.update(this.emptyText);
16017 if (this.dataName) {
16018 this.el.update(t.apply(this.store.meta)); //????
16019 el = this.el.child('.roo-tpl-' + this.dataName);
16022 for(var i = 0, len = records.length; i < len; i++){
16023 var data = this.prepareData(records[i].data, i, records[i]);
16024 this.fireEvent("preparedata", this, data, i, records[i]);
16026 var d = Roo.apply({}, data);
16029 Roo.apply(d, {'roo-id' : Roo.id()});
16033 Roo.each(this.parent.item, function(item){
16034 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16037 Roo.apply(d, {'roo-data-checked' : 'checked'});
16041 html[html.length] = Roo.util.Format.trim(
16043 t.applySubtemplate(this.dataName, d, this.store.meta) :
16050 el.update(html.join(""));
16051 this.nodes = el.dom.childNodes;
16052 this.updateIndexes(0);
16057 * Function to override to reformat the data that is sent to
16058 * the template for each node.
16059 * DEPRICATED - use the preparedata event handler.
16060 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16061 * a JSON object for an UpdateManager bound view).
16063 prepareData : function(data, index, record)
16065 this.fireEvent("preparedata", this, data, index, record);
16069 onUpdate : function(ds, record){
16070 // Roo.log('on update');
16071 this.clearSelections();
16072 var index = this.store.indexOf(record);
16073 var n = this.nodes[index];
16074 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16075 n.parentNode.removeChild(n);
16076 this.updateIndexes(index, index);
16082 onAdd : function(ds, records, index)
16084 //Roo.log(['on Add', ds, records, index] );
16085 this.clearSelections();
16086 if(this.nodes.length == 0){
16090 var n = this.nodes[index];
16091 for(var i = 0, len = records.length; i < len; i++){
16092 var d = this.prepareData(records[i].data, i, records[i]);
16094 this.tpl.insertBefore(n, d);
16097 this.tpl.append(this.el, d);
16100 this.updateIndexes(index);
16103 onRemove : function(ds, record, index){
16104 // Roo.log('onRemove');
16105 this.clearSelections();
16106 var el = this.dataName ?
16107 this.el.child('.roo-tpl-' + this.dataName) :
16110 el.dom.removeChild(this.nodes[index]);
16111 this.updateIndexes(index);
16115 * Refresh an individual node.
16116 * @param {Number} index
16118 refreshNode : function(index){
16119 this.onUpdate(this.store, this.store.getAt(index));
16122 updateIndexes : function(startIndex, endIndex){
16123 var ns = this.nodes;
16124 startIndex = startIndex || 0;
16125 endIndex = endIndex || ns.length - 1;
16126 for(var i = startIndex; i <= endIndex; i++){
16127 ns[i].nodeIndex = i;
16132 * Changes the data store this view uses and refresh the view.
16133 * @param {Store} store
16135 setStore : function(store, initial){
16136 if(!initial && this.store){
16137 this.store.un("datachanged", this.refresh);
16138 this.store.un("add", this.onAdd);
16139 this.store.un("remove", this.onRemove);
16140 this.store.un("update", this.onUpdate);
16141 this.store.un("clear", this.refresh);
16142 this.store.un("beforeload", this.onBeforeLoad);
16143 this.store.un("load", this.onLoad);
16144 this.store.un("loadexception", this.onLoad);
16148 store.on("datachanged", this.refresh, this);
16149 store.on("add", this.onAdd, this);
16150 store.on("remove", this.onRemove, this);
16151 store.on("update", this.onUpdate, this);
16152 store.on("clear", this.refresh, this);
16153 store.on("beforeload", this.onBeforeLoad, this);
16154 store.on("load", this.onLoad, this);
16155 store.on("loadexception", this.onLoad, this);
16163 * onbeforeLoad - masks the loading area.
16166 onBeforeLoad : function(store,opts)
16168 //Roo.log('onBeforeLoad');
16170 this.el.update("");
16172 this.el.mask(this.mask ? this.mask : "Loading" );
16174 onLoad : function ()
16181 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16182 * @param {HTMLElement} node
16183 * @return {HTMLElement} The template node
16185 findItemFromChild : function(node){
16186 var el = this.dataName ?
16187 this.el.child('.roo-tpl-' + this.dataName,true) :
16190 if(!node || node.parentNode == el){
16193 var p = node.parentNode;
16194 while(p && p != el){
16195 if(p.parentNode == el){
16204 onClick : function(e){
16205 var item = this.findItemFromChild(e.getTarget());
16207 var index = this.indexOf(item);
16208 if(this.onItemClick(item, index, e) !== false){
16209 this.fireEvent("click", this, index, item, e);
16212 this.clearSelections();
16217 onContextMenu : function(e){
16218 var item = this.findItemFromChild(e.getTarget());
16220 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16225 onDblClick : function(e){
16226 var item = this.findItemFromChild(e.getTarget());
16228 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16232 onItemClick : function(item, index, e)
16234 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16237 if (this.toggleSelect) {
16238 var m = this.isSelected(item) ? 'unselect' : 'select';
16241 _t[m](item, true, false);
16244 if(this.multiSelect || this.singleSelect){
16245 if(this.multiSelect && e.shiftKey && this.lastSelection){
16246 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16248 this.select(item, this.multiSelect && e.ctrlKey);
16249 this.lastSelection = item;
16252 if(!this.tickable){
16253 e.preventDefault();
16261 * Get the number of selected nodes.
16264 getSelectionCount : function(){
16265 return this.selections.length;
16269 * Get the currently selected nodes.
16270 * @return {Array} An array of HTMLElements
16272 getSelectedNodes : function(){
16273 return this.selections;
16277 * Get the indexes of the selected nodes.
16280 getSelectedIndexes : function(){
16281 var indexes = [], s = this.selections;
16282 for(var i = 0, len = s.length; i < len; i++){
16283 indexes.push(s[i].nodeIndex);
16289 * Clear all selections
16290 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16292 clearSelections : function(suppressEvent){
16293 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16294 this.cmp.elements = this.selections;
16295 this.cmp.removeClass(this.selectedClass);
16296 this.selections = [];
16297 if(!suppressEvent){
16298 this.fireEvent("selectionchange", this, this.selections);
16304 * Returns true if the passed node is selected
16305 * @param {HTMLElement/Number} node The node or node index
16306 * @return {Boolean}
16308 isSelected : function(node){
16309 var s = this.selections;
16313 node = this.getNode(node);
16314 return s.indexOf(node) !== -1;
16319 * @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
16320 * @param {Boolean} keepExisting (optional) true to keep existing selections
16321 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16323 select : function(nodeInfo, keepExisting, suppressEvent){
16324 if(nodeInfo instanceof Array){
16326 this.clearSelections(true);
16328 for(var i = 0, len = nodeInfo.length; i < len; i++){
16329 this.select(nodeInfo[i], true, true);
16333 var node = this.getNode(nodeInfo);
16334 if(!node || this.isSelected(node)){
16335 return; // already selected.
16338 this.clearSelections(true);
16341 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16342 Roo.fly(node).addClass(this.selectedClass);
16343 this.selections.push(node);
16344 if(!suppressEvent){
16345 this.fireEvent("selectionchange", this, this.selections);
16353 * @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
16354 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16355 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16357 unselect : function(nodeInfo, keepExisting, suppressEvent)
16359 if(nodeInfo instanceof Array){
16360 Roo.each(this.selections, function(s) {
16361 this.unselect(s, nodeInfo);
16365 var node = this.getNode(nodeInfo);
16366 if(!node || !this.isSelected(node)){
16367 //Roo.log("not selected");
16368 return; // not selected.
16372 Roo.each(this.selections, function(s) {
16374 Roo.fly(node).removeClass(this.selectedClass);
16381 this.selections= ns;
16382 this.fireEvent("selectionchange", this, this.selections);
16386 * Gets a template node.
16387 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16388 * @return {HTMLElement} The node or null if it wasn't found
16390 getNode : function(nodeInfo){
16391 if(typeof nodeInfo == "string"){
16392 return document.getElementById(nodeInfo);
16393 }else if(typeof nodeInfo == "number"){
16394 return this.nodes[nodeInfo];
16400 * Gets a range template nodes.
16401 * @param {Number} startIndex
16402 * @param {Number} endIndex
16403 * @return {Array} An array of nodes
16405 getNodes : function(start, end){
16406 var ns = this.nodes;
16407 start = start || 0;
16408 end = typeof end == "undefined" ? ns.length - 1 : end;
16411 for(var i = start; i <= end; i++){
16415 for(var i = start; i >= end; i--){
16423 * Finds the index of the passed node
16424 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16425 * @return {Number} The index of the node or -1
16427 indexOf : function(node){
16428 node = this.getNode(node);
16429 if(typeof node.nodeIndex == "number"){
16430 return node.nodeIndex;
16432 var ns = this.nodes;
16433 for(var i = 0, len = ns.length; i < len; i++){
16444 * based on jquery fullcalendar
16448 Roo.bootstrap = Roo.bootstrap || {};
16450 * @class Roo.bootstrap.Calendar
16451 * @extends Roo.bootstrap.Component
16452 * Bootstrap Calendar class
16453 * @cfg {Boolean} loadMask (true|false) default false
16454 * @cfg {Object} header generate the user specific header of the calendar, default false
16457 * Create a new Container
16458 * @param {Object} config The config object
16463 Roo.bootstrap.Calendar = function(config){
16464 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16468 * Fires when a date is selected
16469 * @param {DatePicker} this
16470 * @param {Date} date The selected date
16474 * @event monthchange
16475 * Fires when the displayed month changes
16476 * @param {DatePicker} this
16477 * @param {Date} date The selected month
16479 'monthchange': true,
16481 * @event evententer
16482 * Fires when mouse over an event
16483 * @param {Calendar} this
16484 * @param {event} Event
16486 'evententer': true,
16488 * @event eventleave
16489 * Fires when the mouse leaves an
16490 * @param {Calendar} this
16493 'eventleave': true,
16495 * @event eventclick
16496 * Fires when the mouse click an
16497 * @param {Calendar} this
16506 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16509 * @cfg {Number} startDay
16510 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16518 getAutoCreate : function(){
16521 var fc_button = function(name, corner, style, content ) {
16522 return Roo.apply({},{
16524 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16526 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16529 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16540 style : 'width:100%',
16547 cls : 'fc-header-left',
16549 fc_button('prev', 'left', 'arrow', '‹' ),
16550 fc_button('next', 'right', 'arrow', '›' ),
16551 { tag: 'span', cls: 'fc-header-space' },
16552 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16560 cls : 'fc-header-center',
16564 cls: 'fc-header-title',
16567 html : 'month / year'
16575 cls : 'fc-header-right',
16577 /* fc_button('month', 'left', '', 'month' ),
16578 fc_button('week', '', '', 'week' ),
16579 fc_button('day', 'right', '', 'day' )
16591 header = this.header;
16594 var cal_heads = function() {
16596 // fixme - handle this.
16598 for (var i =0; i < Date.dayNames.length; i++) {
16599 var d = Date.dayNames[i];
16602 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16603 html : d.substring(0,3)
16607 ret[0].cls += ' fc-first';
16608 ret[6].cls += ' fc-last';
16611 var cal_cell = function(n) {
16614 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16619 cls: 'fc-day-number',
16623 cls: 'fc-day-content',
16627 style: 'position: relative;' // height: 17px;
16639 var cal_rows = function() {
16642 for (var r = 0; r < 6; r++) {
16649 for (var i =0; i < Date.dayNames.length; i++) {
16650 var d = Date.dayNames[i];
16651 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16654 row.cn[0].cls+=' fc-first';
16655 row.cn[0].cn[0].style = 'min-height:90px';
16656 row.cn[6].cls+=' fc-last';
16660 ret[0].cls += ' fc-first';
16661 ret[4].cls += ' fc-prev-last';
16662 ret[5].cls += ' fc-last';
16669 cls: 'fc-border-separate',
16670 style : 'width:100%',
16678 cls : 'fc-first fc-last',
16696 cls : 'fc-content',
16697 style : "position: relative;",
16700 cls : 'fc-view fc-view-month fc-grid',
16701 style : 'position: relative',
16702 unselectable : 'on',
16705 cls : 'fc-event-container',
16706 style : 'position:absolute;z-index:8;top:0;left:0;'
16724 initEvents : function()
16727 throw "can not find store for calendar";
16733 style: "text-align:center",
16737 style: "background-color:white;width:50%;margin:250 auto",
16741 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16752 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16754 var size = this.el.select('.fc-content', true).first().getSize();
16755 this.maskEl.setSize(size.width, size.height);
16756 this.maskEl.enableDisplayMode("block");
16757 if(!this.loadMask){
16758 this.maskEl.hide();
16761 this.store = Roo.factory(this.store, Roo.data);
16762 this.store.on('load', this.onLoad, this);
16763 this.store.on('beforeload', this.onBeforeLoad, this);
16767 this.cells = this.el.select('.fc-day',true);
16768 //Roo.log(this.cells);
16769 this.textNodes = this.el.query('.fc-day-number');
16770 this.cells.addClassOnOver('fc-state-hover');
16772 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16773 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16774 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16775 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16777 this.on('monthchange', this.onMonthChange, this);
16779 this.update(new Date().clearTime());
16782 resize : function() {
16783 var sz = this.el.getSize();
16785 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16786 this.el.select('.fc-day-content div',true).setHeight(34);
16791 showPrevMonth : function(e){
16792 this.update(this.activeDate.add("mo", -1));
16794 showToday : function(e){
16795 this.update(new Date().clearTime());
16798 showNextMonth : function(e){
16799 this.update(this.activeDate.add("mo", 1));
16803 showPrevYear : function(){
16804 this.update(this.activeDate.add("y", -1));
16808 showNextYear : function(){
16809 this.update(this.activeDate.add("y", 1));
16814 update : function(date)
16816 var vd = this.activeDate;
16817 this.activeDate = date;
16818 // if(vd && this.el){
16819 // var t = date.getTime();
16820 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16821 // Roo.log('using add remove');
16823 // this.fireEvent('monthchange', this, date);
16825 // this.cells.removeClass("fc-state-highlight");
16826 // this.cells.each(function(c){
16827 // if(c.dateValue == t){
16828 // c.addClass("fc-state-highlight");
16829 // setTimeout(function(){
16830 // try{c.dom.firstChild.focus();}catch(e){}
16840 var days = date.getDaysInMonth();
16842 var firstOfMonth = date.getFirstDateOfMonth();
16843 var startingPos = firstOfMonth.getDay()-this.startDay;
16845 if(startingPos < this.startDay){
16849 var pm = date.add(Date.MONTH, -1);
16850 var prevStart = pm.getDaysInMonth()-startingPos;
16852 this.cells = this.el.select('.fc-day',true);
16853 this.textNodes = this.el.query('.fc-day-number');
16854 this.cells.addClassOnOver('fc-state-hover');
16856 var cells = this.cells.elements;
16857 var textEls = this.textNodes;
16859 Roo.each(cells, function(cell){
16860 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16863 days += startingPos;
16865 // convert everything to numbers so it's fast
16866 var day = 86400000;
16867 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16870 //Roo.log(prevStart);
16872 var today = new Date().clearTime().getTime();
16873 var sel = date.clearTime().getTime();
16874 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16875 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16876 var ddMatch = this.disabledDatesRE;
16877 var ddText = this.disabledDatesText;
16878 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16879 var ddaysText = this.disabledDaysText;
16880 var format = this.format;
16882 var setCellClass = function(cal, cell){
16886 //Roo.log('set Cell Class');
16888 var t = d.getTime();
16892 cell.dateValue = t;
16894 cell.className += " fc-today";
16895 cell.className += " fc-state-highlight";
16896 cell.title = cal.todayText;
16899 // disable highlight in other month..
16900 //cell.className += " fc-state-highlight";
16905 cell.className = " fc-state-disabled";
16906 cell.title = cal.minText;
16910 cell.className = " fc-state-disabled";
16911 cell.title = cal.maxText;
16915 if(ddays.indexOf(d.getDay()) != -1){
16916 cell.title = ddaysText;
16917 cell.className = " fc-state-disabled";
16920 if(ddMatch && format){
16921 var fvalue = d.dateFormat(format);
16922 if(ddMatch.test(fvalue)){
16923 cell.title = ddText.replace("%0", fvalue);
16924 cell.className = " fc-state-disabled";
16928 if (!cell.initialClassName) {
16929 cell.initialClassName = cell.dom.className;
16932 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16937 for(; i < startingPos; i++) {
16938 textEls[i].innerHTML = (++prevStart);
16939 d.setDate(d.getDate()+1);
16941 cells[i].className = "fc-past fc-other-month";
16942 setCellClass(this, cells[i]);
16947 for(; i < days; i++){
16948 intDay = i - startingPos + 1;
16949 textEls[i].innerHTML = (intDay);
16950 d.setDate(d.getDate()+1);
16952 cells[i].className = ''; // "x-date-active";
16953 setCellClass(this, cells[i]);
16957 for(; i < 42; i++) {
16958 textEls[i].innerHTML = (++extraDays);
16959 d.setDate(d.getDate()+1);
16961 cells[i].className = "fc-future fc-other-month";
16962 setCellClass(this, cells[i]);
16965 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16967 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16969 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16970 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16972 if(totalRows != 6){
16973 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16974 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16977 this.fireEvent('monthchange', this, date);
16981 if(!this.internalRender){
16982 var main = this.el.dom.firstChild;
16983 var w = main.offsetWidth;
16984 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16985 Roo.fly(main).setWidth(w);
16986 this.internalRender = true;
16987 // opera does not respect the auto grow header center column
16988 // then, after it gets a width opera refuses to recalculate
16989 // without a second pass
16990 if(Roo.isOpera && !this.secondPass){
16991 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16992 this.secondPass = true;
16993 this.update.defer(10, this, [date]);
17000 findCell : function(dt) {
17001 dt = dt.clearTime().getTime();
17003 this.cells.each(function(c){
17004 //Roo.log("check " +c.dateValue + '?=' + dt);
17005 if(c.dateValue == dt){
17015 findCells : function(ev) {
17016 var s = ev.start.clone().clearTime().getTime();
17018 var e= ev.end.clone().clearTime().getTime();
17021 this.cells.each(function(c){
17022 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17024 if(c.dateValue > e){
17027 if(c.dateValue < s){
17036 // findBestRow: function(cells)
17040 // for (var i =0 ; i < cells.length;i++) {
17041 // ret = Math.max(cells[i].rows || 0,ret);
17048 addItem : function(ev)
17050 // look for vertical location slot in
17051 var cells = this.findCells(ev);
17053 // ev.row = this.findBestRow(cells);
17055 // work out the location.
17059 for(var i =0; i < cells.length; i++) {
17061 cells[i].row = cells[0].row;
17064 cells[i].row = cells[i].row + 1;
17074 if (crow.start.getY() == cells[i].getY()) {
17076 crow.end = cells[i];
17093 cells[0].events.push(ev);
17095 this.calevents.push(ev);
17098 clearEvents: function() {
17100 if(!this.calevents){
17104 Roo.each(this.cells.elements, function(c){
17110 Roo.each(this.calevents, function(e) {
17111 Roo.each(e.els, function(el) {
17112 el.un('mouseenter' ,this.onEventEnter, this);
17113 el.un('mouseleave' ,this.onEventLeave, this);
17118 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17124 renderEvents: function()
17128 this.cells.each(function(c) {
17137 if(c.row != c.events.length){
17138 r = 4 - (4 - (c.row - c.events.length));
17141 c.events = ev.slice(0, r);
17142 c.more = ev.slice(r);
17144 if(c.more.length && c.more.length == 1){
17145 c.events.push(c.more.pop());
17148 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17152 this.cells.each(function(c) {
17154 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17157 for (var e = 0; e < c.events.length; e++){
17158 var ev = c.events[e];
17159 var rows = ev.rows;
17161 for(var i = 0; i < rows.length; i++) {
17163 // how many rows should it span..
17166 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17167 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17169 unselectable : "on",
17172 cls: 'fc-event-inner',
17176 // cls: 'fc-event-time',
17177 // html : cells.length > 1 ? '' : ev.time
17181 cls: 'fc-event-title',
17182 html : String.format('{0}', ev.title)
17189 cls: 'ui-resizable-handle ui-resizable-e',
17190 html : '  '
17197 cfg.cls += ' fc-event-start';
17199 if ((i+1) == rows.length) {
17200 cfg.cls += ' fc-event-end';
17203 var ctr = _this.el.select('.fc-event-container',true).first();
17204 var cg = ctr.createChild(cfg);
17206 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17207 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17209 var r = (c.more.length) ? 1 : 0;
17210 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17211 cg.setWidth(ebox.right - sbox.x -2);
17213 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17214 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17215 cg.on('click', _this.onEventClick, _this, ev);
17226 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17227 style : 'position: absolute',
17228 unselectable : "on",
17231 cls: 'fc-event-inner',
17235 cls: 'fc-event-title',
17243 cls: 'ui-resizable-handle ui-resizable-e',
17244 html : '  '
17250 var ctr = _this.el.select('.fc-event-container',true).first();
17251 var cg = ctr.createChild(cfg);
17253 var sbox = c.select('.fc-day-content',true).first().getBox();
17254 var ebox = c.select('.fc-day-content',true).first().getBox();
17256 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17257 cg.setWidth(ebox.right - sbox.x -2);
17259 cg.on('click', _this.onMoreEventClick, _this, c.more);
17269 onEventEnter: function (e, el,event,d) {
17270 this.fireEvent('evententer', this, el, event);
17273 onEventLeave: function (e, el,event,d) {
17274 this.fireEvent('eventleave', this, el, event);
17277 onEventClick: function (e, el,event,d) {
17278 this.fireEvent('eventclick', this, el, event);
17281 onMonthChange: function () {
17285 onMoreEventClick: function(e, el, more)
17289 this.calpopover.placement = 'right';
17290 this.calpopover.setTitle('More');
17292 this.calpopover.setContent('');
17294 var ctr = this.calpopover.el.select('.popover-content', true).first();
17296 Roo.each(more, function(m){
17298 cls : 'fc-event-hori fc-event-draggable',
17301 var cg = ctr.createChild(cfg);
17303 cg.on('click', _this.onEventClick, _this, m);
17306 this.calpopover.show(el);
17311 onLoad: function ()
17313 this.calevents = [];
17316 if(this.store.getCount() > 0){
17317 this.store.data.each(function(d){
17320 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17321 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17322 time : d.data.start_time,
17323 title : d.data.title,
17324 description : d.data.description,
17325 venue : d.data.venue
17330 this.renderEvents();
17332 if(this.calevents.length && this.loadMask){
17333 this.maskEl.hide();
17337 onBeforeLoad: function()
17339 this.clearEvents();
17341 this.maskEl.show();
17355 * @class Roo.bootstrap.Popover
17356 * @extends Roo.bootstrap.Component
17357 * Bootstrap Popover class
17358 * @cfg {String} html contents of the popover (or false to use children..)
17359 * @cfg {String} title of popover (or false to hide)
17360 * @cfg {String} placement how it is placed
17361 * @cfg {String} trigger click || hover (or false to trigger manually)
17362 * @cfg {String} over what (parent or false to trigger manually.)
17363 * @cfg {Number} delay - delay before showing
17366 * Create a new Popover
17367 * @param {Object} config The config object
17370 Roo.bootstrap.Popover = function(config){
17371 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17377 * After the popover show
17379 * @param {Roo.bootstrap.Popover} this
17384 * After the popover hide
17386 * @param {Roo.bootstrap.Popover} this
17392 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17394 title: 'Fill in a title',
17397 placement : 'right',
17398 trigger : 'hover', // hover
17404 can_build_overlaid : false,
17406 getChildContainer : function()
17408 return this.el.select('.popover-content',true).first();
17411 getAutoCreate : function(){
17414 cls : 'popover roo-dynamic',
17415 style: 'display:block',
17421 cls : 'popover-inner',
17425 cls: 'popover-title',
17429 cls : 'popover-content',
17440 setTitle: function(str)
17443 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17445 setContent: function(str)
17448 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17450 // as it get's added to the bottom of the page.
17451 onRender : function(ct, position)
17453 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17455 var cfg = Roo.apply({}, this.getAutoCreate());
17459 cfg.cls += ' ' + this.cls;
17462 cfg.style = this.style;
17464 //Roo.log("adding to ");
17465 this.el = Roo.get(document.body).createChild(cfg, position);
17466 // Roo.log(this.el);
17471 initEvents : function()
17473 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17474 this.el.enableDisplayMode('block');
17476 if (this.over === false) {
17479 if (this.triggers === false) {
17482 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17483 var triggers = this.trigger ? this.trigger.split(' ') : [];
17484 Roo.each(triggers, function(trigger) {
17486 if (trigger == 'click') {
17487 on_el.on('click', this.toggle, this);
17488 } else if (trigger != 'manual') {
17489 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17490 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17492 on_el.on(eventIn ,this.enter, this);
17493 on_el.on(eventOut, this.leave, this);
17504 toggle : function () {
17505 this.hoverState == 'in' ? this.leave() : this.enter();
17508 enter : function () {
17510 clearTimeout(this.timeout);
17512 this.hoverState = 'in';
17514 if (!this.delay || !this.delay.show) {
17519 this.timeout = setTimeout(function () {
17520 if (_t.hoverState == 'in') {
17523 }, this.delay.show)
17526 leave : function() {
17527 clearTimeout(this.timeout);
17529 this.hoverState = 'out';
17531 if (!this.delay || !this.delay.hide) {
17536 this.timeout = setTimeout(function () {
17537 if (_t.hoverState == 'out') {
17540 }, this.delay.hide)
17543 show : function (on_el)
17546 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17550 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17551 if (this.html !== false) {
17552 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17554 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17555 if (!this.title.length) {
17556 this.el.select('.popover-title',true).hide();
17559 var placement = typeof this.placement == 'function' ?
17560 this.placement.call(this, this.el, on_el) :
17563 var autoToken = /\s?auto?\s?/i;
17564 var autoPlace = autoToken.test(placement);
17566 placement = placement.replace(autoToken, '') || 'top';
17570 //this.el.setXY([0,0]);
17572 this.el.dom.style.display='block';
17573 this.el.addClass(placement);
17575 //this.el.appendTo(on_el);
17577 var p = this.getPosition();
17578 var box = this.el.getBox();
17583 var align = Roo.bootstrap.Popover.alignment[placement];
17586 this.el.alignTo(on_el, align[0],align[1]);
17587 //var arrow = this.el.select('.arrow',true).first();
17588 //arrow.set(align[2],
17590 this.el.addClass('in');
17593 if (this.el.hasClass('fade')) {
17597 this.hoverState = 'in';
17599 this.fireEvent('show', this);
17604 this.el.setXY([0,0]);
17605 this.el.removeClass('in');
17607 this.hoverState = null;
17609 this.fireEvent('hide', this);
17614 Roo.bootstrap.Popover.alignment = {
17615 'left' : ['r-l', [-10,0], 'right'],
17616 'right' : ['l-r', [10,0], 'left'],
17617 'bottom' : ['t-b', [0,10], 'top'],
17618 'top' : [ 'b-t', [0,-10], 'bottom']
17629 * @class Roo.bootstrap.Progress
17630 * @extends Roo.bootstrap.Component
17631 * Bootstrap Progress class
17632 * @cfg {Boolean} striped striped of the progress bar
17633 * @cfg {Boolean} active animated of the progress bar
17637 * Create a new Progress
17638 * @param {Object} config The config object
17641 Roo.bootstrap.Progress = function(config){
17642 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17645 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17650 getAutoCreate : function(){
17658 cfg.cls += ' progress-striped';
17662 cfg.cls += ' active';
17681 * @class Roo.bootstrap.ProgressBar
17682 * @extends Roo.bootstrap.Component
17683 * Bootstrap ProgressBar class
17684 * @cfg {Number} aria_valuenow aria-value now
17685 * @cfg {Number} aria_valuemin aria-value min
17686 * @cfg {Number} aria_valuemax aria-value max
17687 * @cfg {String} label label for the progress bar
17688 * @cfg {String} panel (success | info | warning | danger )
17689 * @cfg {String} role role of the progress bar
17690 * @cfg {String} sr_only text
17694 * Create a new ProgressBar
17695 * @param {Object} config The config object
17698 Roo.bootstrap.ProgressBar = function(config){
17699 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17702 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17706 aria_valuemax : 100,
17712 getAutoCreate : function()
17717 cls: 'progress-bar',
17718 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17730 cfg.role = this.role;
17733 if(this.aria_valuenow){
17734 cfg['aria-valuenow'] = this.aria_valuenow;
17737 if(this.aria_valuemin){
17738 cfg['aria-valuemin'] = this.aria_valuemin;
17741 if(this.aria_valuemax){
17742 cfg['aria-valuemax'] = this.aria_valuemax;
17745 if(this.label && !this.sr_only){
17746 cfg.html = this.label;
17750 cfg.cls += ' progress-bar-' + this.panel;
17756 update : function(aria_valuenow)
17758 this.aria_valuenow = aria_valuenow;
17760 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17775 * @class Roo.bootstrap.TabGroup
17776 * @extends Roo.bootstrap.Column
17777 * Bootstrap Column class
17778 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17779 * @cfg {Boolean} carousel true to make the group behave like a carousel
17780 * @cfg {Boolean} bullets show bullets for the panels
17781 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17782 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17783 * @cfg {Boolean} showarrow (true|false) show arrow default true
17786 * Create a new TabGroup
17787 * @param {Object} config The config object
17790 Roo.bootstrap.TabGroup = function(config){
17791 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17793 this.navId = Roo.id();
17796 Roo.bootstrap.TabGroup.register(this);
17800 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17803 transition : false,
17808 slideOnTouch : false,
17811 getAutoCreate : function()
17813 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17815 cfg.cls += ' tab-content';
17817 if (this.carousel) {
17818 cfg.cls += ' carousel slide';
17821 cls : 'carousel-inner',
17825 if(this.bullets && !Roo.isTouch){
17828 cls : 'carousel-bullets',
17832 if(this.bullets_cls){
17833 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17840 cfg.cn[0].cn.push(bullets);
17843 if(this.showarrow){
17844 cfg.cn[0].cn.push({
17846 class : 'carousel-arrow',
17850 class : 'carousel-prev',
17854 class : 'fa fa-chevron-left'
17860 class : 'carousel-next',
17864 class : 'fa fa-chevron-right'
17877 initEvents: function()
17879 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17880 // this.el.on("touchstart", this.onTouchStart, this);
17883 if(this.autoslide){
17886 this.slideFn = window.setInterval(function() {
17887 _this.showPanelNext();
17891 if(this.showarrow){
17892 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17893 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17899 // onTouchStart : function(e, el, o)
17901 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17905 // this.showPanelNext();
17909 getChildContainer : function()
17911 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17915 * register a Navigation item
17916 * @param {Roo.bootstrap.NavItem} the navitem to add
17918 register : function(item)
17920 this.tabs.push( item);
17921 item.navId = this.navId; // not really needed..
17926 getActivePanel : function()
17929 Roo.each(this.tabs, function(t) {
17939 getPanelByName : function(n)
17942 Roo.each(this.tabs, function(t) {
17943 if (t.tabId == n) {
17951 indexOfPanel : function(p)
17954 Roo.each(this.tabs, function(t,i) {
17955 if (t.tabId == p.tabId) {
17964 * show a specific panel
17965 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17966 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17968 showPanel : function (pan)
17970 if(this.transition || typeof(pan) == 'undefined'){
17971 Roo.log("waiting for the transitionend");
17975 if (typeof(pan) == 'number') {
17976 pan = this.tabs[pan];
17979 if (typeof(pan) == 'string') {
17980 pan = this.getPanelByName(pan);
17983 var cur = this.getActivePanel();
17986 Roo.log('pan or acitve pan is undefined');
17990 if (pan.tabId == this.getActivePanel().tabId) {
17994 if (false === cur.fireEvent('beforedeactivate')) {
17998 if(this.bullets > 0 && !Roo.isTouch){
17999 this.setActiveBullet(this.indexOfPanel(pan));
18002 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18004 this.transition = true;
18005 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18006 var lr = dir == 'next' ? 'left' : 'right';
18007 pan.el.addClass(dir); // or prev
18008 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18009 cur.el.addClass(lr); // or right
18010 pan.el.addClass(lr);
18013 cur.el.on('transitionend', function() {
18014 Roo.log("trans end?");
18016 pan.el.removeClass([lr,dir]);
18017 pan.setActive(true);
18019 cur.el.removeClass([lr]);
18020 cur.setActive(false);
18022 _this.transition = false;
18024 }, this, { single: true } );
18029 cur.setActive(false);
18030 pan.setActive(true);
18035 showPanelNext : function()
18037 var i = this.indexOfPanel(this.getActivePanel());
18039 if (i >= this.tabs.length - 1 && !this.autoslide) {
18043 if (i >= this.tabs.length - 1 && this.autoslide) {
18047 this.showPanel(this.tabs[i+1]);
18050 showPanelPrev : function()
18052 var i = this.indexOfPanel(this.getActivePanel());
18054 if (i < 1 && !this.autoslide) {
18058 if (i < 1 && this.autoslide) {
18059 i = this.tabs.length;
18062 this.showPanel(this.tabs[i-1]);
18066 addBullet: function()
18068 if(!this.bullets || Roo.isTouch){
18071 var ctr = this.el.select('.carousel-bullets',true).first();
18072 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18073 var bullet = ctr.createChild({
18074 cls : 'bullet bullet-' + i
18075 },ctr.dom.lastChild);
18080 bullet.on('click', (function(e, el, o, ii, t){
18082 e.preventDefault();
18084 this.showPanel(ii);
18086 if(this.autoslide && this.slideFn){
18087 clearInterval(this.slideFn);
18088 this.slideFn = window.setInterval(function() {
18089 _this.showPanelNext();
18093 }).createDelegate(this, [i, bullet], true));
18098 setActiveBullet : function(i)
18104 Roo.each(this.el.select('.bullet', true).elements, function(el){
18105 el.removeClass('selected');
18108 var bullet = this.el.select('.bullet-' + i, true).first();
18114 bullet.addClass('selected');
18125 Roo.apply(Roo.bootstrap.TabGroup, {
18129 * register a Navigation Group
18130 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18132 register : function(navgrp)
18134 this.groups[navgrp.navId] = navgrp;
18138 * fetch a Navigation Group based on the navigation ID
18139 * if one does not exist , it will get created.
18140 * @param {string} the navgroup to add
18141 * @returns {Roo.bootstrap.NavGroup} the navgroup
18143 get: function(navId) {
18144 if (typeof(this.groups[navId]) == 'undefined') {
18145 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18147 return this.groups[navId] ;
18162 * @class Roo.bootstrap.TabPanel
18163 * @extends Roo.bootstrap.Component
18164 * Bootstrap TabPanel class
18165 * @cfg {Boolean} active panel active
18166 * @cfg {String} html panel content
18167 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18168 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18169 * @cfg {String} href click to link..
18173 * Create a new TabPanel
18174 * @param {Object} config The config object
18177 Roo.bootstrap.TabPanel = function(config){
18178 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18182 * Fires when the active status changes
18183 * @param {Roo.bootstrap.TabPanel} this
18184 * @param {Boolean} state the new state
18189 * @event beforedeactivate
18190 * Fires before a tab is de-activated - can be used to do validation on a form.
18191 * @param {Roo.bootstrap.TabPanel} this
18192 * @return {Boolean} false if there is an error
18195 'beforedeactivate': true
18198 this.tabId = this.tabId || Roo.id();
18202 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18210 getAutoCreate : function(){
18213 // item is needed for carousel - not sure if it has any effect otherwise
18214 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18215 html: this.html || ''
18219 cfg.cls += ' active';
18223 cfg.tabId = this.tabId;
18230 initEvents: function()
18232 var p = this.parent();
18234 this.navId = this.navId || p.navId;
18236 if (typeof(this.navId) != 'undefined') {
18237 // not really needed.. but just in case.. parent should be a NavGroup.
18238 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18242 var i = tg.tabs.length - 1;
18244 if(this.active && tg.bullets > 0 && i < tg.bullets){
18245 tg.setActiveBullet(i);
18249 this.el.on('click', this.onClick, this);
18252 this.el.on("touchstart", this.onTouchStart, this);
18253 this.el.on("touchmove", this.onTouchMove, this);
18254 this.el.on("touchend", this.onTouchEnd, this);
18259 onRender : function(ct, position)
18261 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18264 setActive : function(state)
18266 Roo.log("panel - set active " + this.tabId + "=" + state);
18268 this.active = state;
18270 this.el.removeClass('active');
18272 } else if (!this.el.hasClass('active')) {
18273 this.el.addClass('active');
18276 this.fireEvent('changed', this, state);
18279 onClick : function(e)
18281 e.preventDefault();
18283 if(!this.href.length){
18287 window.location.href = this.href;
18296 onTouchStart : function(e)
18298 this.swiping = false;
18300 this.startX = e.browserEvent.touches[0].clientX;
18301 this.startY = e.browserEvent.touches[0].clientY;
18304 onTouchMove : function(e)
18306 this.swiping = true;
18308 this.endX = e.browserEvent.touches[0].clientX;
18309 this.endY = e.browserEvent.touches[0].clientY;
18312 onTouchEnd : function(e)
18319 var tabGroup = this.parent();
18321 if(this.endX > this.startX){ // swiping right
18322 tabGroup.showPanelPrev();
18326 if(this.startX > this.endX){ // swiping left
18327 tabGroup.showPanelNext();
18346 * @class Roo.bootstrap.DateField
18347 * @extends Roo.bootstrap.Input
18348 * Bootstrap DateField class
18349 * @cfg {Number} weekStart default 0
18350 * @cfg {String} viewMode default empty, (months|years)
18351 * @cfg {String} minViewMode default empty, (months|years)
18352 * @cfg {Number} startDate default -Infinity
18353 * @cfg {Number} endDate default Infinity
18354 * @cfg {Boolean} todayHighlight default false
18355 * @cfg {Boolean} todayBtn default false
18356 * @cfg {Boolean} calendarWeeks default false
18357 * @cfg {Object} daysOfWeekDisabled default empty
18358 * @cfg {Boolean} singleMode default false (true | false)
18360 * @cfg {Boolean} keyboardNavigation default true
18361 * @cfg {String} language default en
18364 * Create a new DateField
18365 * @param {Object} config The config object
18368 Roo.bootstrap.DateField = function(config){
18369 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18373 * Fires when this field show.
18374 * @param {Roo.bootstrap.DateField} this
18375 * @param {Mixed} date The date value
18380 * Fires when this field hide.
18381 * @param {Roo.bootstrap.DateField} this
18382 * @param {Mixed} date The date value
18387 * Fires when select a date.
18388 * @param {Roo.bootstrap.DateField} this
18389 * @param {Mixed} date The date value
18393 * @event beforeselect
18394 * Fires when before select a date.
18395 * @param {Roo.bootstrap.DateField} this
18396 * @param {Mixed} date The date value
18398 beforeselect : true
18402 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18405 * @cfg {String} format
18406 * The default date format string which can be overriden for localization support. The format must be
18407 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18411 * @cfg {String} altFormats
18412 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18413 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18415 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18423 todayHighlight : false,
18429 keyboardNavigation: true,
18431 calendarWeeks: false,
18433 startDate: -Infinity,
18437 daysOfWeekDisabled: [],
18441 singleMode : false,
18443 UTCDate: function()
18445 return new Date(Date.UTC.apply(Date, arguments));
18448 UTCToday: function()
18450 var today = new Date();
18451 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18454 getDate: function() {
18455 var d = this.getUTCDate();
18456 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18459 getUTCDate: function() {
18463 setDate: function(d) {
18464 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18467 setUTCDate: function(d) {
18469 this.setValue(this.formatDate(this.date));
18472 onRender: function(ct, position)
18475 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18477 this.language = this.language || 'en';
18478 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18479 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18481 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18482 this.format = this.format || 'm/d/y';
18483 this.isInline = false;
18484 this.isInput = true;
18485 this.component = this.el.select('.add-on', true).first() || false;
18486 this.component = (this.component && this.component.length === 0) ? false : this.component;
18487 this.hasInput = this.component && this.inputEl().length;
18489 if (typeof(this.minViewMode === 'string')) {
18490 switch (this.minViewMode) {
18492 this.minViewMode = 1;
18495 this.minViewMode = 2;
18498 this.minViewMode = 0;
18503 if (typeof(this.viewMode === 'string')) {
18504 switch (this.viewMode) {
18517 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18519 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18521 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18523 this.picker().on('mousedown', this.onMousedown, this);
18524 this.picker().on('click', this.onClick, this);
18526 this.picker().addClass('datepicker-dropdown');
18528 this.startViewMode = this.viewMode;
18530 if(this.singleMode){
18531 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18532 v.setVisibilityMode(Roo.Element.DISPLAY);
18536 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18537 v.setStyle('width', '189px');
18541 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18542 if(!this.calendarWeeks){
18547 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18548 v.attr('colspan', function(i, val){
18549 return parseInt(val) + 1;
18554 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18556 this.setStartDate(this.startDate);
18557 this.setEndDate(this.endDate);
18559 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18566 if(this.isInline) {
18571 picker : function()
18573 return this.pickerEl;
18574 // return this.el.select('.datepicker', true).first();
18577 fillDow: function()
18579 var dowCnt = this.weekStart;
18588 if(this.calendarWeeks){
18596 while (dowCnt < this.weekStart + 7) {
18600 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18604 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18607 fillMonths: function()
18610 var months = this.picker().select('>.datepicker-months td', true).first();
18612 months.dom.innerHTML = '';
18618 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18621 months.createChild(month);
18628 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;
18630 if (this.date < this.startDate) {
18631 this.viewDate = new Date(this.startDate);
18632 } else if (this.date > this.endDate) {
18633 this.viewDate = new Date(this.endDate);
18635 this.viewDate = new Date(this.date);
18643 var d = new Date(this.viewDate),
18644 year = d.getUTCFullYear(),
18645 month = d.getUTCMonth(),
18646 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18647 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18648 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18649 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18650 currentDate = this.date && this.date.valueOf(),
18651 today = this.UTCToday();
18653 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18655 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18657 // this.picker.select('>tfoot th.today').
18658 // .text(dates[this.language].today)
18659 // .toggle(this.todayBtn !== false);
18661 this.updateNavArrows();
18664 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18666 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18668 prevMonth.setUTCDate(day);
18670 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18672 var nextMonth = new Date(prevMonth);
18674 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18676 nextMonth = nextMonth.valueOf();
18678 var fillMonths = false;
18680 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18682 while(prevMonth.valueOf() <= nextMonth) {
18685 if (prevMonth.getUTCDay() === this.weekStart) {
18687 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18695 if(this.calendarWeeks){
18696 // ISO 8601: First week contains first thursday.
18697 // ISO also states week starts on Monday, but we can be more abstract here.
18699 // Start of current week: based on weekstart/current date
18700 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18701 // Thursday of this week
18702 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18703 // First Thursday of year, year from thursday
18704 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18705 // Calendar week: ms between thursdays, div ms per day, div 7 days
18706 calWeek = (th - yth) / 864e5 / 7 + 1;
18708 fillMonths.cn.push({
18716 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18718 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18721 if (this.todayHighlight &&
18722 prevMonth.getUTCFullYear() == today.getFullYear() &&
18723 prevMonth.getUTCMonth() == today.getMonth() &&
18724 prevMonth.getUTCDate() == today.getDate()) {
18725 clsName += ' today';
18728 if (currentDate && prevMonth.valueOf() === currentDate) {
18729 clsName += ' active';
18732 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18733 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18734 clsName += ' disabled';
18737 fillMonths.cn.push({
18739 cls: 'day ' + clsName,
18740 html: prevMonth.getDate()
18743 prevMonth.setDate(prevMonth.getDate()+1);
18746 var currentYear = this.date && this.date.getUTCFullYear();
18747 var currentMonth = this.date && this.date.getUTCMonth();
18749 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18751 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18752 v.removeClass('active');
18754 if(currentYear === year && k === currentMonth){
18755 v.addClass('active');
18758 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18759 v.addClass('disabled');
18765 year = parseInt(year/10, 10) * 10;
18767 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18769 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18772 for (var i = -1; i < 11; i++) {
18773 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18775 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18783 showMode: function(dir)
18786 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18789 Roo.each(this.picker().select('>div',true).elements, function(v){
18790 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18793 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18798 if(this.isInline) {
18802 this.picker().removeClass(['bottom', 'top']);
18804 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18806 * place to the top of element!
18810 this.picker().addClass('top');
18811 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18816 this.picker().addClass('bottom');
18818 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18821 parseDate : function(value)
18823 if(!value || value instanceof Date){
18826 var v = Date.parseDate(value, this.format);
18827 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18828 v = Date.parseDate(value, 'Y-m-d');
18830 if(!v && this.altFormats){
18831 if(!this.altFormatsArray){
18832 this.altFormatsArray = this.altFormats.split("|");
18834 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18835 v = Date.parseDate(value, this.altFormatsArray[i]);
18841 formatDate : function(date, fmt)
18843 return (!date || !(date instanceof Date)) ?
18844 date : date.dateFormat(fmt || this.format);
18847 onFocus : function()
18849 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18853 onBlur : function()
18855 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18857 var d = this.inputEl().getValue();
18864 showPopup : function()
18866 this.picker().show();
18870 this.fireEvent('showpopup', this, this.date);
18873 hidePopup : function()
18875 if(this.isInline) {
18878 this.picker().hide();
18879 this.viewMode = this.startViewMode;
18882 this.fireEvent('hidepopup', this, this.date);
18886 onMousedown: function(e)
18888 e.stopPropagation();
18889 e.preventDefault();
18894 Roo.bootstrap.DateField.superclass.keyup.call(this);
18898 setValue: function(v)
18900 if(this.fireEvent('beforeselect', this, v) !== false){
18901 var d = new Date(this.parseDate(v) ).clearTime();
18903 if(isNaN(d.getTime())){
18904 this.date = this.viewDate = '';
18905 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18909 v = this.formatDate(d);
18911 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18913 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18917 this.fireEvent('select', this, this.date);
18921 getValue: function()
18923 return this.formatDate(this.date);
18926 fireKey: function(e)
18928 if (!this.picker().isVisible()){
18929 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18935 var dateChanged = false,
18937 newDate, newViewDate;
18942 e.preventDefault();
18946 if (!this.keyboardNavigation) {
18949 dir = e.keyCode == 37 ? -1 : 1;
18952 newDate = this.moveYear(this.date, dir);
18953 newViewDate = this.moveYear(this.viewDate, dir);
18954 } else if (e.shiftKey){
18955 newDate = this.moveMonth(this.date, dir);
18956 newViewDate = this.moveMonth(this.viewDate, dir);
18958 newDate = new Date(this.date);
18959 newDate.setUTCDate(this.date.getUTCDate() + dir);
18960 newViewDate = new Date(this.viewDate);
18961 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18963 if (this.dateWithinRange(newDate)){
18964 this.date = newDate;
18965 this.viewDate = newViewDate;
18966 this.setValue(this.formatDate(this.date));
18968 e.preventDefault();
18969 dateChanged = true;
18974 if (!this.keyboardNavigation) {
18977 dir = e.keyCode == 38 ? -1 : 1;
18979 newDate = this.moveYear(this.date, dir);
18980 newViewDate = this.moveYear(this.viewDate, dir);
18981 } else if (e.shiftKey){
18982 newDate = this.moveMonth(this.date, dir);
18983 newViewDate = this.moveMonth(this.viewDate, dir);
18985 newDate = new Date(this.date);
18986 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18987 newViewDate = new Date(this.viewDate);
18988 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18990 if (this.dateWithinRange(newDate)){
18991 this.date = newDate;
18992 this.viewDate = newViewDate;
18993 this.setValue(this.formatDate(this.date));
18995 e.preventDefault();
18996 dateChanged = true;
19000 this.setValue(this.formatDate(this.date));
19002 e.preventDefault();
19005 this.setValue(this.formatDate(this.date));
19019 onClick: function(e)
19021 e.stopPropagation();
19022 e.preventDefault();
19024 var target = e.getTarget();
19026 if(target.nodeName.toLowerCase() === 'i'){
19027 target = Roo.get(target).dom.parentNode;
19030 var nodeName = target.nodeName;
19031 var className = target.className;
19032 var html = target.innerHTML;
19033 //Roo.log(nodeName);
19035 switch(nodeName.toLowerCase()) {
19037 switch(className) {
19043 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19044 switch(this.viewMode){
19046 this.viewDate = this.moveMonth(this.viewDate, dir);
19050 this.viewDate = this.moveYear(this.viewDate, dir);
19056 var date = new Date();
19057 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19059 this.setValue(this.formatDate(this.date));
19066 if (className.indexOf('disabled') < 0) {
19067 this.viewDate.setUTCDate(1);
19068 if (className.indexOf('month') > -1) {
19069 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19071 var year = parseInt(html, 10) || 0;
19072 this.viewDate.setUTCFullYear(year);
19076 if(this.singleMode){
19077 this.setValue(this.formatDate(this.viewDate));
19088 //Roo.log(className);
19089 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19090 var day = parseInt(html, 10) || 1;
19091 var year = this.viewDate.getUTCFullYear(),
19092 month = this.viewDate.getUTCMonth();
19094 if (className.indexOf('old') > -1) {
19101 } else if (className.indexOf('new') > -1) {
19109 //Roo.log([year,month,day]);
19110 this.date = this.UTCDate(year, month, day,0,0,0,0);
19111 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19113 //Roo.log(this.formatDate(this.date));
19114 this.setValue(this.formatDate(this.date));
19121 setStartDate: function(startDate)
19123 this.startDate = startDate || -Infinity;
19124 if (this.startDate !== -Infinity) {
19125 this.startDate = this.parseDate(this.startDate);
19128 this.updateNavArrows();
19131 setEndDate: function(endDate)
19133 this.endDate = endDate || Infinity;
19134 if (this.endDate !== Infinity) {
19135 this.endDate = this.parseDate(this.endDate);
19138 this.updateNavArrows();
19141 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19143 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19144 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19145 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19147 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19148 return parseInt(d, 10);
19151 this.updateNavArrows();
19154 updateNavArrows: function()
19156 if(this.singleMode){
19160 var d = new Date(this.viewDate),
19161 year = d.getUTCFullYear(),
19162 month = d.getUTCMonth();
19164 Roo.each(this.picker().select('.prev', true).elements, function(v){
19166 switch (this.viewMode) {
19169 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19175 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19182 Roo.each(this.picker().select('.next', true).elements, function(v){
19184 switch (this.viewMode) {
19187 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19193 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19201 moveMonth: function(date, dir)
19206 var new_date = new Date(date.valueOf()),
19207 day = new_date.getUTCDate(),
19208 month = new_date.getUTCMonth(),
19209 mag = Math.abs(dir),
19211 dir = dir > 0 ? 1 : -1;
19214 // If going back one month, make sure month is not current month
19215 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19217 return new_date.getUTCMonth() == month;
19219 // If going forward one month, make sure month is as expected
19220 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19222 return new_date.getUTCMonth() != new_month;
19224 new_month = month + dir;
19225 new_date.setUTCMonth(new_month);
19226 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19227 if (new_month < 0 || new_month > 11) {
19228 new_month = (new_month + 12) % 12;
19231 // For magnitudes >1, move one month at a time...
19232 for (var i=0; i<mag; i++) {
19233 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19234 new_date = this.moveMonth(new_date, dir);
19236 // ...then reset the day, keeping it in the new month
19237 new_month = new_date.getUTCMonth();
19238 new_date.setUTCDate(day);
19240 return new_month != new_date.getUTCMonth();
19243 // Common date-resetting loop -- if date is beyond end of month, make it
19246 new_date.setUTCDate(--day);
19247 new_date.setUTCMonth(new_month);
19252 moveYear: function(date, dir)
19254 return this.moveMonth(date, dir*12);
19257 dateWithinRange: function(date)
19259 return date >= this.startDate && date <= this.endDate;
19265 this.picker().remove();
19268 validateValue : function(value)
19270 if(this.getVisibilityEl().hasClass('hidden')){
19274 if(value.length < 1) {
19275 if(this.allowBlank){
19281 if(value.length < this.minLength){
19284 if(value.length > this.maxLength){
19288 var vt = Roo.form.VTypes;
19289 if(!vt[this.vtype](value, this)){
19293 if(typeof this.validator == "function"){
19294 var msg = this.validator(value);
19300 if(this.regex && !this.regex.test(value)){
19304 if(typeof(this.parseDate(value)) == 'undefined'){
19308 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19312 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19322 this.date = this.viewDate = '';
19324 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19329 Roo.apply(Roo.bootstrap.DateField, {
19340 html: '<i class="fa fa-arrow-left"/>'
19350 html: '<i class="fa fa-arrow-right"/>'
19392 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19393 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19394 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19395 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19396 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19409 navFnc: 'FullYear',
19414 navFnc: 'FullYear',
19419 Roo.apply(Roo.bootstrap.DateField, {
19423 cls: 'datepicker dropdown-menu roo-dynamic',
19427 cls: 'datepicker-days',
19431 cls: 'table-condensed',
19433 Roo.bootstrap.DateField.head,
19437 Roo.bootstrap.DateField.footer
19444 cls: 'datepicker-months',
19448 cls: 'table-condensed',
19450 Roo.bootstrap.DateField.head,
19451 Roo.bootstrap.DateField.content,
19452 Roo.bootstrap.DateField.footer
19459 cls: 'datepicker-years',
19463 cls: 'table-condensed',
19465 Roo.bootstrap.DateField.head,
19466 Roo.bootstrap.DateField.content,
19467 Roo.bootstrap.DateField.footer
19486 * @class Roo.bootstrap.TimeField
19487 * @extends Roo.bootstrap.Input
19488 * Bootstrap DateField class
19492 * Create a new TimeField
19493 * @param {Object} config The config object
19496 Roo.bootstrap.TimeField = function(config){
19497 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19501 * Fires when this field show.
19502 * @param {Roo.bootstrap.DateField} thisthis
19503 * @param {Mixed} date The date value
19508 * Fires when this field hide.
19509 * @param {Roo.bootstrap.DateField} this
19510 * @param {Mixed} date The date value
19515 * Fires when select a date.
19516 * @param {Roo.bootstrap.DateField} this
19517 * @param {Mixed} date The date value
19523 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19526 * @cfg {String} format
19527 * The default time format string which can be overriden for localization support. The format must be
19528 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19532 onRender: function(ct, position)
19535 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19537 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19539 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19541 this.pop = this.picker().select('>.datepicker-time',true).first();
19542 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19544 this.picker().on('mousedown', this.onMousedown, this);
19545 this.picker().on('click', this.onClick, this);
19547 this.picker().addClass('datepicker-dropdown');
19552 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19553 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19554 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19555 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19556 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19557 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19561 fireKey: function(e){
19562 if (!this.picker().isVisible()){
19563 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19569 e.preventDefault();
19577 this.onTogglePeriod();
19580 this.onIncrementMinutes();
19583 this.onDecrementMinutes();
19592 onClick: function(e) {
19593 e.stopPropagation();
19594 e.preventDefault();
19597 picker : function()
19599 return this.el.select('.datepicker', true).first();
19602 fillTime: function()
19604 var time = this.pop.select('tbody', true).first();
19606 time.dom.innerHTML = '';
19621 cls: 'hours-up glyphicon glyphicon-chevron-up'
19641 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19662 cls: 'timepicker-hour',
19677 cls: 'timepicker-minute',
19692 cls: 'btn btn-primary period',
19714 cls: 'hours-down glyphicon glyphicon-chevron-down'
19734 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19752 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19759 var hours = this.time.getHours();
19760 var minutes = this.time.getMinutes();
19773 hours = hours - 12;
19777 hours = '0' + hours;
19781 minutes = '0' + minutes;
19784 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19785 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19786 this.pop.select('button', true).first().dom.innerHTML = period;
19792 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19794 var cls = ['bottom'];
19796 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19803 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19808 this.picker().addClass(cls.join('-'));
19812 Roo.each(cls, function(c){
19814 _this.picker().setTop(_this.inputEl().getHeight());
19818 _this.picker().setTop(0 - _this.picker().getHeight());
19823 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19827 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19834 onFocus : function()
19836 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19840 onBlur : function()
19842 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19848 this.picker().show();
19853 this.fireEvent('show', this, this.date);
19858 this.picker().hide();
19861 this.fireEvent('hide', this, this.date);
19864 setTime : function()
19867 this.setValue(this.time.format(this.format));
19869 this.fireEvent('select', this, this.date);
19874 onMousedown: function(e){
19875 e.stopPropagation();
19876 e.preventDefault();
19879 onIncrementHours: function()
19881 Roo.log('onIncrementHours');
19882 this.time = this.time.add(Date.HOUR, 1);
19887 onDecrementHours: function()
19889 Roo.log('onDecrementHours');
19890 this.time = this.time.add(Date.HOUR, -1);
19894 onIncrementMinutes: function()
19896 Roo.log('onIncrementMinutes');
19897 this.time = this.time.add(Date.MINUTE, 1);
19901 onDecrementMinutes: function()
19903 Roo.log('onDecrementMinutes');
19904 this.time = this.time.add(Date.MINUTE, -1);
19908 onTogglePeriod: function()
19910 Roo.log('onTogglePeriod');
19911 this.time = this.time.add(Date.HOUR, 12);
19918 Roo.apply(Roo.bootstrap.TimeField, {
19948 cls: 'btn btn-info ok',
19960 Roo.apply(Roo.bootstrap.TimeField, {
19964 cls: 'datepicker dropdown-menu',
19968 cls: 'datepicker-time',
19972 cls: 'table-condensed',
19974 Roo.bootstrap.TimeField.content,
19975 Roo.bootstrap.TimeField.footer
19994 * @class Roo.bootstrap.MonthField
19995 * @extends Roo.bootstrap.Input
19996 * Bootstrap MonthField class
19998 * @cfg {String} language default en
20001 * Create a new MonthField
20002 * @param {Object} config The config object
20005 Roo.bootstrap.MonthField = function(config){
20006 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20011 * Fires when this field show.
20012 * @param {Roo.bootstrap.MonthField} this
20013 * @param {Mixed} date The date value
20018 * Fires when this field hide.
20019 * @param {Roo.bootstrap.MonthField} this
20020 * @param {Mixed} date The date value
20025 * Fires when select a date.
20026 * @param {Roo.bootstrap.MonthField} this
20027 * @param {String} oldvalue The old value
20028 * @param {String} newvalue The new value
20034 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20036 onRender: function(ct, position)
20039 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20041 this.language = this.language || 'en';
20042 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20043 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20045 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20046 this.isInline = false;
20047 this.isInput = true;
20048 this.component = this.el.select('.add-on', true).first() || false;
20049 this.component = (this.component && this.component.length === 0) ? false : this.component;
20050 this.hasInput = this.component && this.inputEL().length;
20052 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20054 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20056 this.picker().on('mousedown', this.onMousedown, this);
20057 this.picker().on('click', this.onClick, this);
20059 this.picker().addClass('datepicker-dropdown');
20061 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20062 v.setStyle('width', '189px');
20069 if(this.isInline) {
20075 setValue: function(v, suppressEvent)
20077 var o = this.getValue();
20079 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20083 if(suppressEvent !== true){
20084 this.fireEvent('select', this, o, v);
20089 getValue: function()
20094 onClick: function(e)
20096 e.stopPropagation();
20097 e.preventDefault();
20099 var target = e.getTarget();
20101 if(target.nodeName.toLowerCase() === 'i'){
20102 target = Roo.get(target).dom.parentNode;
20105 var nodeName = target.nodeName;
20106 var className = target.className;
20107 var html = target.innerHTML;
20109 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20113 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20115 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20121 picker : function()
20123 return this.pickerEl;
20126 fillMonths: function()
20129 var months = this.picker().select('>.datepicker-months td', true).first();
20131 months.dom.innerHTML = '';
20137 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20140 months.createChild(month);
20149 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20150 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20153 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20154 e.removeClass('active');
20156 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20157 e.addClass('active');
20164 if(this.isInline) {
20168 this.picker().removeClass(['bottom', 'top']);
20170 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20172 * place to the top of element!
20176 this.picker().addClass('top');
20177 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20182 this.picker().addClass('bottom');
20184 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20187 onFocus : function()
20189 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20193 onBlur : function()
20195 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20197 var d = this.inputEl().getValue();
20206 this.picker().show();
20207 this.picker().select('>.datepicker-months', true).first().show();
20211 this.fireEvent('show', this, this.date);
20216 if(this.isInline) {
20219 this.picker().hide();
20220 this.fireEvent('hide', this, this.date);
20224 onMousedown: function(e)
20226 e.stopPropagation();
20227 e.preventDefault();
20232 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20236 fireKey: function(e)
20238 if (!this.picker().isVisible()){
20239 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20250 e.preventDefault();
20254 dir = e.keyCode == 37 ? -1 : 1;
20256 this.vIndex = this.vIndex + dir;
20258 if(this.vIndex < 0){
20262 if(this.vIndex > 11){
20266 if(isNaN(this.vIndex)){
20270 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20276 dir = e.keyCode == 38 ? -1 : 1;
20278 this.vIndex = this.vIndex + dir * 4;
20280 if(this.vIndex < 0){
20284 if(this.vIndex > 11){
20288 if(isNaN(this.vIndex)){
20292 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20297 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20298 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20302 e.preventDefault();
20305 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20306 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20322 this.picker().remove();
20327 Roo.apply(Roo.bootstrap.MonthField, {
20346 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20347 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20352 Roo.apply(Roo.bootstrap.MonthField, {
20356 cls: 'datepicker dropdown-menu roo-dynamic',
20360 cls: 'datepicker-months',
20364 cls: 'table-condensed',
20366 Roo.bootstrap.DateField.content
20386 * @class Roo.bootstrap.CheckBox
20387 * @extends Roo.bootstrap.Input
20388 * Bootstrap CheckBox class
20390 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20391 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20392 * @cfg {String} boxLabel The text that appears beside the checkbox
20393 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20394 * @cfg {Boolean} checked initnal the element
20395 * @cfg {Boolean} inline inline the element (default false)
20396 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20397 * @cfg {String} tooltip label tooltip
20400 * Create a new CheckBox
20401 * @param {Object} config The config object
20404 Roo.bootstrap.CheckBox = function(config){
20405 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20410 * Fires when the element is checked or unchecked.
20411 * @param {Roo.bootstrap.CheckBox} this This input
20412 * @param {Boolean} checked The new checked value
20417 * Fires when the element is click.
20418 * @param {Roo.bootstrap.CheckBox} this This input
20425 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20427 inputType: 'checkbox',
20436 getAutoCreate : function()
20438 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20444 cfg.cls = 'form-group ' + this.inputType; //input-group
20447 cfg.cls += ' ' + this.inputType + '-inline';
20453 type : this.inputType,
20454 value : this.inputValue,
20455 cls : 'roo-' + this.inputType, //'form-box',
20456 placeholder : this.placeholder || ''
20460 if(this.inputType != 'radio'){
20464 cls : 'roo-hidden-value',
20465 value : this.checked ? this.inputValue : this.valueOff
20470 if (this.weight) { // Validity check?
20471 cfg.cls += " " + this.inputType + "-" + this.weight;
20474 if (this.disabled) {
20475 input.disabled=true;
20479 input.checked = this.checked;
20484 input.name = this.name;
20486 if(this.inputType != 'radio'){
20487 hidden.name = this.name;
20488 input.name = '_hidden_' + this.name;
20493 input.cls += ' input-' + this.size;
20498 ['xs','sm','md','lg'].map(function(size){
20499 if (settings[size]) {
20500 cfg.cls += ' col-' + size + '-' + settings[size];
20504 var inputblock = input;
20506 if (this.before || this.after) {
20509 cls : 'input-group',
20514 inputblock.cn.push({
20516 cls : 'input-group-addon',
20521 inputblock.cn.push(input);
20523 if(this.inputType != 'radio'){
20524 inputblock.cn.push(hidden);
20528 inputblock.cn.push({
20530 cls : 'input-group-addon',
20537 if (align ==='left' && this.fieldLabel.length) {
20538 // Roo.log("left and has label");
20543 cls : 'control-label',
20544 html : this.fieldLabel
20554 if(this.labelWidth > 12){
20555 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20558 if(this.labelWidth < 13 && this.labelmd == 0){
20559 this.labelmd = this.labelWidth;
20562 if(this.labellg > 0){
20563 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20564 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20567 if(this.labelmd > 0){
20568 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20569 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20572 if(this.labelsm > 0){
20573 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20574 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20577 if(this.labelxs > 0){
20578 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20579 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20582 } else if ( this.fieldLabel.length) {
20583 // Roo.log(" label");
20587 tag: this.boxLabel ? 'span' : 'label',
20589 cls: 'control-label box-input-label',
20590 //cls : 'input-group-addon',
20591 html : this.fieldLabel
20600 // Roo.log(" no label && no align");
20601 cfg.cn = [ inputblock ] ;
20607 var boxLabelCfg = {
20609 //'for': id, // box label is handled by onclick - so no for...
20611 html: this.boxLabel
20615 boxLabelCfg.tooltip = this.tooltip;
20618 cfg.cn.push(boxLabelCfg);
20621 if(this.inputType != 'radio'){
20622 cfg.cn.push(hidden);
20630 * return the real input element.
20632 inputEl: function ()
20634 return this.el.select('input.roo-' + this.inputType,true).first();
20636 hiddenEl: function ()
20638 return this.el.select('input.roo-hidden-value',true).first();
20641 labelEl: function()
20643 return this.el.select('label.control-label',true).first();
20645 /* depricated... */
20649 return this.labelEl();
20652 boxLabelEl: function()
20654 return this.el.select('label.box-label',true).first();
20657 initEvents : function()
20659 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20661 this.inputEl().on('click', this.onClick, this);
20663 if (this.boxLabel) {
20664 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20667 this.startValue = this.getValue();
20670 Roo.bootstrap.CheckBox.register(this);
20674 onClick : function(e)
20676 if(this.fireEvent('click', this, e) !== false){
20677 this.setChecked(!this.checked);
20682 setChecked : function(state,suppressEvent)
20684 this.startValue = this.getValue();
20686 if(this.inputType == 'radio'){
20688 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20689 e.dom.checked = false;
20692 this.inputEl().dom.checked = true;
20694 this.inputEl().dom.value = this.inputValue;
20696 if(suppressEvent !== true){
20697 this.fireEvent('check', this, true);
20705 this.checked = state;
20707 this.inputEl().dom.checked = state;
20710 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20712 if(suppressEvent !== true){
20713 this.fireEvent('check', this, state);
20719 getValue : function()
20721 if(this.inputType == 'radio'){
20722 return this.getGroupValue();
20725 return this.hiddenEl().dom.value;
20729 getGroupValue : function()
20731 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20735 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20738 setValue : function(v,suppressEvent)
20740 if(this.inputType == 'radio'){
20741 this.setGroupValue(v, suppressEvent);
20745 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20750 setGroupValue : function(v, suppressEvent)
20752 this.startValue = this.getValue();
20754 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20755 e.dom.checked = false;
20757 if(e.dom.value == v){
20758 e.dom.checked = true;
20762 if(suppressEvent !== true){
20763 this.fireEvent('check', this, true);
20771 validate : function()
20773 if(this.getVisibilityEl().hasClass('hidden')){
20779 (this.inputType == 'radio' && this.validateRadio()) ||
20780 (this.inputType == 'checkbox' && this.validateCheckbox())
20786 this.markInvalid();
20790 validateRadio : function()
20792 if(this.getVisibilityEl().hasClass('hidden')){
20796 if(this.allowBlank){
20802 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20803 if(!e.dom.checked){
20815 validateCheckbox : function()
20818 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20819 //return (this.getValue() == this.inputValue) ? true : false;
20822 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20830 for(var i in group){
20831 if(group[i].el.isVisible(true)){
20839 for(var i in group){
20844 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20851 * Mark this field as valid
20853 markValid : function()
20857 this.fireEvent('valid', this);
20859 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20862 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20869 if(this.inputType == 'radio'){
20870 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20871 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20872 e.findParent('.form-group', false, true).addClass(_this.validClass);
20879 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20880 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20884 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20890 for(var i in group){
20891 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20892 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20897 * Mark this field as invalid
20898 * @param {String} msg The validation message
20900 markInvalid : function(msg)
20902 if(this.allowBlank){
20908 this.fireEvent('invalid', this, msg);
20910 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20913 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20917 label.markInvalid();
20920 if(this.inputType == 'radio'){
20921 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20922 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20923 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20930 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20931 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20935 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20941 for(var i in group){
20942 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20943 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20948 clearInvalid : function()
20950 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20952 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20954 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20956 if (label && label.iconEl) {
20957 label.iconEl.removeClass(label.validClass);
20958 label.iconEl.removeClass(label.invalidClass);
20962 disable : function()
20964 if(this.inputType != 'radio'){
20965 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20972 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20973 _this.getActionEl().addClass(this.disabledClass);
20974 e.dom.disabled = true;
20978 this.disabled = true;
20979 this.fireEvent("disable", this);
20983 enable : function()
20985 if(this.inputType != 'radio'){
20986 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20993 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20994 _this.getActionEl().removeClass(this.disabledClass);
20995 e.dom.disabled = false;
20999 this.disabled = false;
21000 this.fireEvent("enable", this);
21004 setBoxLabel : function(v)
21009 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21015 Roo.apply(Roo.bootstrap.CheckBox, {
21020 * register a CheckBox Group
21021 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21023 register : function(checkbox)
21025 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21026 this.groups[checkbox.groupId] = {};
21029 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21033 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21037 * fetch a CheckBox Group based on the group ID
21038 * @param {string} the group ID
21039 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21041 get: function(groupId) {
21042 if (typeof(this.groups[groupId]) == 'undefined') {
21046 return this.groups[groupId] ;
21059 * @class Roo.bootstrap.Radio
21060 * @extends Roo.bootstrap.Component
21061 * Bootstrap Radio class
21062 * @cfg {String} boxLabel - the label associated
21063 * @cfg {String} value - the value of radio
21066 * Create a new Radio
21067 * @param {Object} config The config object
21069 Roo.bootstrap.Radio = function(config){
21070 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21074 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21080 getAutoCreate : function()
21084 cls : 'form-group radio',
21089 html : this.boxLabel
21097 initEvents : function()
21099 this.parent().register(this);
21101 this.el.on('click', this.onClick, this);
21105 onClick : function(e)
21107 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21108 this.setChecked(true);
21112 setChecked : function(state, suppressEvent)
21114 this.parent().setValue(this.value, suppressEvent);
21118 setBoxLabel : function(v)
21123 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21138 * @class Roo.bootstrap.SecurePass
21139 * @extends Roo.bootstrap.Input
21140 * Bootstrap SecurePass class
21144 * Create a new SecurePass
21145 * @param {Object} config The config object
21148 Roo.bootstrap.SecurePass = function (config) {
21149 // these go here, so the translation tool can replace them..
21151 PwdEmpty: "Please type a password, and then retype it to confirm.",
21152 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21153 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21154 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21155 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21156 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21157 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21158 TooWeak: "Your password is Too Weak."
21160 this.meterLabel = "Password strength:";
21161 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21162 this.meterClass = [
21163 "roo-password-meter-tooweak",
21164 "roo-password-meter-weak",
21165 "roo-password-meter-medium",
21166 "roo-password-meter-strong",
21167 "roo-password-meter-grey"
21172 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21175 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21177 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21179 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21180 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21181 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21182 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21183 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21184 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21185 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21195 * @cfg {String/Object} Label for the strength meter (defaults to
21196 * 'Password strength:')
21201 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21202 * ['Weak', 'Medium', 'Strong'])
21205 pwdStrengths: false,
21218 initEvents: function ()
21220 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21222 if (this.el.is('input[type=password]') && Roo.isSafari) {
21223 this.el.on('keydown', this.SafariOnKeyDown, this);
21226 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21229 onRender: function (ct, position)
21231 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21232 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21233 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21235 this.trigger.createChild({
21240 cls: 'roo-password-meter-grey col-xs-12',
21243 //width: this.meterWidth + 'px'
21247 cls: 'roo-password-meter-text'
21253 if (this.hideTrigger) {
21254 this.trigger.setDisplayed(false);
21256 this.setSize(this.width || '', this.height || '');
21259 onDestroy: function ()
21261 if (this.trigger) {
21262 this.trigger.removeAllListeners();
21263 this.trigger.remove();
21266 this.wrap.remove();
21268 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21271 checkStrength: function ()
21273 var pwd = this.inputEl().getValue();
21274 if (pwd == this._lastPwd) {
21279 if (this.ClientSideStrongPassword(pwd)) {
21281 } else if (this.ClientSideMediumPassword(pwd)) {
21283 } else if (this.ClientSideWeakPassword(pwd)) {
21289 Roo.log('strength1: ' + strength);
21291 //var pm = this.trigger.child('div/div/div').dom;
21292 var pm = this.trigger.child('div/div');
21293 pm.removeClass(this.meterClass);
21294 pm.addClass(this.meterClass[strength]);
21297 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21299 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21301 this._lastPwd = pwd;
21305 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21307 this._lastPwd = '';
21309 var pm = this.trigger.child('div/div');
21310 pm.removeClass(this.meterClass);
21311 pm.addClass('roo-password-meter-grey');
21314 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21317 this.inputEl().dom.type='password';
21320 validateValue: function (value)
21323 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21326 if (value.length == 0) {
21327 if (this.allowBlank) {
21328 this.clearInvalid();
21332 this.markInvalid(this.errors.PwdEmpty);
21333 this.errorMsg = this.errors.PwdEmpty;
21341 if ('[\x21-\x7e]*'.match(value)) {
21342 this.markInvalid(this.errors.PwdBadChar);
21343 this.errorMsg = this.errors.PwdBadChar;
21346 if (value.length < 6) {
21347 this.markInvalid(this.errors.PwdShort);
21348 this.errorMsg = this.errors.PwdShort;
21351 if (value.length > 16) {
21352 this.markInvalid(this.errors.PwdLong);
21353 this.errorMsg = this.errors.PwdLong;
21357 if (this.ClientSideStrongPassword(value)) {
21359 } else if (this.ClientSideMediumPassword(value)) {
21361 } else if (this.ClientSideWeakPassword(value)) {
21368 if (strength < 2) {
21369 //this.markInvalid(this.errors.TooWeak);
21370 this.errorMsg = this.errors.TooWeak;
21375 console.log('strength2: ' + strength);
21377 //var pm = this.trigger.child('div/div/div').dom;
21379 var pm = this.trigger.child('div/div');
21380 pm.removeClass(this.meterClass);
21381 pm.addClass(this.meterClass[strength]);
21383 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21385 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21387 this.errorMsg = '';
21391 CharacterSetChecks: function (type)
21394 this.fResult = false;
21397 isctype: function (character, type)
21400 case this.kCapitalLetter:
21401 if (character >= 'A' && character <= 'Z') {
21406 case this.kSmallLetter:
21407 if (character >= 'a' && character <= 'z') {
21413 if (character >= '0' && character <= '9') {
21418 case this.kPunctuation:
21419 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21430 IsLongEnough: function (pwd, size)
21432 return !(pwd == null || isNaN(size) || pwd.length < size);
21435 SpansEnoughCharacterSets: function (word, nb)
21437 if (!this.IsLongEnough(word, nb))
21442 var characterSetChecks = new Array(
21443 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21444 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21447 for (var index = 0; index < word.length; ++index) {
21448 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21449 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21450 characterSetChecks[nCharSet].fResult = true;
21457 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21458 if (characterSetChecks[nCharSet].fResult) {
21463 if (nCharSets < nb) {
21469 ClientSideStrongPassword: function (pwd)
21471 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21474 ClientSideMediumPassword: function (pwd)
21476 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21479 ClientSideWeakPassword: function (pwd)
21481 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21484 })//<script type="text/javascript">
21487 * Based Ext JS Library 1.1.1
21488 * Copyright(c) 2006-2007, Ext JS, LLC.
21494 * @class Roo.HtmlEditorCore
21495 * @extends Roo.Component
21496 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21498 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21501 Roo.HtmlEditorCore = function(config){
21504 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21509 * @event initialize
21510 * Fires when the editor is fully initialized (including the iframe)
21511 * @param {Roo.HtmlEditorCore} this
21516 * Fires when the editor is first receives the focus. Any insertion must wait
21517 * until after this event.
21518 * @param {Roo.HtmlEditorCore} this
21522 * @event beforesync
21523 * Fires before the textarea is updated with content from the editor iframe. Return false
21524 * to cancel the sync.
21525 * @param {Roo.HtmlEditorCore} this
21526 * @param {String} html
21530 * @event beforepush
21531 * Fires before the iframe editor is updated with content from the textarea. Return false
21532 * to cancel the push.
21533 * @param {Roo.HtmlEditorCore} this
21534 * @param {String} html
21539 * Fires when the textarea is updated with content from the editor iframe.
21540 * @param {Roo.HtmlEditorCore} this
21541 * @param {String} html
21546 * Fires when the iframe editor is updated with content from the textarea.
21547 * @param {Roo.HtmlEditorCore} this
21548 * @param {String} html
21553 * @event editorevent
21554 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21555 * @param {Roo.HtmlEditorCore} this
21561 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21563 // defaults : white / black...
21564 this.applyBlacklists();
21571 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21575 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21581 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21586 * @cfg {Number} height (in pixels)
21590 * @cfg {Number} width (in pixels)
21595 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21598 stylesheets: false,
21603 // private properties
21604 validationEvent : false,
21606 initialized : false,
21608 sourceEditMode : false,
21609 onFocus : Roo.emptyFn,
21611 hideMode:'offsets',
21615 // blacklist + whitelisted elements..
21622 * Protected method that will not generally be called directly. It
21623 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21624 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21626 getDocMarkup : function(){
21630 // inherit styels from page...??
21631 if (this.stylesheets === false) {
21633 Roo.get(document.head).select('style').each(function(node) {
21634 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21637 Roo.get(document.head).select('link').each(function(node) {
21638 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21641 } else if (!this.stylesheets.length) {
21643 st = '<style type="text/css">' +
21644 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21647 st = '<style type="text/css">' +
21652 st += '<style type="text/css">' +
21653 'IMG { cursor: pointer } ' +
21656 var cls = 'roo-htmleditor-body';
21658 if(this.bodyCls.length){
21659 cls += ' ' + this.bodyCls;
21662 return '<html><head>' + st +
21663 //<style type="text/css">' +
21664 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21666 ' </head><body class="' + cls + '"></body></html>';
21670 onRender : function(ct, position)
21673 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21674 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21677 this.el.dom.style.border = '0 none';
21678 this.el.dom.setAttribute('tabIndex', -1);
21679 this.el.addClass('x-hidden hide');
21683 if(Roo.isIE){ // fix IE 1px bogus margin
21684 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21688 this.frameId = Roo.id();
21692 var iframe = this.owner.wrap.createChild({
21694 cls: 'form-control', // bootstrap..
21696 name: this.frameId,
21697 frameBorder : 'no',
21698 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21703 this.iframe = iframe.dom;
21705 this.assignDocWin();
21707 this.doc.designMode = 'on';
21710 this.doc.write(this.getDocMarkup());
21714 var task = { // must defer to wait for browser to be ready
21716 //console.log("run task?" + this.doc.readyState);
21717 this.assignDocWin();
21718 if(this.doc.body || this.doc.readyState == 'complete'){
21720 this.doc.designMode="on";
21724 Roo.TaskMgr.stop(task);
21725 this.initEditor.defer(10, this);
21732 Roo.TaskMgr.start(task);
21737 onResize : function(w, h)
21739 Roo.log('resize: ' +w + ',' + h );
21740 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21744 if(typeof w == 'number'){
21746 this.iframe.style.width = w + 'px';
21748 if(typeof h == 'number'){
21750 this.iframe.style.height = h + 'px';
21752 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21759 * Toggles the editor between standard and source edit mode.
21760 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21762 toggleSourceEdit : function(sourceEditMode){
21764 this.sourceEditMode = sourceEditMode === true;
21766 if(this.sourceEditMode){
21768 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21771 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21772 //this.iframe.className = '';
21775 //this.setSize(this.owner.wrap.getSize());
21776 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21783 * Protected method that will not generally be called directly. If you need/want
21784 * custom HTML cleanup, this is the method you should override.
21785 * @param {String} html The HTML to be cleaned
21786 * return {String} The cleaned HTML
21788 cleanHtml : function(html){
21789 html = String(html);
21790 if(html.length > 5){
21791 if(Roo.isSafari){ // strip safari nonsense
21792 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21795 if(html == ' '){
21802 * HTML Editor -> Textarea
21803 * Protected method that will not generally be called directly. Syncs the contents
21804 * of the editor iframe with the textarea.
21806 syncValue : function(){
21807 if(this.initialized){
21808 var bd = (this.doc.body || this.doc.documentElement);
21809 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21810 var html = bd.innerHTML;
21812 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21813 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21815 html = '<div style="'+m[0]+'">' + html + '</div>';
21818 html = this.cleanHtml(html);
21819 // fix up the special chars.. normaly like back quotes in word...
21820 // however we do not want to do this with chinese..
21821 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21822 var cc = b.charCodeAt();
21824 (cc >= 0x4E00 && cc < 0xA000 ) ||
21825 (cc >= 0x3400 && cc < 0x4E00 ) ||
21826 (cc >= 0xf900 && cc < 0xfb00 )
21832 if(this.owner.fireEvent('beforesync', this, html) !== false){
21833 this.el.dom.value = html;
21834 this.owner.fireEvent('sync', this, html);
21840 * Protected method that will not generally be called directly. Pushes the value of the textarea
21841 * into the iframe editor.
21843 pushValue : function(){
21844 if(this.initialized){
21845 var v = this.el.dom.value.trim();
21847 // if(v.length < 1){
21851 if(this.owner.fireEvent('beforepush', this, v) !== false){
21852 var d = (this.doc.body || this.doc.documentElement);
21854 this.cleanUpPaste();
21855 this.el.dom.value = d.innerHTML;
21856 this.owner.fireEvent('push', this, v);
21862 deferFocus : function(){
21863 this.focus.defer(10, this);
21867 focus : function(){
21868 if(this.win && !this.sourceEditMode){
21875 assignDocWin: function()
21877 var iframe = this.iframe;
21880 this.doc = iframe.contentWindow.document;
21881 this.win = iframe.contentWindow;
21883 // if (!Roo.get(this.frameId)) {
21886 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21887 // this.win = Roo.get(this.frameId).dom.contentWindow;
21889 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21893 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21894 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21899 initEditor : function(){
21900 //console.log("INIT EDITOR");
21901 this.assignDocWin();
21905 this.doc.designMode="on";
21907 this.doc.write(this.getDocMarkup());
21910 var dbody = (this.doc.body || this.doc.documentElement);
21911 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21912 // this copies styles from the containing element into thsi one..
21913 // not sure why we need all of this..
21914 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21916 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21917 //ss['background-attachment'] = 'fixed'; // w3c
21918 dbody.bgProperties = 'fixed'; // ie
21919 //Roo.DomHelper.applyStyles(dbody, ss);
21920 Roo.EventManager.on(this.doc, {
21921 //'mousedown': this.onEditorEvent,
21922 'mouseup': this.onEditorEvent,
21923 'dblclick': this.onEditorEvent,
21924 'click': this.onEditorEvent,
21925 'keyup': this.onEditorEvent,
21930 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21932 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21933 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21935 this.initialized = true;
21937 this.owner.fireEvent('initialize', this);
21942 onDestroy : function(){
21948 //for (var i =0; i < this.toolbars.length;i++) {
21949 // // fixme - ask toolbars for heights?
21950 // this.toolbars[i].onDestroy();
21953 //this.wrap.dom.innerHTML = '';
21954 //this.wrap.remove();
21959 onFirstFocus : function(){
21961 this.assignDocWin();
21964 this.activated = true;
21967 if(Roo.isGecko){ // prevent silly gecko errors
21969 var s = this.win.getSelection();
21970 if(!s.focusNode || s.focusNode.nodeType != 3){
21971 var r = s.getRangeAt(0);
21972 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21977 this.execCmd('useCSS', true);
21978 this.execCmd('styleWithCSS', false);
21981 this.owner.fireEvent('activate', this);
21985 adjustFont: function(btn){
21986 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21987 //if(Roo.isSafari){ // safari
21990 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21991 if(Roo.isSafari){ // safari
21992 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21993 v = (v < 10) ? 10 : v;
21994 v = (v > 48) ? 48 : v;
21995 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22000 v = Math.max(1, v+adjust);
22002 this.execCmd('FontSize', v );
22005 onEditorEvent : function(e)
22007 this.owner.fireEvent('editorevent', this, e);
22008 // this.updateToolbar();
22009 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22012 insertTag : function(tg)
22014 // could be a bit smarter... -> wrap the current selected tRoo..
22015 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22017 range = this.createRange(this.getSelection());
22018 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22019 wrappingNode.appendChild(range.extractContents());
22020 range.insertNode(wrappingNode);
22027 this.execCmd("formatblock", tg);
22031 insertText : function(txt)
22035 var range = this.createRange();
22036 range.deleteContents();
22037 //alert(Sender.getAttribute('label'));
22039 range.insertNode(this.doc.createTextNode(txt));
22045 * Executes a Midas editor command on the editor document and performs necessary focus and
22046 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22047 * @param {String} cmd The Midas command
22048 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22050 relayCmd : function(cmd, value){
22052 this.execCmd(cmd, value);
22053 this.owner.fireEvent('editorevent', this);
22054 //this.updateToolbar();
22055 this.owner.deferFocus();
22059 * Executes a Midas editor command directly on the editor document.
22060 * For visual commands, you should use {@link #relayCmd} instead.
22061 * <b>This should only be called after the editor is initialized.</b>
22062 * @param {String} cmd The Midas command
22063 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22065 execCmd : function(cmd, value){
22066 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22073 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22075 * @param {String} text | dom node..
22077 insertAtCursor : function(text)
22080 if(!this.activated){
22086 var r = this.doc.selection.createRange();
22097 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22101 // from jquery ui (MIT licenced)
22103 var win = this.win;
22105 if (win.getSelection && win.getSelection().getRangeAt) {
22106 range = win.getSelection().getRangeAt(0);
22107 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22108 range.insertNode(node);
22109 } else if (win.document.selection && win.document.selection.createRange) {
22110 // no firefox support
22111 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22112 win.document.selection.createRange().pasteHTML(txt);
22114 // no firefox support
22115 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22116 this.execCmd('InsertHTML', txt);
22125 mozKeyPress : function(e){
22127 var c = e.getCharCode(), cmd;
22130 c = String.fromCharCode(c).toLowerCase();
22144 this.cleanUpPaste.defer(100, this);
22152 e.preventDefault();
22160 fixKeys : function(){ // load time branching for fastest keydown performance
22162 return function(e){
22163 var k = e.getKey(), r;
22166 r = this.doc.selection.createRange();
22169 r.pasteHTML('    ');
22176 r = this.doc.selection.createRange();
22178 var target = r.parentElement();
22179 if(!target || target.tagName.toLowerCase() != 'li'){
22181 r.pasteHTML('<br />');
22187 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22188 this.cleanUpPaste.defer(100, this);
22194 }else if(Roo.isOpera){
22195 return function(e){
22196 var k = e.getKey();
22200 this.execCmd('InsertHTML','    ');
22203 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22204 this.cleanUpPaste.defer(100, this);
22209 }else if(Roo.isSafari){
22210 return function(e){
22211 var k = e.getKey();
22215 this.execCmd('InsertText','\t');
22219 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22220 this.cleanUpPaste.defer(100, this);
22228 getAllAncestors: function()
22230 var p = this.getSelectedNode();
22233 a.push(p); // push blank onto stack..
22234 p = this.getParentElement();
22238 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22242 a.push(this.doc.body);
22246 lastSelNode : false,
22249 getSelection : function()
22251 this.assignDocWin();
22252 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22255 getSelectedNode: function()
22257 // this may only work on Gecko!!!
22259 // should we cache this!!!!
22264 var range = this.createRange(this.getSelection()).cloneRange();
22267 var parent = range.parentElement();
22269 var testRange = range.duplicate();
22270 testRange.moveToElementText(parent);
22271 if (testRange.inRange(range)) {
22274 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22277 parent = parent.parentElement;
22282 // is ancestor a text element.
22283 var ac = range.commonAncestorContainer;
22284 if (ac.nodeType == 3) {
22285 ac = ac.parentNode;
22288 var ar = ac.childNodes;
22291 var other_nodes = [];
22292 var has_other_nodes = false;
22293 for (var i=0;i<ar.length;i++) {
22294 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22297 // fullly contained node.
22299 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22304 // probably selected..
22305 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22306 other_nodes.push(ar[i]);
22310 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22315 has_other_nodes = true;
22317 if (!nodes.length && other_nodes.length) {
22318 nodes= other_nodes;
22320 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22326 createRange: function(sel)
22328 // this has strange effects when using with
22329 // top toolbar - not sure if it's a great idea.
22330 //this.editor.contentWindow.focus();
22331 if (typeof sel != "undefined") {
22333 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22335 return this.doc.createRange();
22338 return this.doc.createRange();
22341 getParentElement: function()
22344 this.assignDocWin();
22345 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22347 var range = this.createRange(sel);
22350 var p = range.commonAncestorContainer;
22351 while (p.nodeType == 3) { // text node
22362 * Range intersection.. the hard stuff...
22366 * [ -- selected range --- ]
22370 * if end is before start or hits it. fail.
22371 * if start is after end or hits it fail.
22373 * if either hits (but other is outside. - then it's not
22379 // @see http://www.thismuchiknow.co.uk/?p=64.
22380 rangeIntersectsNode : function(range, node)
22382 var nodeRange = node.ownerDocument.createRange();
22384 nodeRange.selectNode(node);
22386 nodeRange.selectNodeContents(node);
22389 var rangeStartRange = range.cloneRange();
22390 rangeStartRange.collapse(true);
22392 var rangeEndRange = range.cloneRange();
22393 rangeEndRange.collapse(false);
22395 var nodeStartRange = nodeRange.cloneRange();
22396 nodeStartRange.collapse(true);
22398 var nodeEndRange = nodeRange.cloneRange();
22399 nodeEndRange.collapse(false);
22401 return rangeStartRange.compareBoundaryPoints(
22402 Range.START_TO_START, nodeEndRange) == -1 &&
22403 rangeEndRange.compareBoundaryPoints(
22404 Range.START_TO_START, nodeStartRange) == 1;
22408 rangeCompareNode : function(range, node)
22410 var nodeRange = node.ownerDocument.createRange();
22412 nodeRange.selectNode(node);
22414 nodeRange.selectNodeContents(node);
22418 range.collapse(true);
22420 nodeRange.collapse(true);
22422 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22423 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22425 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22427 var nodeIsBefore = ss == 1;
22428 var nodeIsAfter = ee == -1;
22430 if (nodeIsBefore && nodeIsAfter) {
22433 if (!nodeIsBefore && nodeIsAfter) {
22434 return 1; //right trailed.
22437 if (nodeIsBefore && !nodeIsAfter) {
22438 return 2; // left trailed.
22444 // private? - in a new class?
22445 cleanUpPaste : function()
22447 // cleans up the whole document..
22448 Roo.log('cleanuppaste');
22450 this.cleanUpChildren(this.doc.body);
22451 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22452 if (clean != this.doc.body.innerHTML) {
22453 this.doc.body.innerHTML = clean;
22458 cleanWordChars : function(input) {// change the chars to hex code
22459 var he = Roo.HtmlEditorCore;
22461 var output = input;
22462 Roo.each(he.swapCodes, function(sw) {
22463 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22465 output = output.replace(swapper, sw[1]);
22472 cleanUpChildren : function (n)
22474 if (!n.childNodes.length) {
22477 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22478 this.cleanUpChild(n.childNodes[i]);
22485 cleanUpChild : function (node)
22488 //console.log(node);
22489 if (node.nodeName == "#text") {
22490 // clean up silly Windows -- stuff?
22493 if (node.nodeName == "#comment") {
22494 node.parentNode.removeChild(node);
22495 // clean up silly Windows -- stuff?
22498 var lcname = node.tagName.toLowerCase();
22499 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22500 // whitelist of tags..
22502 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22504 node.parentNode.removeChild(node);
22509 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22511 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22512 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22514 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22515 // remove_keep_children = true;
22518 if (remove_keep_children) {
22519 this.cleanUpChildren(node);
22520 // inserts everything just before this node...
22521 while (node.childNodes.length) {
22522 var cn = node.childNodes[0];
22523 node.removeChild(cn);
22524 node.parentNode.insertBefore(cn, node);
22526 node.parentNode.removeChild(node);
22530 if (!node.attributes || !node.attributes.length) {
22531 this.cleanUpChildren(node);
22535 function cleanAttr(n,v)
22538 if (v.match(/^\./) || v.match(/^\//)) {
22541 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22544 if (v.match(/^#/)) {
22547 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22548 node.removeAttribute(n);
22552 var cwhite = this.cwhite;
22553 var cblack = this.cblack;
22555 function cleanStyle(n,v)
22557 if (v.match(/expression/)) { //XSS?? should we even bother..
22558 node.removeAttribute(n);
22562 var parts = v.split(/;/);
22565 Roo.each(parts, function(p) {
22566 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22570 var l = p.split(':').shift().replace(/\s+/g,'');
22571 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22573 if ( cwhite.length && cblack.indexOf(l) > -1) {
22574 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22575 //node.removeAttribute(n);
22579 // only allow 'c whitelisted system attributes'
22580 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22581 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22582 //node.removeAttribute(n);
22592 if (clean.length) {
22593 node.setAttribute(n, clean.join(';'));
22595 node.removeAttribute(n);
22601 for (var i = node.attributes.length-1; i > -1 ; i--) {
22602 var a = node.attributes[i];
22605 if (a.name.toLowerCase().substr(0,2)=='on') {
22606 node.removeAttribute(a.name);
22609 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22610 node.removeAttribute(a.name);
22613 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22614 cleanAttr(a.name,a.value); // fixme..
22617 if (a.name == 'style') {
22618 cleanStyle(a.name,a.value);
22621 /// clean up MS crap..
22622 // tecnically this should be a list of valid class'es..
22625 if (a.name == 'class') {
22626 if (a.value.match(/^Mso/)) {
22627 node.className = '';
22630 if (a.value.match(/^body$/)) {
22631 node.className = '';
22642 this.cleanUpChildren(node);
22648 * Clean up MS wordisms...
22650 cleanWord : function(node)
22655 this.cleanWord(this.doc.body);
22658 if (node.nodeName == "#text") {
22659 // clean up silly Windows -- stuff?
22662 if (node.nodeName == "#comment") {
22663 node.parentNode.removeChild(node);
22664 // clean up silly Windows -- stuff?
22668 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22669 node.parentNode.removeChild(node);
22673 // remove - but keep children..
22674 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22675 while (node.childNodes.length) {
22676 var cn = node.childNodes[0];
22677 node.removeChild(cn);
22678 node.parentNode.insertBefore(cn, node);
22680 node.parentNode.removeChild(node);
22681 this.iterateChildren(node, this.cleanWord);
22685 if (node.className.length) {
22687 var cn = node.className.split(/\W+/);
22689 Roo.each(cn, function(cls) {
22690 if (cls.match(/Mso[a-zA-Z]+/)) {
22695 node.className = cna.length ? cna.join(' ') : '';
22697 node.removeAttribute("class");
22701 if (node.hasAttribute("lang")) {
22702 node.removeAttribute("lang");
22705 if (node.hasAttribute("style")) {
22707 var styles = node.getAttribute("style").split(";");
22709 Roo.each(styles, function(s) {
22710 if (!s.match(/:/)) {
22713 var kv = s.split(":");
22714 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22717 // what ever is left... we allow.
22720 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22721 if (!nstyle.length) {
22722 node.removeAttribute('style');
22725 this.iterateChildren(node, this.cleanWord);
22731 * iterateChildren of a Node, calling fn each time, using this as the scole..
22732 * @param {DomNode} node node to iterate children of.
22733 * @param {Function} fn method of this class to call on each item.
22735 iterateChildren : function(node, fn)
22737 if (!node.childNodes.length) {
22740 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22741 fn.call(this, node.childNodes[i])
22747 * cleanTableWidths.
22749 * Quite often pasting from word etc.. results in tables with column and widths.
22750 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22753 cleanTableWidths : function(node)
22758 this.cleanTableWidths(this.doc.body);
22763 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22766 Roo.log(node.tagName);
22767 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22768 this.iterateChildren(node, this.cleanTableWidths);
22771 if (node.hasAttribute('width')) {
22772 node.removeAttribute('width');
22776 if (node.hasAttribute("style")) {
22779 var styles = node.getAttribute("style").split(";");
22781 Roo.each(styles, function(s) {
22782 if (!s.match(/:/)) {
22785 var kv = s.split(":");
22786 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22789 // what ever is left... we allow.
22792 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22793 if (!nstyle.length) {
22794 node.removeAttribute('style');
22798 this.iterateChildren(node, this.cleanTableWidths);
22806 domToHTML : function(currentElement, depth, nopadtext) {
22808 depth = depth || 0;
22809 nopadtext = nopadtext || false;
22811 if (!currentElement) {
22812 return this.domToHTML(this.doc.body);
22815 //Roo.log(currentElement);
22817 var allText = false;
22818 var nodeName = currentElement.nodeName;
22819 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22821 if (nodeName == '#text') {
22823 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22828 if (nodeName != 'BODY') {
22831 // Prints the node tagName, such as <A>, <IMG>, etc
22834 for(i = 0; i < currentElement.attributes.length;i++) {
22836 var aname = currentElement.attributes.item(i).name;
22837 if (!currentElement.attributes.item(i).value.length) {
22840 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22843 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22852 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22855 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22860 // Traverse the tree
22862 var currentElementChild = currentElement.childNodes.item(i);
22863 var allText = true;
22864 var innerHTML = '';
22866 while (currentElementChild) {
22867 // Formatting code (indent the tree so it looks nice on the screen)
22868 var nopad = nopadtext;
22869 if (lastnode == 'SPAN') {
22873 if (currentElementChild.nodeName == '#text') {
22874 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22875 toadd = nopadtext ? toadd : toadd.trim();
22876 if (!nopad && toadd.length > 80) {
22877 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22879 innerHTML += toadd;
22882 currentElementChild = currentElement.childNodes.item(i);
22888 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22890 // Recursively traverse the tree structure of the child node
22891 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22892 lastnode = currentElementChild.nodeName;
22894 currentElementChild=currentElement.childNodes.item(i);
22900 // The remaining code is mostly for formatting the tree
22901 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22906 ret+= "</"+tagName+">";
22912 applyBlacklists : function()
22914 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22915 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22919 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22920 if (b.indexOf(tag) > -1) {
22923 this.white.push(tag);
22927 Roo.each(w, function(tag) {
22928 if (b.indexOf(tag) > -1) {
22931 if (this.white.indexOf(tag) > -1) {
22934 this.white.push(tag);
22939 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22940 if (w.indexOf(tag) > -1) {
22943 this.black.push(tag);
22947 Roo.each(b, function(tag) {
22948 if (w.indexOf(tag) > -1) {
22951 if (this.black.indexOf(tag) > -1) {
22954 this.black.push(tag);
22959 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22960 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22964 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22965 if (b.indexOf(tag) > -1) {
22968 this.cwhite.push(tag);
22972 Roo.each(w, function(tag) {
22973 if (b.indexOf(tag) > -1) {
22976 if (this.cwhite.indexOf(tag) > -1) {
22979 this.cwhite.push(tag);
22984 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22985 if (w.indexOf(tag) > -1) {
22988 this.cblack.push(tag);
22992 Roo.each(b, function(tag) {
22993 if (w.indexOf(tag) > -1) {
22996 if (this.cblack.indexOf(tag) > -1) {
22999 this.cblack.push(tag);
23004 setStylesheets : function(stylesheets)
23006 if(typeof(stylesheets) == 'string'){
23007 Roo.get(this.iframe.contentDocument.head).createChild({
23009 rel : 'stylesheet',
23018 Roo.each(stylesheets, function(s) {
23023 Roo.get(_this.iframe.contentDocument.head).createChild({
23025 rel : 'stylesheet',
23034 removeStylesheets : function()
23038 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23043 setStyle : function(style)
23045 Roo.get(this.iframe.contentDocument.head).createChild({
23054 // hide stuff that is not compatible
23068 * @event specialkey
23072 * @cfg {String} fieldClass @hide
23075 * @cfg {String} focusClass @hide
23078 * @cfg {String} autoCreate @hide
23081 * @cfg {String} inputType @hide
23084 * @cfg {String} invalidClass @hide
23087 * @cfg {String} invalidText @hide
23090 * @cfg {String} msgFx @hide
23093 * @cfg {String} validateOnBlur @hide
23097 Roo.HtmlEditorCore.white = [
23098 'area', 'br', 'img', 'input', 'hr', 'wbr',
23100 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23101 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23102 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23103 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23104 'table', 'ul', 'xmp',
23106 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23109 'dir', 'menu', 'ol', 'ul', 'dl',
23115 Roo.HtmlEditorCore.black = [
23116 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23118 'base', 'basefont', 'bgsound', 'blink', 'body',
23119 'frame', 'frameset', 'head', 'html', 'ilayer',
23120 'iframe', 'layer', 'link', 'meta', 'object',
23121 'script', 'style' ,'title', 'xml' // clean later..
23123 Roo.HtmlEditorCore.clean = [
23124 'script', 'style', 'title', 'xml'
23126 Roo.HtmlEditorCore.remove = [
23131 Roo.HtmlEditorCore.ablack = [
23135 Roo.HtmlEditorCore.aclean = [
23136 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23140 Roo.HtmlEditorCore.pwhite= [
23141 'http', 'https', 'mailto'
23144 // white listed style attributes.
23145 Roo.HtmlEditorCore.cwhite= [
23146 // 'text-align', /// default is to allow most things..
23152 // black listed style attributes.
23153 Roo.HtmlEditorCore.cblack= [
23154 // 'font-size' -- this can be set by the project
23158 Roo.HtmlEditorCore.swapCodes =[
23177 * @class Roo.bootstrap.HtmlEditor
23178 * @extends Roo.bootstrap.TextArea
23179 * Bootstrap HtmlEditor class
23182 * Create a new HtmlEditor
23183 * @param {Object} config The config object
23186 Roo.bootstrap.HtmlEditor = function(config){
23187 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23188 if (!this.toolbars) {
23189 this.toolbars = [];
23192 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23195 * @event initialize
23196 * Fires when the editor is fully initialized (including the iframe)
23197 * @param {HtmlEditor} this
23202 * Fires when the editor is first receives the focus. Any insertion must wait
23203 * until after this event.
23204 * @param {HtmlEditor} this
23208 * @event beforesync
23209 * Fires before the textarea is updated with content from the editor iframe. Return false
23210 * to cancel the sync.
23211 * @param {HtmlEditor} this
23212 * @param {String} html
23216 * @event beforepush
23217 * Fires before the iframe editor is updated with content from the textarea. Return false
23218 * to cancel the push.
23219 * @param {HtmlEditor} this
23220 * @param {String} html
23225 * Fires when the textarea is updated with content from the editor iframe.
23226 * @param {HtmlEditor} this
23227 * @param {String} html
23232 * Fires when the iframe editor is updated with content from the textarea.
23233 * @param {HtmlEditor} this
23234 * @param {String} html
23238 * @event editmodechange
23239 * Fires when the editor switches edit modes
23240 * @param {HtmlEditor} this
23241 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23243 editmodechange: true,
23245 * @event editorevent
23246 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23247 * @param {HtmlEditor} this
23251 * @event firstfocus
23252 * Fires when on first focus - needed by toolbars..
23253 * @param {HtmlEditor} this
23258 * Auto save the htmlEditor value as a file into Events
23259 * @param {HtmlEditor} this
23263 * @event savedpreview
23264 * preview the saved version of htmlEditor
23265 * @param {HtmlEditor} this
23272 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23276 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23281 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23286 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23291 * @cfg {Number} height (in pixels)
23295 * @cfg {Number} width (in pixels)
23300 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23303 stylesheets: false,
23308 // private properties
23309 validationEvent : false,
23311 initialized : false,
23314 onFocus : Roo.emptyFn,
23316 hideMode:'offsets',
23318 tbContainer : false,
23322 toolbarContainer :function() {
23323 return this.wrap.select('.x-html-editor-tb',true).first();
23327 * Protected method that will not generally be called directly. It
23328 * is called when the editor creates its toolbar. Override this method if you need to
23329 * add custom toolbar buttons.
23330 * @param {HtmlEditor} editor
23332 createToolbar : function(){
23333 Roo.log('renewing');
23334 Roo.log("create toolbars");
23336 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23337 this.toolbars[0].render(this.toolbarContainer());
23341 // if (!editor.toolbars || !editor.toolbars.length) {
23342 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23345 // for (var i =0 ; i < editor.toolbars.length;i++) {
23346 // editor.toolbars[i] = Roo.factory(
23347 // typeof(editor.toolbars[i]) == 'string' ?
23348 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23349 // Roo.bootstrap.HtmlEditor);
23350 // editor.toolbars[i].init(editor);
23356 onRender : function(ct, position)
23358 // Roo.log("Call onRender: " + this.xtype);
23360 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23362 this.wrap = this.inputEl().wrap({
23363 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23366 this.editorcore.onRender(ct, position);
23368 if (this.resizable) {
23369 this.resizeEl = new Roo.Resizable(this.wrap, {
23373 minHeight : this.height,
23374 height: this.height,
23375 handles : this.resizable,
23378 resize : function(r, w, h) {
23379 _t.onResize(w,h); // -something
23385 this.createToolbar(this);
23388 if(!this.width && this.resizable){
23389 this.setSize(this.wrap.getSize());
23391 if (this.resizeEl) {
23392 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23393 // should trigger onReize..
23399 onResize : function(w, h)
23401 Roo.log('resize: ' +w + ',' + h );
23402 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23406 if(this.inputEl() ){
23407 if(typeof w == 'number'){
23408 var aw = w - this.wrap.getFrameWidth('lr');
23409 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23412 if(typeof h == 'number'){
23413 var tbh = -11; // fixme it needs to tool bar size!
23414 for (var i =0; i < this.toolbars.length;i++) {
23415 // fixme - ask toolbars for heights?
23416 tbh += this.toolbars[i].el.getHeight();
23417 //if (this.toolbars[i].footer) {
23418 // tbh += this.toolbars[i].footer.el.getHeight();
23426 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23427 ah -= 5; // knock a few pixes off for look..
23428 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23432 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23433 this.editorcore.onResize(ew,eh);
23438 * Toggles the editor between standard and source edit mode.
23439 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23441 toggleSourceEdit : function(sourceEditMode)
23443 this.editorcore.toggleSourceEdit(sourceEditMode);
23445 if(this.editorcore.sourceEditMode){
23446 Roo.log('editor - showing textarea');
23449 // Roo.log(this.syncValue());
23451 this.inputEl().removeClass(['hide', 'x-hidden']);
23452 this.inputEl().dom.removeAttribute('tabIndex');
23453 this.inputEl().focus();
23455 Roo.log('editor - hiding textarea');
23457 // Roo.log(this.pushValue());
23460 this.inputEl().addClass(['hide', 'x-hidden']);
23461 this.inputEl().dom.setAttribute('tabIndex', -1);
23462 //this.deferFocus();
23465 if(this.resizable){
23466 this.setSize(this.wrap.getSize());
23469 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23472 // private (for BoxComponent)
23473 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23475 // private (for BoxComponent)
23476 getResizeEl : function(){
23480 // private (for BoxComponent)
23481 getPositionEl : function(){
23486 initEvents : function(){
23487 this.originalValue = this.getValue();
23491 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23494 // markInvalid : Roo.emptyFn,
23496 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23499 // clearInvalid : Roo.emptyFn,
23501 setValue : function(v){
23502 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23503 this.editorcore.pushValue();
23508 deferFocus : function(){
23509 this.focus.defer(10, this);
23513 focus : function(){
23514 this.editorcore.focus();
23520 onDestroy : function(){
23526 for (var i =0; i < this.toolbars.length;i++) {
23527 // fixme - ask toolbars for heights?
23528 this.toolbars[i].onDestroy();
23531 this.wrap.dom.innerHTML = '';
23532 this.wrap.remove();
23537 onFirstFocus : function(){
23538 //Roo.log("onFirstFocus");
23539 this.editorcore.onFirstFocus();
23540 for (var i =0; i < this.toolbars.length;i++) {
23541 this.toolbars[i].onFirstFocus();
23547 syncValue : function()
23549 this.editorcore.syncValue();
23552 pushValue : function()
23554 this.editorcore.pushValue();
23558 // hide stuff that is not compatible
23572 * @event specialkey
23576 * @cfg {String} fieldClass @hide
23579 * @cfg {String} focusClass @hide
23582 * @cfg {String} autoCreate @hide
23585 * @cfg {String} inputType @hide
23588 * @cfg {String} invalidClass @hide
23591 * @cfg {String} invalidText @hide
23594 * @cfg {String} msgFx @hide
23597 * @cfg {String} validateOnBlur @hide
23606 Roo.namespace('Roo.bootstrap.htmleditor');
23608 * @class Roo.bootstrap.HtmlEditorToolbar1
23613 new Roo.bootstrap.HtmlEditor({
23616 new Roo.bootstrap.HtmlEditorToolbar1({
23617 disable : { fonts: 1 , format: 1, ..., ... , ...],
23623 * @cfg {Object} disable List of elements to disable..
23624 * @cfg {Array} btns List of additional buttons.
23628 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23631 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23634 Roo.apply(this, config);
23636 // default disabled, based on 'good practice'..
23637 this.disable = this.disable || {};
23638 Roo.applyIf(this.disable, {
23641 specialElements : true
23643 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23645 this.editor = config.editor;
23646 this.editorcore = config.editor.editorcore;
23648 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23650 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23651 // dont call parent... till later.
23653 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23658 editorcore : false,
23663 "h1","h2","h3","h4","h5","h6",
23665 "abbr", "acronym", "address", "cite", "samp", "var",
23669 onRender : function(ct, position)
23671 // Roo.log("Call onRender: " + this.xtype);
23673 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23675 this.el.dom.style.marginBottom = '0';
23677 var editorcore = this.editorcore;
23678 var editor= this.editor;
23681 var btn = function(id,cmd , toggle, handler, html){
23683 var event = toggle ? 'toggle' : 'click';
23688 xns: Roo.bootstrap,
23691 enableToggle:toggle !== false,
23693 pressed : toggle ? false : null,
23696 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23697 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23703 // var cb_box = function...
23708 xns: Roo.bootstrap,
23709 glyphicon : 'font',
23713 xns: Roo.bootstrap,
23717 Roo.each(this.formats, function(f) {
23718 style.menu.items.push({
23720 xns: Roo.bootstrap,
23721 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23726 editorcore.insertTag(this.tagname);
23733 children.push(style);
23735 btn('bold',false,true);
23736 btn('italic',false,true);
23737 btn('align-left', 'justifyleft',true);
23738 btn('align-center', 'justifycenter',true);
23739 btn('align-right' , 'justifyright',true);
23740 btn('link', false, false, function(btn) {
23741 //Roo.log("create link?");
23742 var url = prompt(this.createLinkText, this.defaultLinkValue);
23743 if(url && url != 'http:/'+'/'){
23744 this.editorcore.relayCmd('createlink', url);
23747 btn('list','insertunorderedlist',true);
23748 btn('pencil', false,true, function(btn){
23750 this.toggleSourceEdit(btn.pressed);
23753 if (this.editor.btns.length > 0) {
23754 for (var i = 0; i<this.editor.btns.length; i++) {
23755 children.push(this.editor.btns[i]);
23763 xns: Roo.bootstrap,
23768 xns: Roo.bootstrap,
23773 cog.menu.items.push({
23775 xns: Roo.bootstrap,
23776 html : Clean styles,
23781 editorcore.insertTag(this.tagname);
23790 this.xtype = 'NavSimplebar';
23792 for(var i=0;i< children.length;i++) {
23794 this.buttons.add(this.addxtypeChild(children[i]));
23798 editor.on('editorevent', this.updateToolbar, this);
23800 onBtnClick : function(id)
23802 this.editorcore.relayCmd(id);
23803 this.editorcore.focus();
23807 * Protected method that will not generally be called directly. It triggers
23808 * a toolbar update by reading the markup state of the current selection in the editor.
23810 updateToolbar: function(){
23812 if(!this.editorcore.activated){
23813 this.editor.onFirstFocus(); // is this neeed?
23817 var btns = this.buttons;
23818 var doc = this.editorcore.doc;
23819 btns.get('bold').setActive(doc.queryCommandState('bold'));
23820 btns.get('italic').setActive(doc.queryCommandState('italic'));
23821 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23823 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23824 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23825 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23827 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23828 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23831 var ans = this.editorcore.getAllAncestors();
23832 if (this.formatCombo) {
23835 var store = this.formatCombo.store;
23836 this.formatCombo.setValue("");
23837 for (var i =0; i < ans.length;i++) {
23838 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23840 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23848 // hides menus... - so this cant be on a menu...
23849 Roo.bootstrap.MenuMgr.hideAll();
23851 Roo.bootstrap.MenuMgr.hideAll();
23852 //this.editorsyncValue();
23854 onFirstFocus: function() {
23855 this.buttons.each(function(item){
23859 toggleSourceEdit : function(sourceEditMode){
23862 if(sourceEditMode){
23863 Roo.log("disabling buttons");
23864 this.buttons.each( function(item){
23865 if(item.cmd != 'pencil'){
23871 Roo.log("enabling buttons");
23872 if(this.editorcore.initialized){
23873 this.buttons.each( function(item){
23879 Roo.log("calling toggole on editor");
23880 // tell the editor that it's been pressed..
23881 this.editor.toggleSourceEdit(sourceEditMode);
23891 * @class Roo.bootstrap.Table.AbstractSelectionModel
23892 * @extends Roo.util.Observable
23893 * Abstract base class for grid SelectionModels. It provides the interface that should be
23894 * implemented by descendant classes. This class should not be directly instantiated.
23897 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23898 this.locked = false;
23899 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23903 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23904 /** @ignore Called by the grid automatically. Do not call directly. */
23905 init : function(grid){
23911 * Locks the selections.
23914 this.locked = true;
23918 * Unlocks the selections.
23920 unlock : function(){
23921 this.locked = false;
23925 * Returns true if the selections are locked.
23926 * @return {Boolean}
23928 isLocked : function(){
23929 return this.locked;
23933 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23934 * @class Roo.bootstrap.Table.RowSelectionModel
23935 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23936 * It supports multiple selections and keyboard selection/navigation.
23938 * @param {Object} config
23941 Roo.bootstrap.Table.RowSelectionModel = function(config){
23942 Roo.apply(this, config);
23943 this.selections = new Roo.util.MixedCollection(false, function(o){
23948 this.lastActive = false;
23952 * @event selectionchange
23953 * Fires when the selection changes
23954 * @param {SelectionModel} this
23956 "selectionchange" : true,
23958 * @event afterselectionchange
23959 * Fires after the selection changes (eg. by key press or clicking)
23960 * @param {SelectionModel} this
23962 "afterselectionchange" : true,
23964 * @event beforerowselect
23965 * Fires when a row is selected being selected, return false to cancel.
23966 * @param {SelectionModel} this
23967 * @param {Number} rowIndex The selected index
23968 * @param {Boolean} keepExisting False if other selections will be cleared
23970 "beforerowselect" : true,
23973 * Fires when a row is selected.
23974 * @param {SelectionModel} this
23975 * @param {Number} rowIndex The selected index
23976 * @param {Roo.data.Record} r The record
23978 "rowselect" : true,
23980 * @event rowdeselect
23981 * Fires when a row is deselected.
23982 * @param {SelectionModel} this
23983 * @param {Number} rowIndex The selected index
23985 "rowdeselect" : true
23987 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23988 this.locked = false;
23991 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23993 * @cfg {Boolean} singleSelect
23994 * True to allow selection of only one row at a time (defaults to false)
23996 singleSelect : false,
23999 initEvents : function()
24002 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24003 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24004 //}else{ // allow click to work like normal
24005 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24007 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24008 this.grid.on("rowclick", this.handleMouseDown, this);
24010 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24011 "up" : function(e){
24013 this.selectPrevious(e.shiftKey);
24014 }else if(this.last !== false && this.lastActive !== false){
24015 var last = this.last;
24016 this.selectRange(this.last, this.lastActive-1);
24017 this.grid.getView().focusRow(this.lastActive);
24018 if(last !== false){
24022 this.selectFirstRow();
24024 this.fireEvent("afterselectionchange", this);
24026 "down" : function(e){
24028 this.selectNext(e.shiftKey);
24029 }else if(this.last !== false && this.lastActive !== false){
24030 var last = this.last;
24031 this.selectRange(this.last, this.lastActive+1);
24032 this.grid.getView().focusRow(this.lastActive);
24033 if(last !== false){
24037 this.selectFirstRow();
24039 this.fireEvent("afterselectionchange", this);
24043 this.grid.store.on('load', function(){
24044 this.selections.clear();
24047 var view = this.grid.view;
24048 view.on("refresh", this.onRefresh, this);
24049 view.on("rowupdated", this.onRowUpdated, this);
24050 view.on("rowremoved", this.onRemove, this);
24055 onRefresh : function()
24057 var ds = this.grid.store, i, v = this.grid.view;
24058 var s = this.selections;
24059 s.each(function(r){
24060 if((i = ds.indexOfId(r.id)) != -1){
24069 onRemove : function(v, index, r){
24070 this.selections.remove(r);
24074 onRowUpdated : function(v, index, r){
24075 if(this.isSelected(r)){
24076 v.onRowSelect(index);
24082 * @param {Array} records The records to select
24083 * @param {Boolean} keepExisting (optional) True to keep existing selections
24085 selectRecords : function(records, keepExisting)
24088 this.clearSelections();
24090 var ds = this.grid.store;
24091 for(var i = 0, len = records.length; i < len; i++){
24092 this.selectRow(ds.indexOf(records[i]), true);
24097 * Gets the number of selected rows.
24100 getCount : function(){
24101 return this.selections.length;
24105 * Selects the first row in the grid.
24107 selectFirstRow : function(){
24112 * Select the last row.
24113 * @param {Boolean} keepExisting (optional) True to keep existing selections
24115 selectLastRow : function(keepExisting){
24116 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24117 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24121 * Selects the row immediately following the last selected row.
24122 * @param {Boolean} keepExisting (optional) True to keep existing selections
24124 selectNext : function(keepExisting)
24126 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24127 this.selectRow(this.last+1, keepExisting);
24128 this.grid.getView().focusRow(this.last);
24133 * Selects the row that precedes the last selected row.
24134 * @param {Boolean} keepExisting (optional) True to keep existing selections
24136 selectPrevious : function(keepExisting){
24138 this.selectRow(this.last-1, keepExisting);
24139 this.grid.getView().focusRow(this.last);
24144 * Returns the selected records
24145 * @return {Array} Array of selected records
24147 getSelections : function(){
24148 return [].concat(this.selections.items);
24152 * Returns the first selected record.
24155 getSelected : function(){
24156 return this.selections.itemAt(0);
24161 * Clears all selections.
24163 clearSelections : function(fast)
24169 var ds = this.grid.store;
24170 var s = this.selections;
24171 s.each(function(r){
24172 this.deselectRow(ds.indexOfId(r.id));
24176 this.selections.clear();
24183 * Selects all rows.
24185 selectAll : function(){
24189 this.selections.clear();
24190 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24191 this.selectRow(i, true);
24196 * Returns True if there is a selection.
24197 * @return {Boolean}
24199 hasSelection : function(){
24200 return this.selections.length > 0;
24204 * Returns True if the specified row is selected.
24205 * @param {Number/Record} record The record or index of the record to check
24206 * @return {Boolean}
24208 isSelected : function(index){
24209 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24210 return (r && this.selections.key(r.id) ? true : false);
24214 * Returns True if the specified record id is selected.
24215 * @param {String} id The id of record to check
24216 * @return {Boolean}
24218 isIdSelected : function(id){
24219 return (this.selections.key(id) ? true : false);
24224 handleMouseDBClick : function(e, t){
24228 handleMouseDown : function(e, t)
24230 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24231 if(this.isLocked() || rowIndex < 0 ){
24234 if(e.shiftKey && this.last !== false){
24235 var last = this.last;
24236 this.selectRange(last, rowIndex, e.ctrlKey);
24237 this.last = last; // reset the last
24241 var isSelected = this.isSelected(rowIndex);
24242 //Roo.log("select row:" + rowIndex);
24244 this.deselectRow(rowIndex);
24246 this.selectRow(rowIndex, true);
24250 if(e.button !== 0 && isSelected){
24251 alert('rowIndex 2: ' + rowIndex);
24252 view.focusRow(rowIndex);
24253 }else if(e.ctrlKey && isSelected){
24254 this.deselectRow(rowIndex);
24255 }else if(!isSelected){
24256 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24257 view.focusRow(rowIndex);
24261 this.fireEvent("afterselectionchange", this);
24264 handleDragableRowClick : function(grid, rowIndex, e)
24266 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24267 this.selectRow(rowIndex, false);
24268 grid.view.focusRow(rowIndex);
24269 this.fireEvent("afterselectionchange", this);
24274 * Selects multiple rows.
24275 * @param {Array} rows Array of the indexes of the row to select
24276 * @param {Boolean} keepExisting (optional) True to keep existing selections
24278 selectRows : function(rows, keepExisting){
24280 this.clearSelections();
24282 for(var i = 0, len = rows.length; i < len; i++){
24283 this.selectRow(rows[i], true);
24288 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24289 * @param {Number} startRow The index of the first row in the range
24290 * @param {Number} endRow The index of the last row in the range
24291 * @param {Boolean} keepExisting (optional) True to retain existing selections
24293 selectRange : function(startRow, endRow, keepExisting){
24298 this.clearSelections();
24300 if(startRow <= endRow){
24301 for(var i = startRow; i <= endRow; i++){
24302 this.selectRow(i, true);
24305 for(var i = startRow; i >= endRow; i--){
24306 this.selectRow(i, true);
24312 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24313 * @param {Number} startRow The index of the first row in the range
24314 * @param {Number} endRow The index of the last row in the range
24316 deselectRange : function(startRow, endRow, preventViewNotify){
24320 for(var i = startRow; i <= endRow; i++){
24321 this.deselectRow(i, preventViewNotify);
24327 * @param {Number} row The index of the row to select
24328 * @param {Boolean} keepExisting (optional) True to keep existing selections
24330 selectRow : function(index, keepExisting, preventViewNotify)
24332 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24335 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24336 if(!keepExisting || this.singleSelect){
24337 this.clearSelections();
24340 var r = this.grid.store.getAt(index);
24341 //console.log('selectRow - record id :' + r.id);
24343 this.selections.add(r);
24344 this.last = this.lastActive = index;
24345 if(!preventViewNotify){
24346 var proxy = new Roo.Element(
24347 this.grid.getRowDom(index)
24349 proxy.addClass('bg-info info');
24351 this.fireEvent("rowselect", this, index, r);
24352 this.fireEvent("selectionchange", this);
24358 * @param {Number} row The index of the row to deselect
24360 deselectRow : function(index, preventViewNotify)
24365 if(this.last == index){
24368 if(this.lastActive == index){
24369 this.lastActive = false;
24372 var r = this.grid.store.getAt(index);
24377 this.selections.remove(r);
24378 //.console.log('deselectRow - record id :' + r.id);
24379 if(!preventViewNotify){
24381 var proxy = new Roo.Element(
24382 this.grid.getRowDom(index)
24384 proxy.removeClass('bg-info info');
24386 this.fireEvent("rowdeselect", this, index);
24387 this.fireEvent("selectionchange", this);
24391 restoreLast : function(){
24393 this.last = this._last;
24398 acceptsNav : function(row, col, cm){
24399 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24403 onEditorKey : function(field, e){
24404 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24409 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24411 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24413 }else if(k == e.ENTER && !e.ctrlKey){
24417 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24419 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24421 }else if(k == e.ESC){
24425 g.startEditing(newCell[0], newCell[1]);
24431 * Ext JS Library 1.1.1
24432 * Copyright(c) 2006-2007, Ext JS, LLC.
24434 * Originally Released Under LGPL - original licence link has changed is not relivant.
24437 * <script type="text/javascript">
24441 * @class Roo.bootstrap.PagingToolbar
24442 * @extends Roo.bootstrap.NavSimplebar
24443 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24445 * Create a new PagingToolbar
24446 * @param {Object} config The config object
24447 * @param {Roo.data.Store} store
24449 Roo.bootstrap.PagingToolbar = function(config)
24451 // old args format still supported... - xtype is prefered..
24452 // created from xtype...
24454 this.ds = config.dataSource;
24456 if (config.store && !this.ds) {
24457 this.store= Roo.factory(config.store, Roo.data);
24458 this.ds = this.store;
24459 this.ds.xmodule = this.xmodule || false;
24462 this.toolbarItems = [];
24463 if (config.items) {
24464 this.toolbarItems = config.items;
24467 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24472 this.bind(this.ds);
24475 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24479 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24481 * @cfg {Roo.data.Store} dataSource
24482 * The underlying data store providing the paged data
24485 * @cfg {String/HTMLElement/Element} container
24486 * container The id or element that will contain the toolbar
24489 * @cfg {Boolean} displayInfo
24490 * True to display the displayMsg (defaults to false)
24493 * @cfg {Number} pageSize
24494 * The number of records to display per page (defaults to 20)
24498 * @cfg {String} displayMsg
24499 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24501 displayMsg : 'Displaying {0} - {1} of {2}',
24503 * @cfg {String} emptyMsg
24504 * The message to display when no records are found (defaults to "No data to display")
24506 emptyMsg : 'No data to display',
24508 * Customizable piece of the default paging text (defaults to "Page")
24511 beforePageText : "Page",
24513 * Customizable piece of the default paging text (defaults to "of %0")
24516 afterPageText : "of {0}",
24518 * Customizable piece of the default paging text (defaults to "First Page")
24521 firstText : "First Page",
24523 * Customizable piece of the default paging text (defaults to "Previous Page")
24526 prevText : "Previous Page",
24528 * Customizable piece of the default paging text (defaults to "Next Page")
24531 nextText : "Next Page",
24533 * Customizable piece of the default paging text (defaults to "Last Page")
24536 lastText : "Last Page",
24538 * Customizable piece of the default paging text (defaults to "Refresh")
24541 refreshText : "Refresh",
24545 onRender : function(ct, position)
24547 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24548 this.navgroup.parentId = this.id;
24549 this.navgroup.onRender(this.el, null);
24550 // add the buttons to the navgroup
24552 if(this.displayInfo){
24553 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24554 this.displayEl = this.el.select('.x-paging-info', true).first();
24555 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24556 // this.displayEl = navel.el.select('span',true).first();
24562 Roo.each(_this.buttons, function(e){ // this might need to use render????
24563 Roo.factory(e).render(_this.el);
24567 Roo.each(_this.toolbarItems, function(e) {
24568 _this.navgroup.addItem(e);
24572 this.first = this.navgroup.addItem({
24573 tooltip: this.firstText,
24575 icon : 'fa fa-backward',
24577 preventDefault: true,
24578 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24581 this.prev = this.navgroup.addItem({
24582 tooltip: this.prevText,
24584 icon : 'fa fa-step-backward',
24586 preventDefault: true,
24587 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24589 //this.addSeparator();
24592 var field = this.navgroup.addItem( {
24594 cls : 'x-paging-position',
24596 html : this.beforePageText +
24597 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24598 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24601 this.field = field.el.select('input', true).first();
24602 this.field.on("keydown", this.onPagingKeydown, this);
24603 this.field.on("focus", function(){this.dom.select();});
24606 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24607 //this.field.setHeight(18);
24608 //this.addSeparator();
24609 this.next = this.navgroup.addItem({
24610 tooltip: this.nextText,
24612 html : ' <i class="fa fa-step-forward">',
24614 preventDefault: true,
24615 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24617 this.last = this.navgroup.addItem({
24618 tooltip: this.lastText,
24619 icon : 'fa fa-forward',
24622 preventDefault: true,
24623 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24625 //this.addSeparator();
24626 this.loading = this.navgroup.addItem({
24627 tooltip: this.refreshText,
24628 icon: 'fa fa-refresh',
24629 preventDefault: true,
24630 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24636 updateInfo : function(){
24637 if(this.displayEl){
24638 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24639 var msg = count == 0 ?
24643 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24645 this.displayEl.update(msg);
24650 onLoad : function(ds, r, o)
24652 this.cursor = o.params.start ? o.params.start : 0;
24654 var d = this.getPageData(),
24659 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24660 this.field.dom.value = ap;
24661 this.first.setDisabled(ap == 1);
24662 this.prev.setDisabled(ap == 1);
24663 this.next.setDisabled(ap == ps);
24664 this.last.setDisabled(ap == ps);
24665 this.loading.enable();
24670 getPageData : function(){
24671 var total = this.ds.getTotalCount();
24674 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24675 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24680 onLoadError : function(){
24681 this.loading.enable();
24685 onPagingKeydown : function(e){
24686 var k = e.getKey();
24687 var d = this.getPageData();
24689 var v = this.field.dom.value, pageNum;
24690 if(!v || isNaN(pageNum = parseInt(v, 10))){
24691 this.field.dom.value = d.activePage;
24694 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24695 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24698 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))
24700 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24701 this.field.dom.value = pageNum;
24702 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24705 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24707 var v = this.field.dom.value, pageNum;
24708 var increment = (e.shiftKey) ? 10 : 1;
24709 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24712 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24713 this.field.dom.value = d.activePage;
24716 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24718 this.field.dom.value = parseInt(v, 10) + increment;
24719 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24720 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24727 beforeLoad : function(){
24729 this.loading.disable();
24734 onClick : function(which){
24743 ds.load({params:{start: 0, limit: this.pageSize}});
24746 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24749 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24752 var total = ds.getTotalCount();
24753 var extra = total % this.pageSize;
24754 var lastStart = extra ? (total - extra) : total-this.pageSize;
24755 ds.load({params:{start: lastStart, limit: this.pageSize}});
24758 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24764 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24765 * @param {Roo.data.Store} store The data store to unbind
24767 unbind : function(ds){
24768 ds.un("beforeload", this.beforeLoad, this);
24769 ds.un("load", this.onLoad, this);
24770 ds.un("loadexception", this.onLoadError, this);
24771 ds.un("remove", this.updateInfo, this);
24772 ds.un("add", this.updateInfo, this);
24773 this.ds = undefined;
24777 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24778 * @param {Roo.data.Store} store The data store to bind
24780 bind : function(ds){
24781 ds.on("beforeload", this.beforeLoad, this);
24782 ds.on("load", this.onLoad, this);
24783 ds.on("loadexception", this.onLoadError, this);
24784 ds.on("remove", this.updateInfo, this);
24785 ds.on("add", this.updateInfo, this);
24796 * @class Roo.bootstrap.MessageBar
24797 * @extends Roo.bootstrap.Component
24798 * Bootstrap MessageBar class
24799 * @cfg {String} html contents of the MessageBar
24800 * @cfg {String} weight (info | success | warning | danger) default info
24801 * @cfg {String} beforeClass insert the bar before the given class
24802 * @cfg {Boolean} closable (true | false) default false
24803 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24806 * Create a new Element
24807 * @param {Object} config The config object
24810 Roo.bootstrap.MessageBar = function(config){
24811 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24814 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24820 beforeClass: 'bootstrap-sticky-wrap',
24822 getAutoCreate : function(){
24826 cls: 'alert alert-dismissable alert-' + this.weight,
24831 html: this.html || ''
24837 cfg.cls += ' alert-messages-fixed';
24851 onRender : function(ct, position)
24853 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24856 var cfg = Roo.apply({}, this.getAutoCreate());
24860 cfg.cls += ' ' + this.cls;
24863 cfg.style = this.style;
24865 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24867 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24870 this.el.select('>button.close').on('click', this.hide, this);
24876 if (!this.rendered) {
24882 this.fireEvent('show', this);
24888 if (!this.rendered) {
24894 this.fireEvent('hide', this);
24897 update : function()
24899 // var e = this.el.dom.firstChild;
24901 // if(this.closable){
24902 // e = e.nextSibling;
24905 // e.data = this.html || '';
24907 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24923 * @class Roo.bootstrap.Graph
24924 * @extends Roo.bootstrap.Component
24925 * Bootstrap Graph class
24929 @cfg {String} graphtype bar | vbar | pie
24930 @cfg {number} g_x coodinator | centre x (pie)
24931 @cfg {number} g_y coodinator | centre y (pie)
24932 @cfg {number} g_r radius (pie)
24933 @cfg {number} g_height height of the chart (respected by all elements in the set)
24934 @cfg {number} g_width width of the chart (respected by all elements in the set)
24935 @cfg {Object} title The title of the chart
24938 -opts (object) options for the chart
24940 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24941 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24943 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.
24944 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24946 o stretch (boolean)
24948 -opts (object) options for the pie
24951 o startAngle (number)
24952 o endAngle (number)
24956 * Create a new Input
24957 * @param {Object} config The config object
24960 Roo.bootstrap.Graph = function(config){
24961 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24967 * The img click event for the img.
24968 * @param {Roo.EventObject} e
24974 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24985 //g_colors: this.colors,
24992 getAutoCreate : function(){
25003 onRender : function(ct,position){
25006 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25008 if (typeof(Raphael) == 'undefined') {
25009 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25013 this.raphael = Raphael(this.el.dom);
25015 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25016 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25017 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25018 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25020 r.text(160, 10, "Single Series Chart").attr(txtattr);
25021 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25022 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25023 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25025 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25026 r.barchart(330, 10, 300, 220, data1);
25027 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25028 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25031 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25032 // r.barchart(30, 30, 560, 250, xdata, {
25033 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25034 // axis : "0 0 1 1",
25035 // axisxlabels : xdata
25036 // //yvalues : cols,
25039 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25041 // this.load(null,xdata,{
25042 // axis : "0 0 1 1",
25043 // axisxlabels : xdata
25048 load : function(graphtype,xdata,opts)
25050 this.raphael.clear();
25052 graphtype = this.graphtype;
25057 var r = this.raphael,
25058 fin = function () {
25059 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25061 fout = function () {
25062 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25064 pfin = function() {
25065 this.sector.stop();
25066 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25069 this.label[0].stop();
25070 this.label[0].attr({ r: 7.5 });
25071 this.label[1].attr({ "font-weight": 800 });
25074 pfout = function() {
25075 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25078 this.label[0].animate({ r: 5 }, 500, "bounce");
25079 this.label[1].attr({ "font-weight": 400 });
25085 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25088 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25091 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25092 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25094 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25101 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25106 setTitle: function(o)
25111 initEvents: function() {
25114 this.el.on('click', this.onClick, this);
25118 onClick : function(e)
25120 Roo.log('img onclick');
25121 this.fireEvent('click', this, e);
25133 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25136 * @class Roo.bootstrap.dash.NumberBox
25137 * @extends Roo.bootstrap.Component
25138 * Bootstrap NumberBox class
25139 * @cfg {String} headline Box headline
25140 * @cfg {String} content Box content
25141 * @cfg {String} icon Box icon
25142 * @cfg {String} footer Footer text
25143 * @cfg {String} fhref Footer href
25146 * Create a new NumberBox
25147 * @param {Object} config The config object
25151 Roo.bootstrap.dash.NumberBox = function(config){
25152 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25156 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25165 getAutoCreate : function(){
25169 cls : 'small-box ',
25177 cls : 'roo-headline',
25178 html : this.headline
25182 cls : 'roo-content',
25183 html : this.content
25197 cls : 'ion ' + this.icon
25206 cls : 'small-box-footer',
25207 href : this.fhref || '#',
25211 cfg.cn.push(footer);
25218 onRender : function(ct,position){
25219 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25226 setHeadline: function (value)
25228 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25231 setFooter: function (value, href)
25233 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25236 this.el.select('a.small-box-footer',true).first().attr('href', href);
25241 setContent: function (value)
25243 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25246 initEvents: function()
25260 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25263 * @class Roo.bootstrap.dash.TabBox
25264 * @extends Roo.bootstrap.Component
25265 * Bootstrap TabBox class
25266 * @cfg {String} title Title of the TabBox
25267 * @cfg {String} icon Icon of the TabBox
25268 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25269 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25272 * Create a new TabBox
25273 * @param {Object} config The config object
25277 Roo.bootstrap.dash.TabBox = function(config){
25278 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25283 * When a pane is added
25284 * @param {Roo.bootstrap.dash.TabPane} pane
25288 * @event activatepane
25289 * When a pane is activated
25290 * @param {Roo.bootstrap.dash.TabPane} pane
25292 "activatepane" : true
25300 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25305 tabScrollable : false,
25307 getChildContainer : function()
25309 return this.el.select('.tab-content', true).first();
25312 getAutoCreate : function(){
25316 cls: 'pull-left header',
25324 cls: 'fa ' + this.icon
25330 cls: 'nav nav-tabs pull-right',
25336 if(this.tabScrollable){
25343 cls: 'nav nav-tabs pull-right',
25354 cls: 'nav-tabs-custom',
25359 cls: 'tab-content no-padding',
25367 initEvents : function()
25369 //Roo.log('add add pane handler');
25370 this.on('addpane', this.onAddPane, this);
25373 * Updates the box title
25374 * @param {String} html to set the title to.
25376 setTitle : function(value)
25378 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25380 onAddPane : function(pane)
25382 this.panes.push(pane);
25383 //Roo.log('addpane');
25385 // tabs are rendere left to right..
25386 if(!this.showtabs){
25390 var ctr = this.el.select('.nav-tabs', true).first();
25393 var existing = ctr.select('.nav-tab',true);
25394 var qty = existing.getCount();;
25397 var tab = ctr.createChild({
25399 cls : 'nav-tab' + (qty ? '' : ' active'),
25407 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25410 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25412 pane.el.addClass('active');
25417 onTabClick : function(ev,un,ob,pane)
25419 //Roo.log('tab - prev default');
25420 ev.preventDefault();
25423 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25424 pane.tab.addClass('active');
25425 //Roo.log(pane.title);
25426 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25427 // technically we should have a deactivate event.. but maybe add later.
25428 // and it should not de-activate the selected tab...
25429 this.fireEvent('activatepane', pane);
25430 pane.el.addClass('active');
25431 pane.fireEvent('activate');
25436 getActivePane : function()
25439 Roo.each(this.panes, function(p) {
25440 if(p.el.hasClass('active')){
25461 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25463 * @class Roo.bootstrap.TabPane
25464 * @extends Roo.bootstrap.Component
25465 * Bootstrap TabPane class
25466 * @cfg {Boolean} active (false | true) Default false
25467 * @cfg {String} title title of panel
25471 * Create a new TabPane
25472 * @param {Object} config The config object
25475 Roo.bootstrap.dash.TabPane = function(config){
25476 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25482 * When a pane is activated
25483 * @param {Roo.bootstrap.dash.TabPane} pane
25490 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25495 // the tabBox that this is attached to.
25498 getAutoCreate : function()
25506 cfg.cls += ' active';
25511 initEvents : function()
25513 //Roo.log('trigger add pane handler');
25514 this.parent().fireEvent('addpane', this)
25518 * Updates the tab title
25519 * @param {String} html to set the title to.
25521 setTitle: function(str)
25527 this.tab.select('a', true).first().dom.innerHTML = str;
25544 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25547 * @class Roo.bootstrap.menu.Menu
25548 * @extends Roo.bootstrap.Component
25549 * Bootstrap Menu class - container for Menu
25550 * @cfg {String} html Text of the menu
25551 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25552 * @cfg {String} icon Font awesome icon
25553 * @cfg {String} pos Menu align to (top | bottom) default bottom
25557 * Create a new Menu
25558 * @param {Object} config The config object
25562 Roo.bootstrap.menu.Menu = function(config){
25563 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25567 * @event beforeshow
25568 * Fires before this menu is displayed
25569 * @param {Roo.bootstrap.menu.Menu} this
25573 * @event beforehide
25574 * Fires before this menu is hidden
25575 * @param {Roo.bootstrap.menu.Menu} this
25580 * Fires after this menu is displayed
25581 * @param {Roo.bootstrap.menu.Menu} this
25586 * Fires after this menu is hidden
25587 * @param {Roo.bootstrap.menu.Menu} this
25592 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25593 * @param {Roo.bootstrap.menu.Menu} this
25594 * @param {Roo.EventObject} e
25601 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25605 weight : 'default',
25610 getChildContainer : function() {
25611 if(this.isSubMenu){
25615 return this.el.select('ul.dropdown-menu', true).first();
25618 getAutoCreate : function()
25623 cls : 'roo-menu-text',
25631 cls : 'fa ' + this.icon
25642 cls : 'dropdown-button btn btn-' + this.weight,
25647 cls : 'dropdown-toggle btn btn-' + this.weight,
25657 cls : 'dropdown-menu'
25663 if(this.pos == 'top'){
25664 cfg.cls += ' dropup';
25667 if(this.isSubMenu){
25670 cls : 'dropdown-menu'
25677 onRender : function(ct, position)
25679 this.isSubMenu = ct.hasClass('dropdown-submenu');
25681 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25684 initEvents : function()
25686 if(this.isSubMenu){
25690 this.hidden = true;
25692 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25693 this.triggerEl.on('click', this.onTriggerPress, this);
25695 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25696 this.buttonEl.on('click', this.onClick, this);
25702 if(this.isSubMenu){
25706 return this.el.select('ul.dropdown-menu', true).first();
25709 onClick : function(e)
25711 this.fireEvent("click", this, e);
25714 onTriggerPress : function(e)
25716 if (this.isVisible()) {
25723 isVisible : function(){
25724 return !this.hidden;
25729 this.fireEvent("beforeshow", this);
25731 this.hidden = false;
25732 this.el.addClass('open');
25734 Roo.get(document).on("mouseup", this.onMouseUp, this);
25736 this.fireEvent("show", this);
25743 this.fireEvent("beforehide", this);
25745 this.hidden = true;
25746 this.el.removeClass('open');
25748 Roo.get(document).un("mouseup", this.onMouseUp);
25750 this.fireEvent("hide", this);
25753 onMouseUp : function()
25767 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25770 * @class Roo.bootstrap.menu.Item
25771 * @extends Roo.bootstrap.Component
25772 * Bootstrap MenuItem class
25773 * @cfg {Boolean} submenu (true | false) default false
25774 * @cfg {String} html text of the item
25775 * @cfg {String} href the link
25776 * @cfg {Boolean} disable (true | false) default false
25777 * @cfg {Boolean} preventDefault (true | false) default true
25778 * @cfg {String} icon Font awesome icon
25779 * @cfg {String} pos Submenu align to (left | right) default right
25783 * Create a new Item
25784 * @param {Object} config The config object
25788 Roo.bootstrap.menu.Item = function(config){
25789 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25793 * Fires when the mouse is hovering over this menu
25794 * @param {Roo.bootstrap.menu.Item} this
25795 * @param {Roo.EventObject} e
25800 * Fires when the mouse exits this menu
25801 * @param {Roo.bootstrap.menu.Item} this
25802 * @param {Roo.EventObject} e
25808 * The raw click event for the entire grid.
25809 * @param {Roo.EventObject} e
25815 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25820 preventDefault: true,
25825 getAutoCreate : function()
25830 cls : 'roo-menu-item-text',
25838 cls : 'fa ' + this.icon
25847 href : this.href || '#',
25854 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25858 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25860 if(this.pos == 'left'){
25861 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25868 initEvents : function()
25870 this.el.on('mouseover', this.onMouseOver, this);
25871 this.el.on('mouseout', this.onMouseOut, this);
25873 this.el.select('a', true).first().on('click', this.onClick, this);
25877 onClick : function(e)
25879 if(this.preventDefault){
25880 e.preventDefault();
25883 this.fireEvent("click", this, e);
25886 onMouseOver : function(e)
25888 if(this.submenu && this.pos == 'left'){
25889 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25892 this.fireEvent("mouseover", this, e);
25895 onMouseOut : function(e)
25897 this.fireEvent("mouseout", this, e);
25909 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25912 * @class Roo.bootstrap.menu.Separator
25913 * @extends Roo.bootstrap.Component
25914 * Bootstrap Separator class
25917 * Create a new Separator
25918 * @param {Object} config The config object
25922 Roo.bootstrap.menu.Separator = function(config){
25923 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25926 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25928 getAutoCreate : function(){
25949 * @class Roo.bootstrap.Tooltip
25950 * Bootstrap Tooltip class
25951 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25952 * to determine which dom element triggers the tooltip.
25954 * It needs to add support for additional attributes like tooltip-position
25957 * Create a new Toolti
25958 * @param {Object} config The config object
25961 Roo.bootstrap.Tooltip = function(config){
25962 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25964 this.alignment = Roo.bootstrap.Tooltip.alignment;
25966 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25967 this.alignment = config.alignment;
25972 Roo.apply(Roo.bootstrap.Tooltip, {
25974 * @function init initialize tooltip monitoring.
25978 currentTip : false,
25979 currentRegion : false,
25985 Roo.get(document).on('mouseover', this.enter ,this);
25986 Roo.get(document).on('mouseout', this.leave, this);
25989 this.currentTip = new Roo.bootstrap.Tooltip();
25992 enter : function(ev)
25994 var dom = ev.getTarget();
25996 //Roo.log(['enter',dom]);
25997 var el = Roo.fly(dom);
25998 if (this.currentEl) {
26000 //Roo.log(this.currentEl);
26001 //Roo.log(this.currentEl.contains(dom));
26002 if (this.currentEl == el) {
26005 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26011 if (this.currentTip.el) {
26012 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26016 if(!el || el.dom == document){
26022 // you can not look for children, as if el is the body.. then everythign is the child..
26023 if (!el.attr('tooltip')) { //
26024 if (!el.select("[tooltip]").elements.length) {
26027 // is the mouse over this child...?
26028 bindEl = el.select("[tooltip]").first();
26029 var xy = ev.getXY();
26030 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26031 //Roo.log("not in region.");
26034 //Roo.log("child element over..");
26037 this.currentEl = bindEl;
26038 this.currentTip.bind(bindEl);
26039 this.currentRegion = Roo.lib.Region.getRegion(dom);
26040 this.currentTip.enter();
26043 leave : function(ev)
26045 var dom = ev.getTarget();
26046 //Roo.log(['leave',dom]);
26047 if (!this.currentEl) {
26052 if (dom != this.currentEl.dom) {
26055 var xy = ev.getXY();
26056 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26059 // only activate leave if mouse cursor is outside... bounding box..
26064 if (this.currentTip) {
26065 this.currentTip.leave();
26067 //Roo.log('clear currentEl');
26068 this.currentEl = false;
26073 'left' : ['r-l', [-2,0], 'right'],
26074 'right' : ['l-r', [2,0], 'left'],
26075 'bottom' : ['t-b', [0,2], 'top'],
26076 'top' : [ 'b-t', [0,-2], 'bottom']
26082 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26087 delay : null, // can be { show : 300 , hide: 500}
26091 hoverState : null, //???
26093 placement : 'bottom',
26097 getAutoCreate : function(){
26104 cls : 'tooltip-arrow'
26107 cls : 'tooltip-inner'
26114 bind : function(el)
26120 enter : function () {
26122 if (this.timeout != null) {
26123 clearTimeout(this.timeout);
26126 this.hoverState = 'in';
26127 //Roo.log("enter - show");
26128 if (!this.delay || !this.delay.show) {
26133 this.timeout = setTimeout(function () {
26134 if (_t.hoverState == 'in') {
26137 }, this.delay.show);
26141 clearTimeout(this.timeout);
26143 this.hoverState = 'out';
26144 if (!this.delay || !this.delay.hide) {
26150 this.timeout = setTimeout(function () {
26151 //Roo.log("leave - timeout");
26153 if (_t.hoverState == 'out') {
26155 Roo.bootstrap.Tooltip.currentEl = false;
26160 show : function (msg)
26163 this.render(document.body);
26166 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26168 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26170 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26172 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26174 var placement = typeof this.placement == 'function' ?
26175 this.placement.call(this, this.el, on_el) :
26178 var autoToken = /\s?auto?\s?/i;
26179 var autoPlace = autoToken.test(placement);
26181 placement = placement.replace(autoToken, '') || 'top';
26185 //this.el.setXY([0,0]);
26187 //this.el.dom.style.display='block';
26189 //this.el.appendTo(on_el);
26191 var p = this.getPosition();
26192 var box = this.el.getBox();
26198 var align = this.alignment[placement];
26200 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26202 if(placement == 'top' || placement == 'bottom'){
26204 placement = 'right';
26207 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26208 placement = 'left';
26211 var scroll = Roo.select('body', true).first().getScroll();
26213 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26217 align = this.alignment[placement];
26220 this.el.alignTo(this.bindEl, align[0],align[1]);
26221 //var arrow = this.el.select('.arrow',true).first();
26222 //arrow.set(align[2],
26224 this.el.addClass(placement);
26226 this.el.addClass('in fade');
26228 this.hoverState = null;
26230 if (this.el.hasClass('fade')) {
26241 //this.el.setXY([0,0]);
26242 this.el.removeClass('in');
26258 * @class Roo.bootstrap.LocationPicker
26259 * @extends Roo.bootstrap.Component
26260 * Bootstrap LocationPicker class
26261 * @cfg {Number} latitude Position when init default 0
26262 * @cfg {Number} longitude Position when init default 0
26263 * @cfg {Number} zoom default 15
26264 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26265 * @cfg {Boolean} mapTypeControl default false
26266 * @cfg {Boolean} disableDoubleClickZoom default false
26267 * @cfg {Boolean} scrollwheel default true
26268 * @cfg {Boolean} streetViewControl default false
26269 * @cfg {Number} radius default 0
26270 * @cfg {String} locationName
26271 * @cfg {Boolean} draggable default true
26272 * @cfg {Boolean} enableAutocomplete default false
26273 * @cfg {Boolean} enableReverseGeocode default true
26274 * @cfg {String} markerTitle
26277 * Create a new LocationPicker
26278 * @param {Object} config The config object
26282 Roo.bootstrap.LocationPicker = function(config){
26284 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26289 * Fires when the picker initialized.
26290 * @param {Roo.bootstrap.LocationPicker} this
26291 * @param {Google Location} location
26295 * @event positionchanged
26296 * Fires when the picker position changed.
26297 * @param {Roo.bootstrap.LocationPicker} this
26298 * @param {Google Location} location
26300 positionchanged : true,
26303 * Fires when the map resize.
26304 * @param {Roo.bootstrap.LocationPicker} this
26309 * Fires when the map show.
26310 * @param {Roo.bootstrap.LocationPicker} this
26315 * Fires when the map hide.
26316 * @param {Roo.bootstrap.LocationPicker} this
26321 * Fires when click the map.
26322 * @param {Roo.bootstrap.LocationPicker} this
26323 * @param {Map event} e
26327 * @event mapRightClick
26328 * Fires when right click the map.
26329 * @param {Roo.bootstrap.LocationPicker} this
26330 * @param {Map event} e
26332 mapRightClick : true,
26334 * @event markerClick
26335 * Fires when click the marker.
26336 * @param {Roo.bootstrap.LocationPicker} this
26337 * @param {Map event} e
26339 markerClick : true,
26341 * @event markerRightClick
26342 * Fires when right click the marker.
26343 * @param {Roo.bootstrap.LocationPicker} this
26344 * @param {Map event} e
26346 markerRightClick : true,
26348 * @event OverlayViewDraw
26349 * Fires when OverlayView Draw
26350 * @param {Roo.bootstrap.LocationPicker} this
26352 OverlayViewDraw : true,
26354 * @event OverlayViewOnAdd
26355 * Fires when OverlayView Draw
26356 * @param {Roo.bootstrap.LocationPicker} this
26358 OverlayViewOnAdd : true,
26360 * @event OverlayViewOnRemove
26361 * Fires when OverlayView Draw
26362 * @param {Roo.bootstrap.LocationPicker} this
26364 OverlayViewOnRemove : true,
26366 * @event OverlayViewShow
26367 * Fires when OverlayView Draw
26368 * @param {Roo.bootstrap.LocationPicker} this
26369 * @param {Pixel} cpx
26371 OverlayViewShow : true,
26373 * @event OverlayViewHide
26374 * Fires when OverlayView Draw
26375 * @param {Roo.bootstrap.LocationPicker} this
26377 OverlayViewHide : true,
26379 * @event loadexception
26380 * Fires when load google lib failed.
26381 * @param {Roo.bootstrap.LocationPicker} this
26383 loadexception : true
26388 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26390 gMapContext: false,
26396 mapTypeControl: false,
26397 disableDoubleClickZoom: false,
26399 streetViewControl: false,
26403 enableAutocomplete: false,
26404 enableReverseGeocode: true,
26407 getAutoCreate: function()
26412 cls: 'roo-location-picker'
26418 initEvents: function(ct, position)
26420 if(!this.el.getWidth() || this.isApplied()){
26424 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26429 initial: function()
26431 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26432 this.fireEvent('loadexception', this);
26436 if(!this.mapTypeId){
26437 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26440 this.gMapContext = this.GMapContext();
26442 this.initOverlayView();
26444 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26448 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26449 _this.setPosition(_this.gMapContext.marker.position);
26452 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26453 _this.fireEvent('mapClick', this, event);
26457 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26458 _this.fireEvent('mapRightClick', this, event);
26462 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26463 _this.fireEvent('markerClick', this, event);
26467 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26468 _this.fireEvent('markerRightClick', this, event);
26472 this.setPosition(this.gMapContext.location);
26474 this.fireEvent('initial', this, this.gMapContext.location);
26477 initOverlayView: function()
26481 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26485 _this.fireEvent('OverlayViewDraw', _this);
26490 _this.fireEvent('OverlayViewOnAdd', _this);
26493 onRemove: function()
26495 _this.fireEvent('OverlayViewOnRemove', _this);
26498 show: function(cpx)
26500 _this.fireEvent('OverlayViewShow', _this, cpx);
26505 _this.fireEvent('OverlayViewHide', _this);
26511 fromLatLngToContainerPixel: function(event)
26513 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26516 isApplied: function()
26518 return this.getGmapContext() == false ? false : true;
26521 getGmapContext: function()
26523 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26526 GMapContext: function()
26528 var position = new google.maps.LatLng(this.latitude, this.longitude);
26530 var _map = new google.maps.Map(this.el.dom, {
26533 mapTypeId: this.mapTypeId,
26534 mapTypeControl: this.mapTypeControl,
26535 disableDoubleClickZoom: this.disableDoubleClickZoom,
26536 scrollwheel: this.scrollwheel,
26537 streetViewControl: this.streetViewControl,
26538 locationName: this.locationName,
26539 draggable: this.draggable,
26540 enableAutocomplete: this.enableAutocomplete,
26541 enableReverseGeocode: this.enableReverseGeocode
26544 var _marker = new google.maps.Marker({
26545 position: position,
26547 title: this.markerTitle,
26548 draggable: this.draggable
26555 location: position,
26556 radius: this.radius,
26557 locationName: this.locationName,
26558 addressComponents: {
26559 formatted_address: null,
26560 addressLine1: null,
26561 addressLine2: null,
26563 streetNumber: null,
26567 stateOrProvince: null
26570 domContainer: this.el.dom,
26571 geodecoder: new google.maps.Geocoder()
26575 drawCircle: function(center, radius, options)
26577 if (this.gMapContext.circle != null) {
26578 this.gMapContext.circle.setMap(null);
26582 options = Roo.apply({}, options, {
26583 strokeColor: "#0000FF",
26584 strokeOpacity: .35,
26586 fillColor: "#0000FF",
26590 options.map = this.gMapContext.map;
26591 options.radius = radius;
26592 options.center = center;
26593 this.gMapContext.circle = new google.maps.Circle(options);
26594 return this.gMapContext.circle;
26600 setPosition: function(location)
26602 this.gMapContext.location = location;
26603 this.gMapContext.marker.setPosition(location);
26604 this.gMapContext.map.panTo(location);
26605 this.drawCircle(location, this.gMapContext.radius, {});
26609 if (this.gMapContext.settings.enableReverseGeocode) {
26610 this.gMapContext.geodecoder.geocode({
26611 latLng: this.gMapContext.location
26612 }, function(results, status) {
26614 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26615 _this.gMapContext.locationName = results[0].formatted_address;
26616 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26618 _this.fireEvent('positionchanged', this, location);
26625 this.fireEvent('positionchanged', this, location);
26630 google.maps.event.trigger(this.gMapContext.map, "resize");
26632 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26634 this.fireEvent('resize', this);
26637 setPositionByLatLng: function(latitude, longitude)
26639 this.setPosition(new google.maps.LatLng(latitude, longitude));
26642 getCurrentPosition: function()
26645 latitude: this.gMapContext.location.lat(),
26646 longitude: this.gMapContext.location.lng()
26650 getAddressName: function()
26652 return this.gMapContext.locationName;
26655 getAddressComponents: function()
26657 return this.gMapContext.addressComponents;
26660 address_component_from_google_geocode: function(address_components)
26664 for (var i = 0; i < address_components.length; i++) {
26665 var component = address_components[i];
26666 if (component.types.indexOf("postal_code") >= 0) {
26667 result.postalCode = component.short_name;
26668 } else if (component.types.indexOf("street_number") >= 0) {
26669 result.streetNumber = component.short_name;
26670 } else if (component.types.indexOf("route") >= 0) {
26671 result.streetName = component.short_name;
26672 } else if (component.types.indexOf("neighborhood") >= 0) {
26673 result.city = component.short_name;
26674 } else if (component.types.indexOf("locality") >= 0) {
26675 result.city = component.short_name;
26676 } else if (component.types.indexOf("sublocality") >= 0) {
26677 result.district = component.short_name;
26678 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26679 result.stateOrProvince = component.short_name;
26680 } else if (component.types.indexOf("country") >= 0) {
26681 result.country = component.short_name;
26685 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26686 result.addressLine2 = "";
26690 setZoomLevel: function(zoom)
26692 this.gMapContext.map.setZoom(zoom);
26705 this.fireEvent('show', this);
26716 this.fireEvent('hide', this);
26721 Roo.apply(Roo.bootstrap.LocationPicker, {
26723 OverlayView : function(map, options)
26725 options = options || {};
26739 * @class Roo.bootstrap.Alert
26740 * @extends Roo.bootstrap.Component
26741 * Bootstrap Alert class
26742 * @cfg {String} title The title of alert
26743 * @cfg {String} html The content of alert
26744 * @cfg {String} weight ( success | info | warning | danger )
26745 * @cfg {String} faicon font-awesomeicon
26748 * Create a new alert
26749 * @param {Object} config The config object
26753 Roo.bootstrap.Alert = function(config){
26754 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26758 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26765 getAutoCreate : function()
26774 cls : 'roo-alert-icon'
26779 cls : 'roo-alert-title',
26784 cls : 'roo-alert-text',
26791 cfg.cn[0].cls += ' fa ' + this.faicon;
26795 cfg.cls += ' alert-' + this.weight;
26801 initEvents: function()
26803 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26806 setTitle : function(str)
26808 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26811 setText : function(str)
26813 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26816 setWeight : function(weight)
26819 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26822 this.weight = weight;
26824 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26827 setIcon : function(icon)
26830 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26833 this.faicon = icon;
26835 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26856 * @class Roo.bootstrap.UploadCropbox
26857 * @extends Roo.bootstrap.Component
26858 * Bootstrap UploadCropbox class
26859 * @cfg {String} emptyText show when image has been loaded
26860 * @cfg {String} rotateNotify show when image too small to rotate
26861 * @cfg {Number} errorTimeout default 3000
26862 * @cfg {Number} minWidth default 300
26863 * @cfg {Number} minHeight default 300
26864 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26865 * @cfg {Boolean} isDocument (true|false) default false
26866 * @cfg {String} url action url
26867 * @cfg {String} paramName default 'imageUpload'
26868 * @cfg {String} method default POST
26869 * @cfg {Boolean} loadMask (true|false) default true
26870 * @cfg {Boolean} loadingText default 'Loading...'
26873 * Create a new UploadCropbox
26874 * @param {Object} config The config object
26877 Roo.bootstrap.UploadCropbox = function(config){
26878 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26882 * @event beforeselectfile
26883 * Fire before select file
26884 * @param {Roo.bootstrap.UploadCropbox} this
26886 "beforeselectfile" : true,
26889 * Fire after initEvent
26890 * @param {Roo.bootstrap.UploadCropbox} this
26895 * Fire after initEvent
26896 * @param {Roo.bootstrap.UploadCropbox} this
26897 * @param {String} data
26902 * Fire when preparing the file data
26903 * @param {Roo.bootstrap.UploadCropbox} this
26904 * @param {Object} file
26909 * Fire when get exception
26910 * @param {Roo.bootstrap.UploadCropbox} this
26911 * @param {XMLHttpRequest} xhr
26913 "exception" : true,
26915 * @event beforeloadcanvas
26916 * Fire before load the canvas
26917 * @param {Roo.bootstrap.UploadCropbox} this
26918 * @param {String} src
26920 "beforeloadcanvas" : true,
26923 * Fire when trash image
26924 * @param {Roo.bootstrap.UploadCropbox} this
26929 * Fire when download the image
26930 * @param {Roo.bootstrap.UploadCropbox} this
26934 * @event footerbuttonclick
26935 * Fire when footerbuttonclick
26936 * @param {Roo.bootstrap.UploadCropbox} this
26937 * @param {String} type
26939 "footerbuttonclick" : true,
26943 * @param {Roo.bootstrap.UploadCropbox} this
26948 * Fire when rotate the image
26949 * @param {Roo.bootstrap.UploadCropbox} this
26950 * @param {String} pos
26955 * Fire when inspect the file
26956 * @param {Roo.bootstrap.UploadCropbox} this
26957 * @param {Object} file
26962 * Fire when xhr upload the file
26963 * @param {Roo.bootstrap.UploadCropbox} this
26964 * @param {Object} data
26969 * Fire when arrange the file data
26970 * @param {Roo.bootstrap.UploadCropbox} this
26971 * @param {Object} formData
26976 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26979 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26981 emptyText : 'Click to upload image',
26982 rotateNotify : 'Image is too small to rotate',
26983 errorTimeout : 3000,
26997 cropType : 'image/jpeg',
26999 canvasLoaded : false,
27000 isDocument : false,
27002 paramName : 'imageUpload',
27004 loadingText : 'Loading...',
27007 getAutoCreate : function()
27011 cls : 'roo-upload-cropbox',
27015 cls : 'roo-upload-cropbox-selector',
27020 cls : 'roo-upload-cropbox-body',
27021 style : 'cursor:pointer',
27025 cls : 'roo-upload-cropbox-preview'
27029 cls : 'roo-upload-cropbox-thumb'
27033 cls : 'roo-upload-cropbox-empty-notify',
27034 html : this.emptyText
27038 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27039 html : this.rotateNotify
27045 cls : 'roo-upload-cropbox-footer',
27048 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27058 onRender : function(ct, position)
27060 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27062 if (this.buttons.length) {
27064 Roo.each(this.buttons, function(bb) {
27066 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27068 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27074 this.maskEl = this.el;
27078 initEvents : function()
27080 this.urlAPI = (window.createObjectURL && window) ||
27081 (window.URL && URL.revokeObjectURL && URL) ||
27082 (window.webkitURL && webkitURL);
27084 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27085 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27087 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27088 this.selectorEl.hide();
27090 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27091 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27093 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27094 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27095 this.thumbEl.hide();
27097 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27098 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27100 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27101 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27102 this.errorEl.hide();
27104 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27105 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27106 this.footerEl.hide();
27108 this.setThumbBoxSize();
27114 this.fireEvent('initial', this);
27121 window.addEventListener("resize", function() { _this.resize(); } );
27123 this.bodyEl.on('click', this.beforeSelectFile, this);
27126 this.bodyEl.on('touchstart', this.onTouchStart, this);
27127 this.bodyEl.on('touchmove', this.onTouchMove, this);
27128 this.bodyEl.on('touchend', this.onTouchEnd, this);
27132 this.bodyEl.on('mousedown', this.onMouseDown, this);
27133 this.bodyEl.on('mousemove', this.onMouseMove, this);
27134 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27135 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27136 Roo.get(document).on('mouseup', this.onMouseUp, this);
27139 this.selectorEl.on('change', this.onFileSelected, this);
27145 this.baseScale = 1;
27147 this.baseRotate = 1;
27148 this.dragable = false;
27149 this.pinching = false;
27152 this.cropData = false;
27153 this.notifyEl.dom.innerHTML = this.emptyText;
27155 this.selectorEl.dom.value = '';
27159 resize : function()
27161 if(this.fireEvent('resize', this) != false){
27162 this.setThumbBoxPosition();
27163 this.setCanvasPosition();
27167 onFooterButtonClick : function(e, el, o, type)
27170 case 'rotate-left' :
27171 this.onRotateLeft(e);
27173 case 'rotate-right' :
27174 this.onRotateRight(e);
27177 this.beforeSelectFile(e);
27192 this.fireEvent('footerbuttonclick', this, type);
27195 beforeSelectFile : function(e)
27197 e.preventDefault();
27199 if(this.fireEvent('beforeselectfile', this) != false){
27200 this.selectorEl.dom.click();
27204 onFileSelected : function(e)
27206 e.preventDefault();
27208 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27212 var file = this.selectorEl.dom.files[0];
27214 if(this.fireEvent('inspect', this, file) != false){
27215 this.prepare(file);
27220 trash : function(e)
27222 this.fireEvent('trash', this);
27225 download : function(e)
27227 this.fireEvent('download', this);
27230 loadCanvas : function(src)
27232 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27236 this.imageEl = document.createElement('img');
27240 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27242 this.imageEl.src = src;
27246 onLoadCanvas : function()
27248 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27249 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27251 this.bodyEl.un('click', this.beforeSelectFile, this);
27253 this.notifyEl.hide();
27254 this.thumbEl.show();
27255 this.footerEl.show();
27257 this.baseRotateLevel();
27259 if(this.isDocument){
27260 this.setThumbBoxSize();
27263 this.setThumbBoxPosition();
27265 this.baseScaleLevel();
27271 this.canvasLoaded = true;
27274 this.maskEl.unmask();
27279 setCanvasPosition : function()
27281 if(!this.canvasEl){
27285 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27286 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27288 this.previewEl.setLeft(pw);
27289 this.previewEl.setTop(ph);
27293 onMouseDown : function(e)
27297 this.dragable = true;
27298 this.pinching = false;
27300 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27301 this.dragable = false;
27305 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27306 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27310 onMouseMove : function(e)
27314 if(!this.canvasLoaded){
27318 if (!this.dragable){
27322 var minX = Math.ceil(this.thumbEl.getLeft(true));
27323 var minY = Math.ceil(this.thumbEl.getTop(true));
27325 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27326 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27328 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27329 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27331 x = x - this.mouseX;
27332 y = y - this.mouseY;
27334 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27335 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27337 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27338 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27340 this.previewEl.setLeft(bgX);
27341 this.previewEl.setTop(bgY);
27343 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27344 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27347 onMouseUp : function(e)
27351 this.dragable = false;
27354 onMouseWheel : function(e)
27358 this.startScale = this.scale;
27360 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27362 if(!this.zoomable()){
27363 this.scale = this.startScale;
27372 zoomable : function()
27374 var minScale = this.thumbEl.getWidth() / this.minWidth;
27376 if(this.minWidth < this.minHeight){
27377 minScale = this.thumbEl.getHeight() / this.minHeight;
27380 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27381 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27385 (this.rotate == 0 || this.rotate == 180) &&
27387 width > this.imageEl.OriginWidth ||
27388 height > this.imageEl.OriginHeight ||
27389 (width < this.minWidth && height < this.minHeight)
27397 (this.rotate == 90 || this.rotate == 270) &&
27399 width > this.imageEl.OriginWidth ||
27400 height > this.imageEl.OriginHeight ||
27401 (width < this.minHeight && height < this.minWidth)
27408 !this.isDocument &&
27409 (this.rotate == 0 || this.rotate == 180) &&
27411 width < this.minWidth ||
27412 width > this.imageEl.OriginWidth ||
27413 height < this.minHeight ||
27414 height > this.imageEl.OriginHeight
27421 !this.isDocument &&
27422 (this.rotate == 90 || this.rotate == 270) &&
27424 width < this.minHeight ||
27425 width > this.imageEl.OriginWidth ||
27426 height < this.minWidth ||
27427 height > this.imageEl.OriginHeight
27437 onRotateLeft : function(e)
27439 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27441 var minScale = this.thumbEl.getWidth() / this.minWidth;
27443 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27444 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27446 this.startScale = this.scale;
27448 while (this.getScaleLevel() < minScale){
27450 this.scale = this.scale + 1;
27452 if(!this.zoomable()){
27457 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27458 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27463 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27470 this.scale = this.startScale;
27472 this.onRotateFail();
27477 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27479 if(this.isDocument){
27480 this.setThumbBoxSize();
27481 this.setThumbBoxPosition();
27482 this.setCanvasPosition();
27487 this.fireEvent('rotate', this, 'left');
27491 onRotateRight : function(e)
27493 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27495 var minScale = this.thumbEl.getWidth() / this.minWidth;
27497 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27498 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27500 this.startScale = this.scale;
27502 while (this.getScaleLevel() < minScale){
27504 this.scale = this.scale + 1;
27506 if(!this.zoomable()){
27511 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27512 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27517 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27524 this.scale = this.startScale;
27526 this.onRotateFail();
27531 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27533 if(this.isDocument){
27534 this.setThumbBoxSize();
27535 this.setThumbBoxPosition();
27536 this.setCanvasPosition();
27541 this.fireEvent('rotate', this, 'right');
27544 onRotateFail : function()
27546 this.errorEl.show(true);
27550 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27555 this.previewEl.dom.innerHTML = '';
27557 var canvasEl = document.createElement("canvas");
27559 var contextEl = canvasEl.getContext("2d");
27561 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27562 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27563 var center = this.imageEl.OriginWidth / 2;
27565 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27566 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27567 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27568 center = this.imageEl.OriginHeight / 2;
27571 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27573 contextEl.translate(center, center);
27574 contextEl.rotate(this.rotate * Math.PI / 180);
27576 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27578 this.canvasEl = document.createElement("canvas");
27580 this.contextEl = this.canvasEl.getContext("2d");
27582 switch (this.rotate) {
27585 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27586 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27588 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27593 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27594 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27596 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27597 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);
27601 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27606 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27607 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27609 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27610 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);
27614 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);
27619 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27620 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27622 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27623 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27627 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);
27634 this.previewEl.appendChild(this.canvasEl);
27636 this.setCanvasPosition();
27641 if(!this.canvasLoaded){
27645 var imageCanvas = document.createElement("canvas");
27647 var imageContext = imageCanvas.getContext("2d");
27649 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27650 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27652 var center = imageCanvas.width / 2;
27654 imageContext.translate(center, center);
27656 imageContext.rotate(this.rotate * Math.PI / 180);
27658 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27660 var canvas = document.createElement("canvas");
27662 var context = canvas.getContext("2d");
27664 canvas.width = this.minWidth;
27665 canvas.height = this.minHeight;
27667 switch (this.rotate) {
27670 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27671 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27673 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27674 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27676 var targetWidth = this.minWidth - 2 * x;
27677 var targetHeight = this.minHeight - 2 * y;
27681 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27682 scale = targetWidth / width;
27685 if(x > 0 && y == 0){
27686 scale = targetHeight / height;
27689 if(x > 0 && y > 0){
27690 scale = targetWidth / width;
27692 if(width < height){
27693 scale = targetHeight / height;
27697 context.scale(scale, scale);
27699 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27700 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27702 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27703 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27705 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27710 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27711 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27713 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27714 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27716 var targetWidth = this.minWidth - 2 * x;
27717 var targetHeight = this.minHeight - 2 * y;
27721 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27722 scale = targetWidth / width;
27725 if(x > 0 && y == 0){
27726 scale = targetHeight / height;
27729 if(x > 0 && y > 0){
27730 scale = targetWidth / width;
27732 if(width < height){
27733 scale = targetHeight / height;
27737 context.scale(scale, scale);
27739 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27740 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27742 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27743 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27745 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27747 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27752 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27753 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27755 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27756 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27758 var targetWidth = this.minWidth - 2 * x;
27759 var targetHeight = this.minHeight - 2 * y;
27763 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27764 scale = targetWidth / width;
27767 if(x > 0 && y == 0){
27768 scale = targetHeight / height;
27771 if(x > 0 && y > 0){
27772 scale = targetWidth / width;
27774 if(width < height){
27775 scale = targetHeight / height;
27779 context.scale(scale, scale);
27781 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27782 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27784 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27785 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27787 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27788 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27790 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27795 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27796 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27798 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27799 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27801 var targetWidth = this.minWidth - 2 * x;
27802 var targetHeight = this.minHeight - 2 * y;
27806 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27807 scale = targetWidth / width;
27810 if(x > 0 && y == 0){
27811 scale = targetHeight / height;
27814 if(x > 0 && y > 0){
27815 scale = targetWidth / width;
27817 if(width < height){
27818 scale = targetHeight / height;
27822 context.scale(scale, scale);
27824 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27825 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27827 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27828 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27830 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27832 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27839 this.cropData = canvas.toDataURL(this.cropType);
27841 if(this.fireEvent('crop', this, this.cropData) !== false){
27842 this.process(this.file, this.cropData);
27849 setThumbBoxSize : function()
27853 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27854 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27855 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27857 this.minWidth = width;
27858 this.minHeight = height;
27860 if(this.rotate == 90 || this.rotate == 270){
27861 this.minWidth = height;
27862 this.minHeight = width;
27867 width = Math.ceil(this.minWidth * height / this.minHeight);
27869 if(this.minWidth > this.minHeight){
27871 height = Math.ceil(this.minHeight * width / this.minWidth);
27874 this.thumbEl.setStyle({
27875 width : width + 'px',
27876 height : height + 'px'
27883 setThumbBoxPosition : function()
27885 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27886 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27888 this.thumbEl.setLeft(x);
27889 this.thumbEl.setTop(y);
27893 baseRotateLevel : function()
27895 this.baseRotate = 1;
27898 typeof(this.exif) != 'undefined' &&
27899 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27900 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27902 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27905 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27909 baseScaleLevel : function()
27913 if(this.isDocument){
27915 if(this.baseRotate == 6 || this.baseRotate == 8){
27917 height = this.thumbEl.getHeight();
27918 this.baseScale = height / this.imageEl.OriginWidth;
27920 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27921 width = this.thumbEl.getWidth();
27922 this.baseScale = width / this.imageEl.OriginHeight;
27928 height = this.thumbEl.getHeight();
27929 this.baseScale = height / this.imageEl.OriginHeight;
27931 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27932 width = this.thumbEl.getWidth();
27933 this.baseScale = width / this.imageEl.OriginWidth;
27939 if(this.baseRotate == 6 || this.baseRotate == 8){
27941 width = this.thumbEl.getHeight();
27942 this.baseScale = width / this.imageEl.OriginHeight;
27944 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27945 height = this.thumbEl.getWidth();
27946 this.baseScale = height / this.imageEl.OriginHeight;
27949 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27950 height = this.thumbEl.getWidth();
27951 this.baseScale = height / this.imageEl.OriginHeight;
27953 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27954 width = this.thumbEl.getHeight();
27955 this.baseScale = width / this.imageEl.OriginWidth;
27962 width = this.thumbEl.getWidth();
27963 this.baseScale = width / this.imageEl.OriginWidth;
27965 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27966 height = this.thumbEl.getHeight();
27967 this.baseScale = height / this.imageEl.OriginHeight;
27970 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27972 height = this.thumbEl.getHeight();
27973 this.baseScale = height / this.imageEl.OriginHeight;
27975 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27976 width = this.thumbEl.getWidth();
27977 this.baseScale = width / this.imageEl.OriginWidth;
27985 getScaleLevel : function()
27987 return this.baseScale * Math.pow(1.1, this.scale);
27990 onTouchStart : function(e)
27992 if(!this.canvasLoaded){
27993 this.beforeSelectFile(e);
27997 var touches = e.browserEvent.touches;
28003 if(touches.length == 1){
28004 this.onMouseDown(e);
28008 if(touches.length != 2){
28014 for(var i = 0, finger; finger = touches[i]; i++){
28015 coords.push(finger.pageX, finger.pageY);
28018 var x = Math.pow(coords[0] - coords[2], 2);
28019 var y = Math.pow(coords[1] - coords[3], 2);
28021 this.startDistance = Math.sqrt(x + y);
28023 this.startScale = this.scale;
28025 this.pinching = true;
28026 this.dragable = false;
28030 onTouchMove : function(e)
28032 if(!this.pinching && !this.dragable){
28036 var touches = e.browserEvent.touches;
28043 this.onMouseMove(e);
28049 for(var i = 0, finger; finger = touches[i]; i++){
28050 coords.push(finger.pageX, finger.pageY);
28053 var x = Math.pow(coords[0] - coords[2], 2);
28054 var y = Math.pow(coords[1] - coords[3], 2);
28056 this.endDistance = Math.sqrt(x + y);
28058 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28060 if(!this.zoomable()){
28061 this.scale = this.startScale;
28069 onTouchEnd : function(e)
28071 this.pinching = false;
28072 this.dragable = false;
28076 process : function(file, crop)
28079 this.maskEl.mask(this.loadingText);
28082 this.xhr = new XMLHttpRequest();
28084 file.xhr = this.xhr;
28086 this.xhr.open(this.method, this.url, true);
28089 "Accept": "application/json",
28090 "Cache-Control": "no-cache",
28091 "X-Requested-With": "XMLHttpRequest"
28094 for (var headerName in headers) {
28095 var headerValue = headers[headerName];
28097 this.xhr.setRequestHeader(headerName, headerValue);
28103 this.xhr.onload = function()
28105 _this.xhrOnLoad(_this.xhr);
28108 this.xhr.onerror = function()
28110 _this.xhrOnError(_this.xhr);
28113 var formData = new FormData();
28115 formData.append('returnHTML', 'NO');
28118 formData.append('crop', crop);
28121 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28122 formData.append(this.paramName, file, file.name);
28125 if(typeof(file.filename) != 'undefined'){
28126 formData.append('filename', file.filename);
28129 if(typeof(file.mimetype) != 'undefined'){
28130 formData.append('mimetype', file.mimetype);
28133 if(this.fireEvent('arrange', this, formData) != false){
28134 this.xhr.send(formData);
28138 xhrOnLoad : function(xhr)
28141 this.maskEl.unmask();
28144 if (xhr.readyState !== 4) {
28145 this.fireEvent('exception', this, xhr);
28149 var response = Roo.decode(xhr.responseText);
28151 if(!response.success){
28152 this.fireEvent('exception', this, xhr);
28156 var response = Roo.decode(xhr.responseText);
28158 this.fireEvent('upload', this, response);
28162 xhrOnError : function()
28165 this.maskEl.unmask();
28168 Roo.log('xhr on error');
28170 var response = Roo.decode(xhr.responseText);
28176 prepare : function(file)
28179 this.maskEl.mask(this.loadingText);
28185 if(typeof(file) === 'string'){
28186 this.loadCanvas(file);
28190 if(!file || !this.urlAPI){
28195 this.cropType = file.type;
28199 if(this.fireEvent('prepare', this, this.file) != false){
28201 var reader = new FileReader();
28203 reader.onload = function (e) {
28204 if (e.target.error) {
28205 Roo.log(e.target.error);
28209 var buffer = e.target.result,
28210 dataView = new DataView(buffer),
28212 maxOffset = dataView.byteLength - 4,
28216 if (dataView.getUint16(0) === 0xffd8) {
28217 while (offset < maxOffset) {
28218 markerBytes = dataView.getUint16(offset);
28220 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28221 markerLength = dataView.getUint16(offset + 2) + 2;
28222 if (offset + markerLength > dataView.byteLength) {
28223 Roo.log('Invalid meta data: Invalid segment size.');
28227 if(markerBytes == 0xffe1){
28228 _this.parseExifData(
28235 offset += markerLength;
28245 var url = _this.urlAPI.createObjectURL(_this.file);
28247 _this.loadCanvas(url);
28252 reader.readAsArrayBuffer(this.file);
28258 parseExifData : function(dataView, offset, length)
28260 var tiffOffset = offset + 10,
28264 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28265 // No Exif data, might be XMP data instead
28269 // Check for the ASCII code for "Exif" (0x45786966):
28270 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28271 // No Exif data, might be XMP data instead
28274 if (tiffOffset + 8 > dataView.byteLength) {
28275 Roo.log('Invalid Exif data: Invalid segment size.');
28278 // Check for the two null bytes:
28279 if (dataView.getUint16(offset + 8) !== 0x0000) {
28280 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28283 // Check the byte alignment:
28284 switch (dataView.getUint16(tiffOffset)) {
28286 littleEndian = true;
28289 littleEndian = false;
28292 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28295 // Check for the TIFF tag marker (0x002A):
28296 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28297 Roo.log('Invalid Exif data: Missing TIFF marker.');
28300 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28301 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28303 this.parseExifTags(
28306 tiffOffset + dirOffset,
28311 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28316 if (dirOffset + 6 > dataView.byteLength) {
28317 Roo.log('Invalid Exif data: Invalid directory offset.');
28320 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28321 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28322 if (dirEndOffset + 4 > dataView.byteLength) {
28323 Roo.log('Invalid Exif data: Invalid directory size.');
28326 for (i = 0; i < tagsNumber; i += 1) {
28330 dirOffset + 2 + 12 * i, // tag offset
28334 // Return the offset to the next directory:
28335 return dataView.getUint32(dirEndOffset, littleEndian);
28338 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28340 var tag = dataView.getUint16(offset, littleEndian);
28342 this.exif[tag] = this.getExifValue(
28346 dataView.getUint16(offset + 2, littleEndian), // tag type
28347 dataView.getUint32(offset + 4, littleEndian), // tag length
28352 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28354 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28363 Roo.log('Invalid Exif data: Invalid tag type.');
28367 tagSize = tagType.size * length;
28368 // Determine if the value is contained in the dataOffset bytes,
28369 // or if the value at the dataOffset is a pointer to the actual data:
28370 dataOffset = tagSize > 4 ?
28371 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28372 if (dataOffset + tagSize > dataView.byteLength) {
28373 Roo.log('Invalid Exif data: Invalid data offset.');
28376 if (length === 1) {
28377 return tagType.getValue(dataView, dataOffset, littleEndian);
28380 for (i = 0; i < length; i += 1) {
28381 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28384 if (tagType.ascii) {
28386 // Concatenate the chars:
28387 for (i = 0; i < values.length; i += 1) {
28389 // Ignore the terminating NULL byte(s):
28390 if (c === '\u0000') {
28402 Roo.apply(Roo.bootstrap.UploadCropbox, {
28404 'Orientation': 0x0112
28408 1: 0, //'top-left',
28410 3: 180, //'bottom-right',
28411 // 4: 'bottom-left',
28413 6: 90, //'right-top',
28414 // 7: 'right-bottom',
28415 8: 270 //'left-bottom'
28419 // byte, 8-bit unsigned int:
28421 getValue: function (dataView, dataOffset) {
28422 return dataView.getUint8(dataOffset);
28426 // ascii, 8-bit byte:
28428 getValue: function (dataView, dataOffset) {
28429 return String.fromCharCode(dataView.getUint8(dataOffset));
28434 // short, 16 bit int:
28436 getValue: function (dataView, dataOffset, littleEndian) {
28437 return dataView.getUint16(dataOffset, littleEndian);
28441 // long, 32 bit int:
28443 getValue: function (dataView, dataOffset, littleEndian) {
28444 return dataView.getUint32(dataOffset, littleEndian);
28448 // rational = two long values, first is numerator, second is denominator:
28450 getValue: function (dataView, dataOffset, littleEndian) {
28451 return dataView.getUint32(dataOffset, littleEndian) /
28452 dataView.getUint32(dataOffset + 4, littleEndian);
28456 // slong, 32 bit signed int:
28458 getValue: function (dataView, dataOffset, littleEndian) {
28459 return dataView.getInt32(dataOffset, littleEndian);
28463 // srational, two slongs, first is numerator, second is denominator:
28465 getValue: function (dataView, dataOffset, littleEndian) {
28466 return dataView.getInt32(dataOffset, littleEndian) /
28467 dataView.getInt32(dataOffset + 4, littleEndian);
28477 cls : 'btn-group roo-upload-cropbox-rotate-left',
28478 action : 'rotate-left',
28482 cls : 'btn btn-default',
28483 html : '<i class="fa fa-undo"></i>'
28489 cls : 'btn-group roo-upload-cropbox-picture',
28490 action : 'picture',
28494 cls : 'btn btn-default',
28495 html : '<i class="fa fa-picture-o"></i>'
28501 cls : 'btn-group roo-upload-cropbox-rotate-right',
28502 action : 'rotate-right',
28506 cls : 'btn btn-default',
28507 html : '<i class="fa fa-repeat"></i>'
28515 cls : 'btn-group roo-upload-cropbox-rotate-left',
28516 action : 'rotate-left',
28520 cls : 'btn btn-default',
28521 html : '<i class="fa fa-undo"></i>'
28527 cls : 'btn-group roo-upload-cropbox-download',
28528 action : 'download',
28532 cls : 'btn btn-default',
28533 html : '<i class="fa fa-download"></i>'
28539 cls : 'btn-group roo-upload-cropbox-crop',
28544 cls : 'btn btn-default',
28545 html : '<i class="fa fa-crop"></i>'
28551 cls : 'btn-group roo-upload-cropbox-trash',
28556 cls : 'btn btn-default',
28557 html : '<i class="fa fa-trash"></i>'
28563 cls : 'btn-group roo-upload-cropbox-rotate-right',
28564 action : 'rotate-right',
28568 cls : 'btn btn-default',
28569 html : '<i class="fa fa-repeat"></i>'
28577 cls : 'btn-group roo-upload-cropbox-rotate-left',
28578 action : 'rotate-left',
28582 cls : 'btn btn-default',
28583 html : '<i class="fa fa-undo"></i>'
28589 cls : 'btn-group roo-upload-cropbox-rotate-right',
28590 action : 'rotate-right',
28594 cls : 'btn btn-default',
28595 html : '<i class="fa fa-repeat"></i>'
28608 * @class Roo.bootstrap.DocumentManager
28609 * @extends Roo.bootstrap.Component
28610 * Bootstrap DocumentManager class
28611 * @cfg {String} paramName default 'imageUpload'
28612 * @cfg {String} toolTipName default 'filename'
28613 * @cfg {String} method default POST
28614 * @cfg {String} url action url
28615 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28616 * @cfg {Boolean} multiple multiple upload default true
28617 * @cfg {Number} thumbSize default 300
28618 * @cfg {String} fieldLabel
28619 * @cfg {Number} labelWidth default 4
28620 * @cfg {String} labelAlign (left|top) default left
28621 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28622 * @cfg {Number} labellg set the width of label (1-12)
28623 * @cfg {Number} labelmd set the width of label (1-12)
28624 * @cfg {Number} labelsm set the width of label (1-12)
28625 * @cfg {Number} labelxs set the width of label (1-12)
28628 * Create a new DocumentManager
28629 * @param {Object} config The config object
28632 Roo.bootstrap.DocumentManager = function(config){
28633 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28636 this.delegates = [];
28641 * Fire when initial the DocumentManager
28642 * @param {Roo.bootstrap.DocumentManager} this
28647 * inspect selected file
28648 * @param {Roo.bootstrap.DocumentManager} this
28649 * @param {File} file
28654 * Fire when xhr load exception
28655 * @param {Roo.bootstrap.DocumentManager} this
28656 * @param {XMLHttpRequest} xhr
28658 "exception" : true,
28660 * @event afterupload
28661 * Fire when xhr load exception
28662 * @param {Roo.bootstrap.DocumentManager} this
28663 * @param {XMLHttpRequest} xhr
28665 "afterupload" : true,
28668 * prepare the form data
28669 * @param {Roo.bootstrap.DocumentManager} this
28670 * @param {Object} formData
28675 * Fire when remove the file
28676 * @param {Roo.bootstrap.DocumentManager} this
28677 * @param {Object} file
28682 * Fire after refresh the file
28683 * @param {Roo.bootstrap.DocumentManager} this
28688 * Fire after click the image
28689 * @param {Roo.bootstrap.DocumentManager} this
28690 * @param {Object} file
28695 * Fire when upload a image and editable set to true
28696 * @param {Roo.bootstrap.DocumentManager} this
28697 * @param {Object} file
28701 * @event beforeselectfile
28702 * Fire before select file
28703 * @param {Roo.bootstrap.DocumentManager} this
28705 "beforeselectfile" : true,
28708 * Fire before process file
28709 * @param {Roo.bootstrap.DocumentManager} this
28710 * @param {Object} file
28714 * @event previewrendered
28715 * Fire when preview rendered
28716 * @param {Roo.bootstrap.DocumentManager} this
28717 * @param {Object} file
28719 "previewrendered" : true,
28722 "previewResize" : true
28727 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28736 paramName : 'imageUpload',
28737 toolTipName : 'filename',
28740 labelAlign : 'left',
28750 getAutoCreate : function()
28752 var managerWidget = {
28754 cls : 'roo-document-manager',
28758 cls : 'roo-document-manager-selector',
28763 cls : 'roo-document-manager-uploader',
28767 cls : 'roo-document-manager-upload-btn',
28768 html : '<i class="fa fa-plus"></i>'
28779 cls : 'column col-md-12',
28784 if(this.fieldLabel.length){
28789 cls : 'column col-md-12',
28790 html : this.fieldLabel
28794 cls : 'column col-md-12',
28799 if(this.labelAlign == 'left'){
28804 html : this.fieldLabel
28813 if(this.labelWidth > 12){
28814 content[0].style = "width: " + this.labelWidth + 'px';
28817 if(this.labelWidth < 13 && this.labelmd == 0){
28818 this.labelmd = this.labelWidth;
28821 if(this.labellg > 0){
28822 content[0].cls += ' col-lg-' + this.labellg;
28823 content[1].cls += ' col-lg-' + (12 - this.labellg);
28826 if(this.labelmd > 0){
28827 content[0].cls += ' col-md-' + this.labelmd;
28828 content[1].cls += ' col-md-' + (12 - this.labelmd);
28831 if(this.labelsm > 0){
28832 content[0].cls += ' col-sm-' + this.labelsm;
28833 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28836 if(this.labelxs > 0){
28837 content[0].cls += ' col-xs-' + this.labelxs;
28838 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28846 cls : 'row clearfix',
28854 initEvents : function()
28856 this.managerEl = this.el.select('.roo-document-manager', true).first();
28857 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28859 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28860 this.selectorEl.hide();
28863 this.selectorEl.attr('multiple', 'multiple');
28866 this.selectorEl.on('change', this.onFileSelected, this);
28868 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28869 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28871 this.uploader.on('click', this.onUploaderClick, this);
28873 this.renderProgressDialog();
28877 window.addEventListener("resize", function() { _this.refresh(); } );
28879 this.fireEvent('initial', this);
28882 renderProgressDialog : function()
28886 this.progressDialog = new Roo.bootstrap.Modal({
28887 cls : 'roo-document-manager-progress-dialog',
28888 allow_close : false,
28898 btnclick : function() {
28899 _this.uploadCancel();
28905 this.progressDialog.render(Roo.get(document.body));
28907 this.progress = new Roo.bootstrap.Progress({
28908 cls : 'roo-document-manager-progress',
28913 this.progress.render(this.progressDialog.getChildContainer());
28915 this.progressBar = new Roo.bootstrap.ProgressBar({
28916 cls : 'roo-document-manager-progress-bar',
28919 aria_valuemax : 12,
28923 this.progressBar.render(this.progress.getChildContainer());
28926 onUploaderClick : function(e)
28928 e.preventDefault();
28930 if(this.fireEvent('beforeselectfile', this) != false){
28931 this.selectorEl.dom.click();
28936 onFileSelected : function(e)
28938 e.preventDefault();
28940 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28944 Roo.each(this.selectorEl.dom.files, function(file){
28945 if(this.fireEvent('inspect', this, file) != false){
28946 this.files.push(file);
28956 this.selectorEl.dom.value = '';
28958 if(!this.files || !this.files.length){
28962 if(this.boxes > 0 && this.files.length > this.boxes){
28963 this.files = this.files.slice(0, this.boxes);
28966 this.uploader.show();
28968 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28969 this.uploader.hide();
28978 Roo.each(this.files, function(file){
28980 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28981 var f = this.renderPreview(file);
28986 if(file.type.indexOf('image') != -1){
28987 this.delegates.push(
28989 _this.process(file);
28990 }).createDelegate(this)
28998 _this.process(file);
28999 }).createDelegate(this)
29004 this.files = files;
29006 this.delegates = this.delegates.concat(docs);
29008 if(!this.delegates.length){
29013 this.progressBar.aria_valuemax = this.delegates.length;
29020 arrange : function()
29022 if(!this.delegates.length){
29023 this.progressDialog.hide();
29028 var delegate = this.delegates.shift();
29030 this.progressDialog.show();
29032 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29034 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29039 refresh : function()
29041 this.uploader.show();
29043 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29044 this.uploader.hide();
29047 Roo.isTouch ? this.closable(false) : this.closable(true);
29049 this.fireEvent('refresh', this);
29052 onRemove : function(e, el, o)
29054 e.preventDefault();
29056 this.fireEvent('remove', this, o);
29060 remove : function(o)
29064 Roo.each(this.files, function(file){
29065 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29074 this.files = files;
29081 Roo.each(this.files, function(file){
29086 file.target.remove();
29095 onClick : function(e, el, o)
29097 e.preventDefault();
29099 this.fireEvent('click', this, o);
29103 closable : function(closable)
29105 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29107 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29119 xhrOnLoad : function(xhr)
29121 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29125 if (xhr.readyState !== 4) {
29127 this.fireEvent('exception', this, xhr);
29131 var response = Roo.decode(xhr.responseText);
29133 if(!response.success){
29135 this.fireEvent('exception', this, xhr);
29139 var file = this.renderPreview(response.data);
29141 this.files.push(file);
29145 this.fireEvent('afterupload', this, xhr);
29149 xhrOnError : function(xhr)
29151 Roo.log('xhr on error');
29153 var response = Roo.decode(xhr.responseText);
29160 process : function(file)
29162 if(this.fireEvent('process', this, file) !== false){
29163 if(this.editable && file.type.indexOf('image') != -1){
29164 this.fireEvent('edit', this, file);
29168 this.uploadStart(file, false);
29175 uploadStart : function(file, crop)
29177 this.xhr = new XMLHttpRequest();
29179 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29184 file.xhr = this.xhr;
29186 this.managerEl.createChild({
29188 cls : 'roo-document-manager-loading',
29192 tooltip : file.name,
29193 cls : 'roo-document-manager-thumb',
29194 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29200 this.xhr.open(this.method, this.url, true);
29203 "Accept": "application/json",
29204 "Cache-Control": "no-cache",
29205 "X-Requested-With": "XMLHttpRequest"
29208 for (var headerName in headers) {
29209 var headerValue = headers[headerName];
29211 this.xhr.setRequestHeader(headerName, headerValue);
29217 this.xhr.onload = function()
29219 _this.xhrOnLoad(_this.xhr);
29222 this.xhr.onerror = function()
29224 _this.xhrOnError(_this.xhr);
29227 var formData = new FormData();
29229 formData.append('returnHTML', 'NO');
29232 formData.append('crop', crop);
29235 formData.append(this.paramName, file, file.name);
29242 if(this.fireEvent('prepare', this, formData, options) != false){
29244 if(options.manually){
29248 this.xhr.send(formData);
29252 this.uploadCancel();
29255 uploadCancel : function()
29261 this.delegates = [];
29263 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29270 renderPreview : function(file)
29272 if(typeof(file.target) != 'undefined' && file.target){
29276 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29278 var previewEl = this.managerEl.createChild({
29280 cls : 'roo-document-manager-preview',
29284 tooltip : file[this.toolTipName],
29285 cls : 'roo-document-manager-thumb',
29286 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29291 html : '<i class="fa fa-times-circle"></i>'
29296 var close = previewEl.select('button.close', true).first();
29298 close.on('click', this.onRemove, this, file);
29300 file.target = previewEl;
29302 var image = previewEl.select('img', true).first();
29306 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29308 image.on('click', this.onClick, this, file);
29310 this.fireEvent('previewrendered', this, file);
29316 onPreviewLoad : function(file, image)
29318 if(typeof(file.target) == 'undefined' || !file.target){
29322 var width = image.dom.naturalWidth || image.dom.width;
29323 var height = image.dom.naturalHeight || image.dom.height;
29325 if(!this.previewResize) {
29329 if(width > height){
29330 file.target.addClass('wide');
29334 file.target.addClass('tall');
29339 uploadFromSource : function(file, crop)
29341 this.xhr = new XMLHttpRequest();
29343 this.managerEl.createChild({
29345 cls : 'roo-document-manager-loading',
29349 tooltip : file.name,
29350 cls : 'roo-document-manager-thumb',
29351 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29357 this.xhr.open(this.method, this.url, true);
29360 "Accept": "application/json",
29361 "Cache-Control": "no-cache",
29362 "X-Requested-With": "XMLHttpRequest"
29365 for (var headerName in headers) {
29366 var headerValue = headers[headerName];
29368 this.xhr.setRequestHeader(headerName, headerValue);
29374 this.xhr.onload = function()
29376 _this.xhrOnLoad(_this.xhr);
29379 this.xhr.onerror = function()
29381 _this.xhrOnError(_this.xhr);
29384 var formData = new FormData();
29386 formData.append('returnHTML', 'NO');
29388 formData.append('crop', crop);
29390 if(typeof(file.filename) != 'undefined'){
29391 formData.append('filename', file.filename);
29394 if(typeof(file.mimetype) != 'undefined'){
29395 formData.append('mimetype', file.mimetype);
29400 if(this.fireEvent('prepare', this, formData) != false){
29401 this.xhr.send(formData);
29411 * @class Roo.bootstrap.DocumentViewer
29412 * @extends Roo.bootstrap.Component
29413 * Bootstrap DocumentViewer class
29414 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29415 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29418 * Create a new DocumentViewer
29419 * @param {Object} config The config object
29422 Roo.bootstrap.DocumentViewer = function(config){
29423 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29428 * Fire after initEvent
29429 * @param {Roo.bootstrap.DocumentViewer} this
29435 * @param {Roo.bootstrap.DocumentViewer} this
29440 * Fire after download button
29441 * @param {Roo.bootstrap.DocumentViewer} this
29446 * Fire after trash button
29447 * @param {Roo.bootstrap.DocumentViewer} this
29454 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29456 showDownload : true,
29460 getAutoCreate : function()
29464 cls : 'roo-document-viewer',
29468 cls : 'roo-document-viewer-body',
29472 cls : 'roo-document-viewer-thumb',
29476 cls : 'roo-document-viewer-image'
29484 cls : 'roo-document-viewer-footer',
29487 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29491 cls : 'btn-group roo-document-viewer-download',
29495 cls : 'btn btn-default',
29496 html : '<i class="fa fa-download"></i>'
29502 cls : 'btn-group roo-document-viewer-trash',
29506 cls : 'btn btn-default',
29507 html : '<i class="fa fa-trash"></i>'
29520 initEvents : function()
29522 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29523 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29525 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29526 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29528 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29529 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29531 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29532 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29534 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29535 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29537 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29538 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29540 this.bodyEl.on('click', this.onClick, this);
29541 this.downloadBtn.on('click', this.onDownload, this);
29542 this.trashBtn.on('click', this.onTrash, this);
29544 this.downloadBtn.hide();
29545 this.trashBtn.hide();
29547 if(this.showDownload){
29548 this.downloadBtn.show();
29551 if(this.showTrash){
29552 this.trashBtn.show();
29555 if(!this.showDownload && !this.showTrash) {
29556 this.footerEl.hide();
29561 initial : function()
29563 this.fireEvent('initial', this);
29567 onClick : function(e)
29569 e.preventDefault();
29571 this.fireEvent('click', this);
29574 onDownload : function(e)
29576 e.preventDefault();
29578 this.fireEvent('download', this);
29581 onTrash : function(e)
29583 e.preventDefault();
29585 this.fireEvent('trash', this);
29597 * @class Roo.bootstrap.NavProgressBar
29598 * @extends Roo.bootstrap.Component
29599 * Bootstrap NavProgressBar class
29602 * Create a new nav progress bar
29603 * @param {Object} config The config object
29606 Roo.bootstrap.NavProgressBar = function(config){
29607 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29609 this.bullets = this.bullets || [];
29611 // Roo.bootstrap.NavProgressBar.register(this);
29615 * Fires when the active item changes
29616 * @param {Roo.bootstrap.NavProgressBar} this
29617 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29618 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29625 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29630 getAutoCreate : function()
29632 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29636 cls : 'roo-navigation-bar-group',
29640 cls : 'roo-navigation-top-bar'
29644 cls : 'roo-navigation-bullets-bar',
29648 cls : 'roo-navigation-bar'
29655 cls : 'roo-navigation-bottom-bar'
29665 initEvents: function()
29670 onRender : function(ct, position)
29672 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29674 if(this.bullets.length){
29675 Roo.each(this.bullets, function(b){
29684 addItem : function(cfg)
29686 var item = new Roo.bootstrap.NavProgressItem(cfg);
29688 item.parentId = this.id;
29689 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29692 var top = new Roo.bootstrap.Element({
29694 cls : 'roo-navigation-bar-text'
29697 var bottom = new Roo.bootstrap.Element({
29699 cls : 'roo-navigation-bar-text'
29702 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29703 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29705 var topText = new Roo.bootstrap.Element({
29707 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29710 var bottomText = new Roo.bootstrap.Element({
29712 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29715 topText.onRender(top.el, null);
29716 bottomText.onRender(bottom.el, null);
29719 item.bottomEl = bottom;
29722 this.barItems.push(item);
29727 getActive : function()
29729 var active = false;
29731 Roo.each(this.barItems, function(v){
29733 if (!v.isActive()) {
29745 setActiveItem : function(item)
29749 Roo.each(this.barItems, function(v){
29750 if (v.rid == item.rid) {
29754 if (v.isActive()) {
29755 v.setActive(false);
29760 item.setActive(true);
29762 this.fireEvent('changed', this, item, prev);
29765 getBarItem: function(rid)
29769 Roo.each(this.barItems, function(e) {
29770 if (e.rid != rid) {
29781 indexOfItem : function(item)
29785 Roo.each(this.barItems, function(v, i){
29787 if (v.rid != item.rid) {
29798 setActiveNext : function()
29800 var i = this.indexOfItem(this.getActive());
29802 if (i > this.barItems.length) {
29806 this.setActiveItem(this.barItems[i+1]);
29809 setActivePrev : function()
29811 var i = this.indexOfItem(this.getActive());
29817 this.setActiveItem(this.barItems[i-1]);
29820 format : function()
29822 if(!this.barItems.length){
29826 var width = 100 / this.barItems.length;
29828 Roo.each(this.barItems, function(i){
29829 i.el.setStyle('width', width + '%');
29830 i.topEl.el.setStyle('width', width + '%');
29831 i.bottomEl.el.setStyle('width', width + '%');
29840 * Nav Progress Item
29845 * @class Roo.bootstrap.NavProgressItem
29846 * @extends Roo.bootstrap.Component
29847 * Bootstrap NavProgressItem class
29848 * @cfg {String} rid the reference id
29849 * @cfg {Boolean} active (true|false) Is item active default false
29850 * @cfg {Boolean} disabled (true|false) Is item active default false
29851 * @cfg {String} html
29852 * @cfg {String} position (top|bottom) text position default bottom
29853 * @cfg {String} icon show icon instead of number
29856 * Create a new NavProgressItem
29857 * @param {Object} config The config object
29859 Roo.bootstrap.NavProgressItem = function(config){
29860 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29865 * The raw click event for the entire grid.
29866 * @param {Roo.bootstrap.NavProgressItem} this
29867 * @param {Roo.EventObject} e
29874 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29880 position : 'bottom',
29883 getAutoCreate : function()
29885 var iconCls = 'roo-navigation-bar-item-icon';
29887 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29891 cls: 'roo-navigation-bar-item',
29901 cfg.cls += ' active';
29904 cfg.cls += ' disabled';
29910 disable : function()
29912 this.setDisabled(true);
29915 enable : function()
29917 this.setDisabled(false);
29920 initEvents: function()
29922 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29924 this.iconEl.on('click', this.onClick, this);
29927 onClick : function(e)
29929 e.preventDefault();
29935 if(this.fireEvent('click', this, e) === false){
29939 this.parent().setActiveItem(this);
29942 isActive: function ()
29944 return this.active;
29947 setActive : function(state)
29949 if(this.active == state){
29953 this.active = state;
29956 this.el.addClass('active');
29960 this.el.removeClass('active');
29965 setDisabled : function(state)
29967 if(this.disabled == state){
29971 this.disabled = state;
29974 this.el.addClass('disabled');
29978 this.el.removeClass('disabled');
29981 tooltipEl : function()
29983 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29996 * @class Roo.bootstrap.FieldLabel
29997 * @extends Roo.bootstrap.Component
29998 * Bootstrap FieldLabel class
29999 * @cfg {String} html contents of the element
30000 * @cfg {String} tag tag of the element default label
30001 * @cfg {String} cls class of the element
30002 * @cfg {String} target label target
30003 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30004 * @cfg {String} invalidClass default "text-warning"
30005 * @cfg {String} validClass default "text-success"
30006 * @cfg {String} iconTooltip default "This field is required"
30007 * @cfg {String} indicatorpos (left|right) default left
30010 * Create a new FieldLabel
30011 * @param {Object} config The config object
30014 Roo.bootstrap.FieldLabel = function(config){
30015 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30020 * Fires after the field has been marked as invalid.
30021 * @param {Roo.form.FieldLabel} this
30022 * @param {String} msg The validation message
30027 * Fires after the field has been validated with no errors.
30028 * @param {Roo.form.FieldLabel} this
30034 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30041 invalidClass : 'has-warning',
30042 validClass : 'has-success',
30043 iconTooltip : 'This field is required',
30044 indicatorpos : 'left',
30046 getAutoCreate : function(){
30049 if (!this.allowBlank) {
30055 cls : 'roo-bootstrap-field-label ' + this.cls,
30060 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30061 tooltip : this.iconTooltip
30070 if(this.indicatorpos == 'right'){
30073 cls : 'roo-bootstrap-field-label ' + this.cls,
30082 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30083 tooltip : this.iconTooltip
30092 initEvents: function()
30094 Roo.bootstrap.Element.superclass.initEvents.call(this);
30096 this.indicator = this.indicatorEl();
30098 if(this.indicator){
30099 this.indicator.removeClass('visible');
30100 this.indicator.addClass('invisible');
30103 Roo.bootstrap.FieldLabel.register(this);
30106 indicatorEl : function()
30108 var indicator = this.el.select('i.roo-required-indicator',true).first();
30119 * Mark this field as valid
30121 markValid : function()
30123 if(this.indicator){
30124 this.indicator.removeClass('visible');
30125 this.indicator.addClass('invisible');
30128 this.el.removeClass(this.invalidClass);
30130 this.el.addClass(this.validClass);
30132 this.fireEvent('valid', this);
30136 * Mark this field as invalid
30137 * @param {String} msg The validation message
30139 markInvalid : function(msg)
30141 if(this.indicator){
30142 this.indicator.removeClass('invisible');
30143 this.indicator.addClass('visible');
30146 this.el.removeClass(this.validClass);
30148 this.el.addClass(this.invalidClass);
30150 this.fireEvent('invalid', this, msg);
30156 Roo.apply(Roo.bootstrap.FieldLabel, {
30161 * register a FieldLabel Group
30162 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30164 register : function(label)
30166 if(this.groups.hasOwnProperty(label.target)){
30170 this.groups[label.target] = label;
30174 * fetch a FieldLabel Group based on the target
30175 * @param {string} target
30176 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30178 get: function(target) {
30179 if (typeof(this.groups[target]) == 'undefined') {
30183 return this.groups[target] ;
30192 * page DateSplitField.
30198 * @class Roo.bootstrap.DateSplitField
30199 * @extends Roo.bootstrap.Component
30200 * Bootstrap DateSplitField class
30201 * @cfg {string} fieldLabel - the label associated
30202 * @cfg {Number} labelWidth set the width of label (0-12)
30203 * @cfg {String} labelAlign (top|left)
30204 * @cfg {Boolean} dayAllowBlank (true|false) default false
30205 * @cfg {Boolean} monthAllowBlank (true|false) default false
30206 * @cfg {Boolean} yearAllowBlank (true|false) default false
30207 * @cfg {string} dayPlaceholder
30208 * @cfg {string} monthPlaceholder
30209 * @cfg {string} yearPlaceholder
30210 * @cfg {string} dayFormat default 'd'
30211 * @cfg {string} monthFormat default 'm'
30212 * @cfg {string} yearFormat default 'Y'
30213 * @cfg {Number} labellg set the width of label (1-12)
30214 * @cfg {Number} labelmd set the width of label (1-12)
30215 * @cfg {Number} labelsm set the width of label (1-12)
30216 * @cfg {Number} labelxs set the width of label (1-12)
30220 * Create a new DateSplitField
30221 * @param {Object} config The config object
30224 Roo.bootstrap.DateSplitField = function(config){
30225 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30231 * getting the data of years
30232 * @param {Roo.bootstrap.DateSplitField} this
30233 * @param {Object} years
30238 * getting the data of days
30239 * @param {Roo.bootstrap.DateSplitField} this
30240 * @param {Object} days
30245 * Fires after the field has been marked as invalid.
30246 * @param {Roo.form.Field} this
30247 * @param {String} msg The validation message
30252 * Fires after the field has been validated with no errors.
30253 * @param {Roo.form.Field} this
30259 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30262 labelAlign : 'top',
30264 dayAllowBlank : false,
30265 monthAllowBlank : false,
30266 yearAllowBlank : false,
30267 dayPlaceholder : '',
30268 monthPlaceholder : '',
30269 yearPlaceholder : '',
30273 isFormField : true,
30279 getAutoCreate : function()
30283 cls : 'row roo-date-split-field-group',
30288 cls : 'form-hidden-field roo-date-split-field-group-value',
30294 var labelCls = 'col-md-12';
30295 var contentCls = 'col-md-4';
30297 if(this.fieldLabel){
30301 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30305 html : this.fieldLabel
30310 if(this.labelAlign == 'left'){
30312 if(this.labelWidth > 12){
30313 label.style = "width: " + this.labelWidth + 'px';
30316 if(this.labelWidth < 13 && this.labelmd == 0){
30317 this.labelmd = this.labelWidth;
30320 if(this.labellg > 0){
30321 labelCls = ' col-lg-' + this.labellg;
30322 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30325 if(this.labelmd > 0){
30326 labelCls = ' col-md-' + this.labelmd;
30327 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30330 if(this.labelsm > 0){
30331 labelCls = ' col-sm-' + this.labelsm;
30332 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30335 if(this.labelxs > 0){
30336 labelCls = ' col-xs-' + this.labelxs;
30337 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30341 label.cls += ' ' + labelCls;
30343 cfg.cn.push(label);
30346 Roo.each(['day', 'month', 'year'], function(t){
30349 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30356 inputEl: function ()
30358 return this.el.select('.roo-date-split-field-group-value', true).first();
30361 onRender : function(ct, position)
30365 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30367 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30369 this.dayField = new Roo.bootstrap.ComboBox({
30370 allowBlank : this.dayAllowBlank,
30371 alwaysQuery : true,
30372 displayField : 'value',
30375 forceSelection : true,
30377 placeholder : this.dayPlaceholder,
30378 selectOnFocus : true,
30379 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30380 triggerAction : 'all',
30382 valueField : 'value',
30383 store : new Roo.data.SimpleStore({
30384 data : (function() {
30386 _this.fireEvent('days', _this, days);
30389 fields : [ 'value' ]
30392 select : function (_self, record, index)
30394 _this.setValue(_this.getValue());
30399 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30401 this.monthField = new Roo.bootstrap.MonthField({
30402 after : '<i class=\"fa fa-calendar\"></i>',
30403 allowBlank : this.monthAllowBlank,
30404 placeholder : this.monthPlaceholder,
30407 render : function (_self)
30409 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30410 e.preventDefault();
30414 select : function (_self, oldvalue, newvalue)
30416 _this.setValue(_this.getValue());
30421 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30423 this.yearField = new Roo.bootstrap.ComboBox({
30424 allowBlank : this.yearAllowBlank,
30425 alwaysQuery : true,
30426 displayField : 'value',
30429 forceSelection : true,
30431 placeholder : this.yearPlaceholder,
30432 selectOnFocus : true,
30433 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30434 triggerAction : 'all',
30436 valueField : 'value',
30437 store : new Roo.data.SimpleStore({
30438 data : (function() {
30440 _this.fireEvent('years', _this, years);
30443 fields : [ 'value' ]
30446 select : function (_self, record, index)
30448 _this.setValue(_this.getValue());
30453 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30456 setValue : function(v, format)
30458 this.inputEl.dom.value = v;
30460 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30462 var d = Date.parseDate(v, f);
30469 this.setDay(d.format(this.dayFormat));
30470 this.setMonth(d.format(this.monthFormat));
30471 this.setYear(d.format(this.yearFormat));
30478 setDay : function(v)
30480 this.dayField.setValue(v);
30481 this.inputEl.dom.value = this.getValue();
30486 setMonth : function(v)
30488 this.monthField.setValue(v, true);
30489 this.inputEl.dom.value = this.getValue();
30494 setYear : function(v)
30496 this.yearField.setValue(v);
30497 this.inputEl.dom.value = this.getValue();
30502 getDay : function()
30504 return this.dayField.getValue();
30507 getMonth : function()
30509 return this.monthField.getValue();
30512 getYear : function()
30514 return this.yearField.getValue();
30517 getValue : function()
30519 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30521 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30531 this.inputEl.dom.value = '';
30536 validate : function()
30538 var d = this.dayField.validate();
30539 var m = this.monthField.validate();
30540 var y = this.yearField.validate();
30545 (!this.dayAllowBlank && !d) ||
30546 (!this.monthAllowBlank && !m) ||
30547 (!this.yearAllowBlank && !y)
30552 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30561 this.markInvalid();
30566 markValid : function()
30569 var label = this.el.select('label', true).first();
30570 var icon = this.el.select('i.fa-star', true).first();
30576 this.fireEvent('valid', this);
30580 * Mark this field as invalid
30581 * @param {String} msg The validation message
30583 markInvalid : function(msg)
30586 var label = this.el.select('label', true).first();
30587 var icon = this.el.select('i.fa-star', true).first();
30589 if(label && !icon){
30590 this.el.select('.roo-date-split-field-label', true).createChild({
30592 cls : 'text-danger fa fa-lg fa-star',
30593 tooltip : 'This field is required',
30594 style : 'margin-right:5px;'
30598 this.fireEvent('invalid', this, msg);
30601 clearInvalid : function()
30603 var label = this.el.select('label', true).first();
30604 var icon = this.el.select('i.fa-star', true).first();
30610 this.fireEvent('valid', this);
30613 getName: function()
30623 * http://masonry.desandro.com
30625 * The idea is to render all the bricks based on vertical width...
30627 * The original code extends 'outlayer' - we might need to use that....
30633 * @class Roo.bootstrap.LayoutMasonry
30634 * @extends Roo.bootstrap.Component
30635 * Bootstrap Layout Masonry class
30638 * Create a new Element
30639 * @param {Object} config The config object
30642 Roo.bootstrap.LayoutMasonry = function(config){
30644 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30648 Roo.bootstrap.LayoutMasonry.register(this);
30654 * Fire after layout the items
30655 * @param {Roo.bootstrap.LayoutMasonry} this
30656 * @param {Roo.EventObject} e
30663 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30666 * @cfg {Boolean} isLayoutInstant = no animation?
30668 isLayoutInstant : false, // needed?
30671 * @cfg {Number} boxWidth width of the columns
30676 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30681 * @cfg {Number} padWidth padding below box..
30686 * @cfg {Number} gutter gutter width..
30691 * @cfg {Number} maxCols maximum number of columns
30697 * @cfg {Boolean} isAutoInitial defalut true
30699 isAutoInitial : true,
30704 * @cfg {Boolean} isHorizontal defalut false
30706 isHorizontal : false,
30708 currentSize : null,
30714 bricks: null, //CompositeElement
30718 _isLayoutInited : false,
30720 // isAlternative : false, // only use for vertical layout...
30723 * @cfg {Number} alternativePadWidth padding below box..
30725 alternativePadWidth : 50,
30727 selectedBrick : [],
30729 getAutoCreate : function(){
30731 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30735 cls: 'blog-masonary-wrapper ' + this.cls,
30737 cls : 'mas-boxes masonary'
30744 getChildContainer: function( )
30746 if (this.boxesEl) {
30747 return this.boxesEl;
30750 this.boxesEl = this.el.select('.mas-boxes').first();
30752 return this.boxesEl;
30756 initEvents : function()
30760 if(this.isAutoInitial){
30761 Roo.log('hook children rendered');
30762 this.on('childrenrendered', function() {
30763 Roo.log('children rendered');
30769 initial : function()
30771 this.selectedBrick = [];
30773 this.currentSize = this.el.getBox(true);
30775 Roo.EventManager.onWindowResize(this.resize, this);
30777 if(!this.isAutoInitial){
30785 //this.layout.defer(500,this);
30789 resize : function()
30791 var cs = this.el.getBox(true);
30794 this.currentSize.width == cs.width &&
30795 this.currentSize.x == cs.x &&
30796 this.currentSize.height == cs.height &&
30797 this.currentSize.y == cs.y
30799 Roo.log("no change in with or X or Y");
30803 this.currentSize = cs;
30809 layout : function()
30811 this._resetLayout();
30813 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30815 this.layoutItems( isInstant );
30817 this._isLayoutInited = true;
30819 this.fireEvent('layout', this);
30823 _resetLayout : function()
30825 if(this.isHorizontal){
30826 this.horizontalMeasureColumns();
30830 this.verticalMeasureColumns();
30834 verticalMeasureColumns : function()
30836 this.getContainerWidth();
30838 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30839 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30843 var boxWidth = this.boxWidth + this.padWidth;
30845 if(this.containerWidth < this.boxWidth){
30846 boxWidth = this.containerWidth
30849 var containerWidth = this.containerWidth;
30851 var cols = Math.floor(containerWidth / boxWidth);
30853 this.cols = Math.max( cols, 1 );
30855 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30857 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30859 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30861 this.colWidth = boxWidth + avail - this.padWidth;
30863 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30864 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30867 horizontalMeasureColumns : function()
30869 this.getContainerWidth();
30871 var boxWidth = this.boxWidth;
30873 if(this.containerWidth < boxWidth){
30874 boxWidth = this.containerWidth;
30877 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30879 this.el.setHeight(boxWidth);
30883 getContainerWidth : function()
30885 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30888 layoutItems : function( isInstant )
30890 Roo.log(this.bricks);
30892 var items = Roo.apply([], this.bricks);
30894 if(this.isHorizontal){
30895 this._horizontalLayoutItems( items , isInstant );
30899 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30900 // this._verticalAlternativeLayoutItems( items , isInstant );
30904 this._verticalLayoutItems( items , isInstant );
30908 _verticalLayoutItems : function ( items , isInstant)
30910 if ( !items || !items.length ) {
30915 ['xs', 'xs', 'xs', 'tall'],
30916 ['xs', 'xs', 'tall'],
30917 ['xs', 'xs', 'sm'],
30918 ['xs', 'xs', 'xs'],
30924 ['sm', 'xs', 'xs'],
30928 ['tall', 'xs', 'xs', 'xs'],
30929 ['tall', 'xs', 'xs'],
30941 Roo.each(items, function(item, k){
30943 switch (item.size) {
30944 // these layouts take up a full box,
30955 boxes.push([item]);
30978 var filterPattern = function(box, length)
30986 var pattern = box.slice(0, length);
30990 Roo.each(pattern, function(i){
30991 format.push(i.size);
30994 Roo.each(standard, function(s){
30996 if(String(s) != String(format)){
31005 if(!match && length == 1){
31010 filterPattern(box, length - 1);
31014 queue.push(pattern);
31016 box = box.slice(length, box.length);
31018 filterPattern(box, 4);
31024 Roo.each(boxes, function(box, k){
31030 if(box.length == 1){
31035 filterPattern(box, 4);
31039 this._processVerticalLayoutQueue( queue, isInstant );
31043 // _verticalAlternativeLayoutItems : function( items , isInstant )
31045 // if ( !items || !items.length ) {
31049 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31053 _horizontalLayoutItems : function ( items , isInstant)
31055 if ( !items || !items.length || items.length < 3) {
31061 var eItems = items.slice(0, 3);
31063 items = items.slice(3, items.length);
31066 ['xs', 'xs', 'xs', 'wide'],
31067 ['xs', 'xs', 'wide'],
31068 ['xs', 'xs', 'sm'],
31069 ['xs', 'xs', 'xs'],
31075 ['sm', 'xs', 'xs'],
31079 ['wide', 'xs', 'xs', 'xs'],
31080 ['wide', 'xs', 'xs'],
31093 Roo.each(items, function(item, k){
31095 switch (item.size) {
31106 boxes.push([item]);
31130 var filterPattern = function(box, length)
31138 var pattern = box.slice(0, length);
31142 Roo.each(pattern, function(i){
31143 format.push(i.size);
31146 Roo.each(standard, function(s){
31148 if(String(s) != String(format)){
31157 if(!match && length == 1){
31162 filterPattern(box, length - 1);
31166 queue.push(pattern);
31168 box = box.slice(length, box.length);
31170 filterPattern(box, 4);
31176 Roo.each(boxes, function(box, k){
31182 if(box.length == 1){
31187 filterPattern(box, 4);
31194 var pos = this.el.getBox(true);
31198 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31200 var hit_end = false;
31202 Roo.each(queue, function(box){
31206 Roo.each(box, function(b){
31208 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31218 Roo.each(box, function(b){
31220 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31223 mx = Math.max(mx, b.x);
31227 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31231 Roo.each(box, function(b){
31233 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31247 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31250 /** Sets position of item in DOM
31251 * @param {Element} item
31252 * @param {Number} x - horizontal position
31253 * @param {Number} y - vertical position
31254 * @param {Boolean} isInstant - disables transitions
31256 _processVerticalLayoutQueue : function( queue, isInstant )
31258 var pos = this.el.getBox(true);
31263 for (var i = 0; i < this.cols; i++){
31267 Roo.each(queue, function(box, k){
31269 var col = k % this.cols;
31271 Roo.each(box, function(b,kk){
31273 b.el.position('absolute');
31275 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31276 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31278 if(b.size == 'md-left' || b.size == 'md-right'){
31279 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31280 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31283 b.el.setWidth(width);
31284 b.el.setHeight(height);
31286 b.el.select('iframe',true).setSize(width,height);
31290 for (var i = 0; i < this.cols; i++){
31292 if(maxY[i] < maxY[col]){
31297 col = Math.min(col, i);
31301 x = pos.x + col * (this.colWidth + this.padWidth);
31305 var positions = [];
31307 switch (box.length){
31309 positions = this.getVerticalOneBoxColPositions(x, y, box);
31312 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31315 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31318 positions = this.getVerticalFourBoxColPositions(x, y, box);
31324 Roo.each(box, function(b,kk){
31326 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31328 var sz = b.el.getSize();
31330 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31338 for (var i = 0; i < this.cols; i++){
31339 mY = Math.max(mY, maxY[i]);
31342 this.el.setHeight(mY - pos.y);
31346 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31348 // var pos = this.el.getBox(true);
31351 // var maxX = pos.right;
31353 // var maxHeight = 0;
31355 // Roo.each(items, function(item, k){
31359 // item.el.position('absolute');
31361 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31363 // item.el.setWidth(width);
31365 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31367 // item.el.setHeight(height);
31370 // item.el.setXY([x, y], isInstant ? false : true);
31372 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31375 // y = y + height + this.alternativePadWidth;
31377 // maxHeight = maxHeight + height + this.alternativePadWidth;
31381 // this.el.setHeight(maxHeight);
31385 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31387 var pos = this.el.getBox(true);
31392 var maxX = pos.right;
31394 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31396 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31398 Roo.each(queue, function(box, k){
31400 Roo.each(box, function(b, kk){
31402 b.el.position('absolute');
31404 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31405 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31407 if(b.size == 'md-left' || b.size == 'md-right'){
31408 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31409 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31412 b.el.setWidth(width);
31413 b.el.setHeight(height);
31421 var positions = [];
31423 switch (box.length){
31425 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31428 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31431 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31434 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31440 Roo.each(box, function(b,kk){
31442 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31444 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31452 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31454 Roo.each(eItems, function(b,k){
31456 b.size = (k == 0) ? 'sm' : 'xs';
31457 b.x = (k == 0) ? 2 : 1;
31458 b.y = (k == 0) ? 2 : 1;
31460 b.el.position('absolute');
31462 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31464 b.el.setWidth(width);
31466 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31468 b.el.setHeight(height);
31472 var positions = [];
31475 x : maxX - this.unitWidth * 2 - this.gutter,
31480 x : maxX - this.unitWidth,
31481 y : minY + (this.unitWidth + this.gutter) * 2
31485 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31489 Roo.each(eItems, function(b,k){
31491 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31497 getVerticalOneBoxColPositions : function(x, y, box)
31501 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31503 if(box[0].size == 'md-left'){
31507 if(box[0].size == 'md-right'){
31512 x : x + (this.unitWidth + this.gutter) * rand,
31519 getVerticalTwoBoxColPositions : function(x, y, box)
31523 if(box[0].size == 'xs'){
31527 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31531 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31545 x : x + (this.unitWidth + this.gutter) * 2,
31546 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31553 getVerticalThreeBoxColPositions : function(x, y, box)
31557 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31565 x : x + (this.unitWidth + this.gutter) * 1,
31570 x : x + (this.unitWidth + this.gutter) * 2,
31578 if(box[0].size == 'xs' && box[1].size == 'xs'){
31587 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31591 x : x + (this.unitWidth + this.gutter) * 1,
31605 x : x + (this.unitWidth + this.gutter) * 2,
31610 x : x + (this.unitWidth + this.gutter) * 2,
31611 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31618 getVerticalFourBoxColPositions : function(x, y, box)
31622 if(box[0].size == 'xs'){
31631 y : y + (this.unitHeight + this.gutter) * 1
31636 y : y + (this.unitHeight + this.gutter) * 2
31640 x : x + (this.unitWidth + this.gutter) * 1,
31654 x : x + (this.unitWidth + this.gutter) * 2,
31659 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31660 y : y + (this.unitHeight + this.gutter) * 1
31664 x : x + (this.unitWidth + this.gutter) * 2,
31665 y : y + (this.unitWidth + this.gutter) * 2
31672 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31676 if(box[0].size == 'md-left'){
31678 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31685 if(box[0].size == 'md-right'){
31687 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31688 y : minY + (this.unitWidth + this.gutter) * 1
31694 var rand = Math.floor(Math.random() * (4 - box[0].y));
31697 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31698 y : minY + (this.unitWidth + this.gutter) * rand
31705 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31709 if(box[0].size == 'xs'){
31712 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31717 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31718 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31726 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31731 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31732 y : minY + (this.unitWidth + this.gutter) * 2
31739 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31743 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31746 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31751 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31752 y : minY + (this.unitWidth + this.gutter) * 1
31756 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31757 y : minY + (this.unitWidth + this.gutter) * 2
31764 if(box[0].size == 'xs' && box[1].size == 'xs'){
31767 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31772 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31777 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31778 y : minY + (this.unitWidth + this.gutter) * 1
31786 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31791 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31792 y : minY + (this.unitWidth + this.gutter) * 2
31796 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31797 y : minY + (this.unitWidth + this.gutter) * 2
31804 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31808 if(box[0].size == 'xs'){
31811 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31816 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31821 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),
31826 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31827 y : minY + (this.unitWidth + this.gutter) * 1
31835 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31840 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31841 y : minY + (this.unitWidth + this.gutter) * 2
31845 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31846 y : minY + (this.unitWidth + this.gutter) * 2
31850 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),
31851 y : minY + (this.unitWidth + this.gutter) * 2
31859 * remove a Masonry Brick
31860 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31862 removeBrick : function(brick_id)
31868 for (var i = 0; i<this.bricks.length; i++) {
31869 if (this.bricks[i].id == brick_id) {
31870 this.bricks.splice(i,1);
31871 this.el.dom.removeChild(Roo.get(brick_id).dom);
31878 * adds a Masonry Brick
31879 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31881 addBrick : function(cfg)
31883 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31884 //this.register(cn);
31885 cn.parentId = this.id;
31886 cn.onRender(this.el, null);
31891 * register a Masonry Brick
31892 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31895 register : function(brick)
31897 this.bricks.push(brick);
31898 brick.masonryId = this.id;
31902 * clear all the Masonry Brick
31904 clearAll : function()
31907 //this.getChildContainer().dom.innerHTML = "";
31908 this.el.dom.innerHTML = '';
31911 getSelected : function()
31913 if (!this.selectedBrick) {
31917 return this.selectedBrick;
31921 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31925 * register a Masonry Layout
31926 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31929 register : function(layout)
31931 this.groups[layout.id] = layout;
31934 * fetch a Masonry Layout based on the masonry layout ID
31935 * @param {string} the masonry layout to add
31936 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31939 get: function(layout_id) {
31940 if (typeof(this.groups[layout_id]) == 'undefined') {
31943 return this.groups[layout_id] ;
31955 * http://masonry.desandro.com
31957 * The idea is to render all the bricks based on vertical width...
31959 * The original code extends 'outlayer' - we might need to use that....
31965 * @class Roo.bootstrap.LayoutMasonryAuto
31966 * @extends Roo.bootstrap.Component
31967 * Bootstrap Layout Masonry class
31970 * Create a new Element
31971 * @param {Object} config The config object
31974 Roo.bootstrap.LayoutMasonryAuto = function(config){
31975 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31978 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31981 * @cfg {Boolean} isFitWidth - resize the width..
31983 isFitWidth : false, // options..
31985 * @cfg {Boolean} isOriginLeft = left align?
31987 isOriginLeft : true,
31989 * @cfg {Boolean} isOriginTop = top align?
31991 isOriginTop : false,
31993 * @cfg {Boolean} isLayoutInstant = no animation?
31995 isLayoutInstant : false, // needed?
31997 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31999 isResizingContainer : true,
32001 * @cfg {Number} columnWidth width of the columns
32007 * @cfg {Number} maxCols maximum number of columns
32012 * @cfg {Number} padHeight padding below box..
32018 * @cfg {Boolean} isAutoInitial defalut true
32021 isAutoInitial : true,
32027 initialColumnWidth : 0,
32028 currentSize : null,
32030 colYs : null, // array.
32037 bricks: null, //CompositeElement
32038 cols : 0, // array?
32039 // element : null, // wrapped now this.el
32040 _isLayoutInited : null,
32043 getAutoCreate : function(){
32047 cls: 'blog-masonary-wrapper ' + this.cls,
32049 cls : 'mas-boxes masonary'
32056 getChildContainer: function( )
32058 if (this.boxesEl) {
32059 return this.boxesEl;
32062 this.boxesEl = this.el.select('.mas-boxes').first();
32064 return this.boxesEl;
32068 initEvents : function()
32072 if(this.isAutoInitial){
32073 Roo.log('hook children rendered');
32074 this.on('childrenrendered', function() {
32075 Roo.log('children rendered');
32082 initial : function()
32084 this.reloadItems();
32086 this.currentSize = this.el.getBox(true);
32088 /// was window resize... - let's see if this works..
32089 Roo.EventManager.onWindowResize(this.resize, this);
32091 if(!this.isAutoInitial){
32096 this.layout.defer(500,this);
32099 reloadItems: function()
32101 this.bricks = this.el.select('.masonry-brick', true);
32103 this.bricks.each(function(b) {
32104 //Roo.log(b.getSize());
32105 if (!b.attr('originalwidth')) {
32106 b.attr('originalwidth', b.getSize().width);
32111 Roo.log(this.bricks.elements.length);
32114 resize : function()
32117 var cs = this.el.getBox(true);
32119 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32120 Roo.log("no change in with or X");
32123 this.currentSize = cs;
32127 layout : function()
32130 this._resetLayout();
32131 //this._manageStamps();
32133 // don't animate first layout
32134 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32135 this.layoutItems( isInstant );
32137 // flag for initalized
32138 this._isLayoutInited = true;
32141 layoutItems : function( isInstant )
32143 //var items = this._getItemsForLayout( this.items );
32144 // original code supports filtering layout items.. we just ignore it..
32146 this._layoutItems( this.bricks , isInstant );
32148 this._postLayout();
32150 _layoutItems : function ( items , isInstant)
32152 //this.fireEvent( 'layout', this, items );
32155 if ( !items || !items.elements.length ) {
32156 // no items, emit event with empty array
32161 items.each(function(item) {
32162 Roo.log("layout item");
32164 // get x/y object from method
32165 var position = this._getItemLayoutPosition( item );
32167 position.item = item;
32168 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32169 queue.push( position );
32172 this._processLayoutQueue( queue );
32174 /** Sets position of item in DOM
32175 * @param {Element} item
32176 * @param {Number} x - horizontal position
32177 * @param {Number} y - vertical position
32178 * @param {Boolean} isInstant - disables transitions
32180 _processLayoutQueue : function( queue )
32182 for ( var i=0, len = queue.length; i < len; i++ ) {
32183 var obj = queue[i];
32184 obj.item.position('absolute');
32185 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32191 * Any logic you want to do after each layout,
32192 * i.e. size the container
32194 _postLayout : function()
32196 this.resizeContainer();
32199 resizeContainer : function()
32201 if ( !this.isResizingContainer ) {
32204 var size = this._getContainerSize();
32206 this.el.setSize(size.width,size.height);
32207 this.boxesEl.setSize(size.width,size.height);
32213 _resetLayout : function()
32215 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32216 this.colWidth = this.el.getWidth();
32217 //this.gutter = this.el.getWidth();
32219 this.measureColumns();
32225 this.colYs.push( 0 );
32231 measureColumns : function()
32233 this.getContainerWidth();
32234 // if columnWidth is 0, default to outerWidth of first item
32235 if ( !this.columnWidth ) {
32236 var firstItem = this.bricks.first();
32237 Roo.log(firstItem);
32238 this.columnWidth = this.containerWidth;
32239 if (firstItem && firstItem.attr('originalwidth') ) {
32240 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32242 // columnWidth fall back to item of first element
32243 Roo.log("set column width?");
32244 this.initialColumnWidth = this.columnWidth ;
32246 // if first elem has no width, default to size of container
32251 if (this.initialColumnWidth) {
32252 this.columnWidth = this.initialColumnWidth;
32257 // column width is fixed at the top - however if container width get's smaller we should
32260 // this bit calcs how man columns..
32262 var columnWidth = this.columnWidth += this.gutter;
32264 // calculate columns
32265 var containerWidth = this.containerWidth + this.gutter;
32267 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32268 // fix rounding errors, typically with gutters
32269 var excess = columnWidth - containerWidth % columnWidth;
32272 // if overshoot is less than a pixel, round up, otherwise floor it
32273 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32274 cols = Math[ mathMethod ]( cols );
32275 this.cols = Math.max( cols, 1 );
32276 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32278 // padding positioning..
32279 var totalColWidth = this.cols * this.columnWidth;
32280 var padavail = this.containerWidth - totalColWidth;
32281 // so for 2 columns - we need 3 'pads'
32283 var padNeeded = (1+this.cols) * this.padWidth;
32285 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32287 this.columnWidth += padExtra
32288 //this.padWidth = Math.floor(padavail / ( this.cols));
32290 // adjust colum width so that padding is fixed??
32292 // we have 3 columns ... total = width * 3
32293 // we have X left over... that should be used by
32295 //if (this.expandC) {
32303 getContainerWidth : function()
32305 /* // container is parent if fit width
32306 var container = this.isFitWidth ? this.element.parentNode : this.element;
32307 // check that this.size and size are there
32308 // IE8 triggers resize on body size change, so they might not be
32310 var size = getSize( container ); //FIXME
32311 this.containerWidth = size && size.innerWidth; //FIXME
32314 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32318 _getItemLayoutPosition : function( item ) // what is item?
32320 // we resize the item to our columnWidth..
32322 item.setWidth(this.columnWidth);
32323 item.autoBoxAdjust = false;
32325 var sz = item.getSize();
32327 // how many columns does this brick span
32328 var remainder = this.containerWidth % this.columnWidth;
32330 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32331 // round if off by 1 pixel, otherwise use ceil
32332 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32333 colSpan = Math.min( colSpan, this.cols );
32335 // normally this should be '1' as we dont' currently allow multi width columns..
32337 var colGroup = this._getColGroup( colSpan );
32338 // get the minimum Y value from the columns
32339 var minimumY = Math.min.apply( Math, colGroup );
32340 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32342 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32344 // position the brick
32346 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32347 y: this.currentSize.y + minimumY + this.padHeight
32351 // apply setHeight to necessary columns
32352 var setHeight = minimumY + sz.height + this.padHeight;
32353 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32355 var setSpan = this.cols + 1 - colGroup.length;
32356 for ( var i = 0; i < setSpan; i++ ) {
32357 this.colYs[ shortColIndex + i ] = setHeight ;
32364 * @param {Number} colSpan - number of columns the element spans
32365 * @returns {Array} colGroup
32367 _getColGroup : function( colSpan )
32369 if ( colSpan < 2 ) {
32370 // if brick spans only one column, use all the column Ys
32375 // how many different places could this brick fit horizontally
32376 var groupCount = this.cols + 1 - colSpan;
32377 // for each group potential horizontal position
32378 for ( var i = 0; i < groupCount; i++ ) {
32379 // make an array of colY values for that one group
32380 var groupColYs = this.colYs.slice( i, i + colSpan );
32381 // and get the max value of the array
32382 colGroup[i] = Math.max.apply( Math, groupColYs );
32387 _manageStamp : function( stamp )
32389 var stampSize = stamp.getSize();
32390 var offset = stamp.getBox();
32391 // get the columns that this stamp affects
32392 var firstX = this.isOriginLeft ? offset.x : offset.right;
32393 var lastX = firstX + stampSize.width;
32394 var firstCol = Math.floor( firstX / this.columnWidth );
32395 firstCol = Math.max( 0, firstCol );
32397 var lastCol = Math.floor( lastX / this.columnWidth );
32398 // lastCol should not go over if multiple of columnWidth #425
32399 lastCol -= lastX % this.columnWidth ? 0 : 1;
32400 lastCol = Math.min( this.cols - 1, lastCol );
32402 // set colYs to bottom of the stamp
32403 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32406 for ( var i = firstCol; i <= lastCol; i++ ) {
32407 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32412 _getContainerSize : function()
32414 this.maxY = Math.max.apply( Math, this.colYs );
32419 if ( this.isFitWidth ) {
32420 size.width = this._getContainerFitWidth();
32426 _getContainerFitWidth : function()
32428 var unusedCols = 0;
32429 // count unused columns
32432 if ( this.colYs[i] !== 0 ) {
32437 // fit container to columns that have been used
32438 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32441 needsResizeLayout : function()
32443 var previousWidth = this.containerWidth;
32444 this.getContainerWidth();
32445 return previousWidth !== this.containerWidth;
32460 * @class Roo.bootstrap.MasonryBrick
32461 * @extends Roo.bootstrap.Component
32462 * Bootstrap MasonryBrick class
32465 * Create a new MasonryBrick
32466 * @param {Object} config The config object
32469 Roo.bootstrap.MasonryBrick = function(config){
32471 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32473 Roo.bootstrap.MasonryBrick.register(this);
32479 * When a MasonryBrick is clcik
32480 * @param {Roo.bootstrap.MasonryBrick} this
32481 * @param {Roo.EventObject} e
32487 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32490 * @cfg {String} title
32494 * @cfg {String} html
32498 * @cfg {String} bgimage
32502 * @cfg {String} videourl
32506 * @cfg {String} cls
32510 * @cfg {String} href
32514 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32519 * @cfg {String} placetitle (center|bottom)
32524 * @cfg {Boolean} isFitContainer defalut true
32526 isFitContainer : true,
32529 * @cfg {Boolean} preventDefault defalut false
32531 preventDefault : false,
32534 * @cfg {Boolean} inverse defalut false
32536 maskInverse : false,
32538 getAutoCreate : function()
32540 if(!this.isFitContainer){
32541 return this.getSplitAutoCreate();
32544 var cls = 'masonry-brick masonry-brick-full';
32546 if(this.href.length){
32547 cls += ' masonry-brick-link';
32550 if(this.bgimage.length){
32551 cls += ' masonry-brick-image';
32554 if(this.maskInverse){
32555 cls += ' mask-inverse';
32558 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32559 cls += ' enable-mask';
32563 cls += ' masonry-' + this.size + '-brick';
32566 if(this.placetitle.length){
32568 switch (this.placetitle) {
32570 cls += ' masonry-center-title';
32573 cls += ' masonry-bottom-title';
32580 if(!this.html.length && !this.bgimage.length){
32581 cls += ' masonry-center-title';
32584 if(!this.html.length && this.bgimage.length){
32585 cls += ' masonry-bottom-title';
32590 cls += ' ' + this.cls;
32594 tag: (this.href.length) ? 'a' : 'div',
32599 cls: 'masonry-brick-mask'
32603 cls: 'masonry-brick-paragraph',
32609 if(this.href.length){
32610 cfg.href = this.href;
32613 var cn = cfg.cn[1].cn;
32615 if(this.title.length){
32618 cls: 'masonry-brick-title',
32623 if(this.html.length){
32626 cls: 'masonry-brick-text',
32631 if (!this.title.length && !this.html.length) {
32632 cfg.cn[1].cls += ' hide';
32635 if(this.bgimage.length){
32638 cls: 'masonry-brick-image-view',
32643 if(this.videourl.length){
32644 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32645 // youtube support only?
32648 cls: 'masonry-brick-image-view',
32651 allowfullscreen : true
32659 getSplitAutoCreate : function()
32661 var cls = 'masonry-brick masonry-brick-split';
32663 if(this.href.length){
32664 cls += ' masonry-brick-link';
32667 if(this.bgimage.length){
32668 cls += ' masonry-brick-image';
32672 cls += ' masonry-' + this.size + '-brick';
32675 switch (this.placetitle) {
32677 cls += ' masonry-center-title';
32680 cls += ' masonry-bottom-title';
32683 if(!this.bgimage.length){
32684 cls += ' masonry-center-title';
32687 if(this.bgimage.length){
32688 cls += ' masonry-bottom-title';
32694 cls += ' ' + this.cls;
32698 tag: (this.href.length) ? 'a' : 'div',
32703 cls: 'masonry-brick-split-head',
32707 cls: 'masonry-brick-paragraph',
32714 cls: 'masonry-brick-split-body',
32720 if(this.href.length){
32721 cfg.href = this.href;
32724 if(this.title.length){
32725 cfg.cn[0].cn[0].cn.push({
32727 cls: 'masonry-brick-title',
32732 if(this.html.length){
32733 cfg.cn[1].cn.push({
32735 cls: 'masonry-brick-text',
32740 if(this.bgimage.length){
32741 cfg.cn[0].cn.push({
32743 cls: 'masonry-brick-image-view',
32748 if(this.videourl.length){
32749 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32750 // youtube support only?
32751 cfg.cn[0].cn.cn.push({
32753 cls: 'masonry-brick-image-view',
32756 allowfullscreen : true
32763 initEvents: function()
32765 switch (this.size) {
32798 this.el.on('touchstart', this.onTouchStart, this);
32799 this.el.on('touchmove', this.onTouchMove, this);
32800 this.el.on('touchend', this.onTouchEnd, this);
32801 this.el.on('contextmenu', this.onContextMenu, this);
32803 this.el.on('mouseenter' ,this.enter, this);
32804 this.el.on('mouseleave', this.leave, this);
32805 this.el.on('click', this.onClick, this);
32808 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32809 this.parent().bricks.push(this);
32814 onClick: function(e, el)
32816 var time = this.endTimer - this.startTimer;
32817 // Roo.log(e.preventDefault());
32820 e.preventDefault();
32825 if(!this.preventDefault){
32829 e.preventDefault();
32831 if (this.activeClass != '') {
32832 this.selectBrick();
32835 this.fireEvent('click', this, e);
32838 enter: function(e, el)
32840 e.preventDefault();
32842 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32846 if(this.bgimage.length && this.html.length){
32847 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32851 leave: function(e, el)
32853 e.preventDefault();
32855 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32859 if(this.bgimage.length && this.html.length){
32860 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32864 onTouchStart: function(e, el)
32866 // e.preventDefault();
32868 this.touchmoved = false;
32870 if(!this.isFitContainer){
32874 if(!this.bgimage.length || !this.html.length){
32878 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32880 this.timer = new Date().getTime();
32884 onTouchMove: function(e, el)
32886 this.touchmoved = true;
32889 onContextMenu : function(e,el)
32891 e.preventDefault();
32892 e.stopPropagation();
32896 onTouchEnd: function(e, el)
32898 // e.preventDefault();
32900 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32907 if(!this.bgimage.length || !this.html.length){
32909 if(this.href.length){
32910 window.location.href = this.href;
32916 if(!this.isFitContainer){
32920 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32922 window.location.href = this.href;
32925 //selection on single brick only
32926 selectBrick : function() {
32928 if (!this.parentId) {
32932 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32933 var index = m.selectedBrick.indexOf(this.id);
32936 m.selectedBrick.splice(index,1);
32937 this.el.removeClass(this.activeClass);
32941 for(var i = 0; i < m.selectedBrick.length; i++) {
32942 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32943 b.el.removeClass(b.activeClass);
32946 m.selectedBrick = [];
32948 m.selectedBrick.push(this.id);
32949 this.el.addClass(this.activeClass);
32953 isSelected : function(){
32954 return this.el.hasClass(this.activeClass);
32959 Roo.apply(Roo.bootstrap.MasonryBrick, {
32962 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32964 * register a Masonry Brick
32965 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32968 register : function(brick)
32970 //this.groups[brick.id] = brick;
32971 this.groups.add(brick.id, brick);
32974 * fetch a masonry brick based on the masonry brick ID
32975 * @param {string} the masonry brick to add
32976 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32979 get: function(brick_id)
32981 // if (typeof(this.groups[brick_id]) == 'undefined') {
32984 // return this.groups[brick_id] ;
32986 if(this.groups.key(brick_id)) {
32987 return this.groups.key(brick_id);
33005 * @class Roo.bootstrap.Brick
33006 * @extends Roo.bootstrap.Component
33007 * Bootstrap Brick class
33010 * Create a new Brick
33011 * @param {Object} config The config object
33014 Roo.bootstrap.Brick = function(config){
33015 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33021 * When a Brick is click
33022 * @param {Roo.bootstrap.Brick} this
33023 * @param {Roo.EventObject} e
33029 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33032 * @cfg {String} title
33036 * @cfg {String} html
33040 * @cfg {String} bgimage
33044 * @cfg {String} cls
33048 * @cfg {String} href
33052 * @cfg {String} video
33056 * @cfg {Boolean} square
33060 getAutoCreate : function()
33062 var cls = 'roo-brick';
33064 if(this.href.length){
33065 cls += ' roo-brick-link';
33068 if(this.bgimage.length){
33069 cls += ' roo-brick-image';
33072 if(!this.html.length && !this.bgimage.length){
33073 cls += ' roo-brick-center-title';
33076 if(!this.html.length && this.bgimage.length){
33077 cls += ' roo-brick-bottom-title';
33081 cls += ' ' + this.cls;
33085 tag: (this.href.length) ? 'a' : 'div',
33090 cls: 'roo-brick-paragraph',
33096 if(this.href.length){
33097 cfg.href = this.href;
33100 var cn = cfg.cn[0].cn;
33102 if(this.title.length){
33105 cls: 'roo-brick-title',
33110 if(this.html.length){
33113 cls: 'roo-brick-text',
33120 if(this.bgimage.length){
33123 cls: 'roo-brick-image-view',
33131 initEvents: function()
33133 if(this.title.length || this.html.length){
33134 this.el.on('mouseenter' ,this.enter, this);
33135 this.el.on('mouseleave', this.leave, this);
33138 Roo.EventManager.onWindowResize(this.resize, this);
33140 if(this.bgimage.length){
33141 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33142 this.imageEl.on('load', this.onImageLoad, this);
33149 onImageLoad : function()
33154 resize : function()
33156 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33158 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33160 if(this.bgimage.length){
33161 var image = this.el.select('.roo-brick-image-view', true).first();
33163 image.setWidth(paragraph.getWidth());
33166 image.setHeight(paragraph.getWidth());
33169 this.el.setHeight(image.getHeight());
33170 paragraph.setHeight(image.getHeight());
33176 enter: function(e, el)
33178 e.preventDefault();
33180 if(this.bgimage.length){
33181 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33182 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33186 leave: function(e, el)
33188 e.preventDefault();
33190 if(this.bgimage.length){
33191 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33192 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33207 * @class Roo.bootstrap.NumberField
33208 * @extends Roo.bootstrap.Input
33209 * Bootstrap NumberField class
33215 * Create a new NumberField
33216 * @param {Object} config The config object
33219 Roo.bootstrap.NumberField = function(config){
33220 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33223 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33226 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33228 allowDecimals : true,
33230 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33232 decimalSeparator : ".",
33234 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33236 decimalPrecision : 2,
33238 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33240 allowNegative : true,
33243 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33247 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33249 minValue : Number.NEGATIVE_INFINITY,
33251 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33253 maxValue : Number.MAX_VALUE,
33255 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33257 minText : "The minimum value for this field is {0}",
33259 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33261 maxText : "The maximum value for this field is {0}",
33263 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33264 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33266 nanText : "{0} is not a valid number",
33268 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33270 thousandsDelimiter : false,
33272 * @cfg {String} valueAlign alignment of value
33274 valueAlign : "left",
33276 getAutoCreate : function()
33278 var hiddenInput = {
33282 cls: 'hidden-number-input'
33286 hiddenInput.name = this.name;
33291 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33293 this.name = hiddenInput.name;
33295 if(cfg.cn.length > 0) {
33296 cfg.cn.push(hiddenInput);
33303 initEvents : function()
33305 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33307 var allowed = "0123456789";
33309 if(this.allowDecimals){
33310 allowed += this.decimalSeparator;
33313 if(this.allowNegative){
33317 if(this.thousandsDelimiter) {
33321 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33323 var keyPress = function(e){
33325 var k = e.getKey();
33327 var c = e.getCharCode();
33330 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33331 allowed.indexOf(String.fromCharCode(c)) === -1
33337 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33341 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33346 this.el.on("keypress", keyPress, this);
33349 validateValue : function(value)
33352 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33356 var num = this.parseValue(value);
33359 this.markInvalid(String.format(this.nanText, value));
33363 if(num < this.minValue){
33364 this.markInvalid(String.format(this.minText, this.minValue));
33368 if(num > this.maxValue){
33369 this.markInvalid(String.format(this.maxText, this.maxValue));
33376 getValue : function()
33378 var v = this.hiddenEl().getValue();
33380 return this.fixPrecision(this.parseValue(v));
33383 parseValue : function(value)
33385 if(this.thousandsDelimiter) {
33387 r = new RegExp(",", "g");
33388 value = value.replace(r, "");
33391 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33392 return isNaN(value) ? '' : value;
33395 fixPrecision : function(value)
33397 if(this.thousandsDelimiter) {
33399 r = new RegExp(",", "g");
33400 value = value.replace(r, "");
33403 var nan = isNaN(value);
33405 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33406 return nan ? '' : value;
33408 return parseFloat(value).toFixed(this.decimalPrecision);
33411 setValue : function(v)
33413 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33419 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33421 this.inputEl().dom.value = (v == '') ? '' :
33422 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33424 if(!this.allowZero && v === '0') {
33425 this.hiddenEl().dom.value = '';
33426 this.inputEl().dom.value = '';
33433 decimalPrecisionFcn : function(v)
33435 return Math.floor(v);
33438 beforeBlur : function()
33440 var v = this.parseValue(this.getRawValue());
33442 if(v || v === 0 || v === ''){
33447 hiddenEl : function()
33449 return this.el.select('input.hidden-number-input',true).first();
33461 * @class Roo.bootstrap.DocumentSlider
33462 * @extends Roo.bootstrap.Component
33463 * Bootstrap DocumentSlider class
33466 * Create a new DocumentViewer
33467 * @param {Object} config The config object
33470 Roo.bootstrap.DocumentSlider = function(config){
33471 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33478 * Fire after initEvent
33479 * @param {Roo.bootstrap.DocumentSlider} this
33484 * Fire after update
33485 * @param {Roo.bootstrap.DocumentSlider} this
33491 * @param {Roo.bootstrap.DocumentSlider} this
33497 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33503 getAutoCreate : function()
33507 cls : 'roo-document-slider',
33511 cls : 'roo-document-slider-header',
33515 cls : 'roo-document-slider-header-title'
33521 cls : 'roo-document-slider-body',
33525 cls : 'roo-document-slider-prev',
33529 cls : 'fa fa-chevron-left'
33535 cls : 'roo-document-slider-thumb',
33539 cls : 'roo-document-slider-image'
33545 cls : 'roo-document-slider-next',
33549 cls : 'fa fa-chevron-right'
33561 initEvents : function()
33563 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33564 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33566 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33567 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33569 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33570 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33572 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33573 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33575 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33576 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33578 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33579 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33581 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33582 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33584 this.thumbEl.on('click', this.onClick, this);
33586 this.prevIndicator.on('click', this.prev, this);
33588 this.nextIndicator.on('click', this.next, this);
33592 initial : function()
33594 if(this.files.length){
33595 this.indicator = 1;
33599 this.fireEvent('initial', this);
33602 update : function()
33604 this.imageEl.attr('src', this.files[this.indicator - 1]);
33606 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33608 this.prevIndicator.show();
33610 if(this.indicator == 1){
33611 this.prevIndicator.hide();
33614 this.nextIndicator.show();
33616 if(this.indicator == this.files.length){
33617 this.nextIndicator.hide();
33620 this.thumbEl.scrollTo('top');
33622 this.fireEvent('update', this);
33625 onClick : function(e)
33627 e.preventDefault();
33629 this.fireEvent('click', this);
33634 e.preventDefault();
33636 this.indicator = Math.max(1, this.indicator - 1);
33643 e.preventDefault();
33645 this.indicator = Math.min(this.files.length, this.indicator + 1);
33659 * @class Roo.bootstrap.RadioSet
33660 * @extends Roo.bootstrap.Input
33661 * Bootstrap RadioSet class
33662 * @cfg {String} indicatorpos (left|right) default left
33663 * @cfg {Boolean} inline (true|false) inline the element (default true)
33664 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33666 * Create a new RadioSet
33667 * @param {Object} config The config object
33670 Roo.bootstrap.RadioSet = function(config){
33672 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33676 Roo.bootstrap.RadioSet.register(this);
33681 * Fires when the element is checked or unchecked.
33682 * @param {Roo.bootstrap.RadioSet} this This radio
33683 * @param {Roo.bootstrap.Radio} item The checked item
33688 * Fires when the element is click.
33689 * @param {Roo.bootstrap.RadioSet} this This radio set
33690 * @param {Roo.bootstrap.Radio} item The checked item
33691 * @param {Roo.EventObject} e The event object
33698 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33706 indicatorpos : 'left',
33708 getAutoCreate : function()
33712 cls : 'roo-radio-set-label',
33716 html : this.fieldLabel
33721 if(this.indicatorpos == 'left'){
33724 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33725 tooltip : 'This field is required'
33730 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33731 tooltip : 'This field is required'
33737 cls : 'roo-radio-set-items'
33740 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33742 if (align === 'left' && this.fieldLabel.length) {
33745 cls : "roo-radio-set-right",
33751 if(this.labelWidth > 12){
33752 label.style = "width: " + this.labelWidth + 'px';
33755 if(this.labelWidth < 13 && this.labelmd == 0){
33756 this.labelmd = this.labelWidth;
33759 if(this.labellg > 0){
33760 label.cls += ' col-lg-' + this.labellg;
33761 items.cls += ' col-lg-' + (12 - this.labellg);
33764 if(this.labelmd > 0){
33765 label.cls += ' col-md-' + this.labelmd;
33766 items.cls += ' col-md-' + (12 - this.labelmd);
33769 if(this.labelsm > 0){
33770 label.cls += ' col-sm-' + this.labelsm;
33771 items.cls += ' col-sm-' + (12 - this.labelsm);
33774 if(this.labelxs > 0){
33775 label.cls += ' col-xs-' + this.labelxs;
33776 items.cls += ' col-xs-' + (12 - this.labelxs);
33782 cls : 'roo-radio-set',
33786 cls : 'roo-radio-set-input',
33789 value : this.value ? this.value : ''
33796 if(this.weight.length){
33797 cfg.cls += ' roo-radio-' + this.weight;
33801 cfg.cls += ' roo-radio-set-inline';
33805 ['xs','sm','md','lg'].map(function(size){
33806 if (settings[size]) {
33807 cfg.cls += ' col-' + size + '-' + settings[size];
33815 initEvents : function()
33817 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33818 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33820 if(!this.fieldLabel.length){
33821 this.labelEl.hide();
33824 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33825 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33827 this.indicator = this.indicatorEl();
33829 if(this.indicator){
33830 this.indicator.addClass('invisible');
33833 this.originalValue = this.getValue();
33837 inputEl: function ()
33839 return this.el.select('.roo-radio-set-input', true).first();
33842 getChildContainer : function()
33844 return this.itemsEl;
33847 register : function(item)
33849 this.radioes.push(item);
33853 validate : function()
33855 if(this.getVisibilityEl().hasClass('hidden')){
33861 Roo.each(this.radioes, function(i){
33870 if(this.allowBlank) {
33874 if(this.disabled || valid){
33879 this.markInvalid();
33884 markValid : function()
33886 if(this.labelEl.isVisible(true)){
33887 this.indicatorEl().removeClass('visible');
33888 this.indicatorEl().addClass('invisible');
33891 this.el.removeClass([this.invalidClass, this.validClass]);
33892 this.el.addClass(this.validClass);
33894 this.fireEvent('valid', this);
33897 markInvalid : function(msg)
33899 if(this.allowBlank || this.disabled){
33903 if(this.labelEl.isVisible(true)){
33904 this.indicatorEl().removeClass('invisible');
33905 this.indicatorEl().addClass('visible');
33908 this.el.removeClass([this.invalidClass, this.validClass]);
33909 this.el.addClass(this.invalidClass);
33911 this.fireEvent('invalid', this, msg);
33915 setValue : function(v, suppressEvent)
33917 if(this.value === v){
33924 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33927 Roo.each(this.radioes, function(i){
33929 i.el.removeClass('checked');
33932 Roo.each(this.radioes, function(i){
33934 if(i.value === v || i.value.toString() === v.toString()){
33936 i.el.addClass('checked');
33938 if(suppressEvent !== true){
33939 this.fireEvent('check', this, i);
33950 clearInvalid : function(){
33952 if(!this.el || this.preventMark){
33956 this.el.removeClass([this.invalidClass]);
33958 this.fireEvent('valid', this);
33963 Roo.apply(Roo.bootstrap.RadioSet, {
33967 register : function(set)
33969 this.groups[set.name] = set;
33972 get: function(name)
33974 if (typeof(this.groups[name]) == 'undefined') {
33978 return this.groups[name] ;
33984 * Ext JS Library 1.1.1
33985 * Copyright(c) 2006-2007, Ext JS, LLC.
33987 * Originally Released Under LGPL - original licence link has changed is not relivant.
33990 * <script type="text/javascript">
33995 * @class Roo.bootstrap.SplitBar
33996 * @extends Roo.util.Observable
33997 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34001 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34002 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34003 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34004 split.minSize = 100;
34005 split.maxSize = 600;
34006 split.animate = true;
34007 split.on('moved', splitterMoved);
34010 * Create a new SplitBar
34011 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34012 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34013 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34014 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34015 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34016 position of the SplitBar).
34018 Roo.bootstrap.SplitBar = function(cfg){
34023 // dragElement : elm
34024 // resizingElement: el,
34026 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34027 // placement : Roo.bootstrap.SplitBar.LEFT ,
34028 // existingProxy ???
34031 this.el = Roo.get(cfg.dragElement, true);
34032 this.el.dom.unselectable = "on";
34034 this.resizingEl = Roo.get(cfg.resizingElement, true);
34038 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34039 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34042 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34045 * The minimum size of the resizing element. (Defaults to 0)
34051 * The maximum size of the resizing element. (Defaults to 2000)
34054 this.maxSize = 2000;
34057 * Whether to animate the transition to the new size
34060 this.animate = false;
34063 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34066 this.useShim = false;
34071 if(!cfg.existingProxy){
34073 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34075 this.proxy = Roo.get(cfg.existingProxy).dom;
34078 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34081 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34084 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34087 this.dragSpecs = {};
34090 * @private The adapter to use to positon and resize elements
34092 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34093 this.adapter.init(this);
34095 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34097 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34098 this.el.addClass("roo-splitbar-h");
34101 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34102 this.el.addClass("roo-splitbar-v");
34108 * Fires when the splitter is moved (alias for {@link #event-moved})
34109 * @param {Roo.bootstrap.SplitBar} this
34110 * @param {Number} newSize the new width or height
34115 * Fires when the splitter is moved
34116 * @param {Roo.bootstrap.SplitBar} this
34117 * @param {Number} newSize the new width or height
34121 * @event beforeresize
34122 * Fires before the splitter is dragged
34123 * @param {Roo.bootstrap.SplitBar} this
34125 "beforeresize" : true,
34127 "beforeapply" : true
34130 Roo.util.Observable.call(this);
34133 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34134 onStartProxyDrag : function(x, y){
34135 this.fireEvent("beforeresize", this);
34137 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34139 o.enableDisplayMode("block");
34140 // all splitbars share the same overlay
34141 Roo.bootstrap.SplitBar.prototype.overlay = o;
34143 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34144 this.overlay.show();
34145 Roo.get(this.proxy).setDisplayed("block");
34146 var size = this.adapter.getElementSize(this);
34147 this.activeMinSize = this.getMinimumSize();;
34148 this.activeMaxSize = this.getMaximumSize();;
34149 var c1 = size - this.activeMinSize;
34150 var c2 = Math.max(this.activeMaxSize - size, 0);
34151 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34152 this.dd.resetConstraints();
34153 this.dd.setXConstraint(
34154 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34155 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34157 this.dd.setYConstraint(0, 0);
34159 this.dd.resetConstraints();
34160 this.dd.setXConstraint(0, 0);
34161 this.dd.setYConstraint(
34162 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34163 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34166 this.dragSpecs.startSize = size;
34167 this.dragSpecs.startPoint = [x, y];
34168 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34172 * @private Called after the drag operation by the DDProxy
34174 onEndProxyDrag : function(e){
34175 Roo.get(this.proxy).setDisplayed(false);
34176 var endPoint = Roo.lib.Event.getXY(e);
34178 this.overlay.hide();
34181 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34182 newSize = this.dragSpecs.startSize +
34183 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34184 endPoint[0] - this.dragSpecs.startPoint[0] :
34185 this.dragSpecs.startPoint[0] - endPoint[0]
34188 newSize = this.dragSpecs.startSize +
34189 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34190 endPoint[1] - this.dragSpecs.startPoint[1] :
34191 this.dragSpecs.startPoint[1] - endPoint[1]
34194 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34195 if(newSize != this.dragSpecs.startSize){
34196 if(this.fireEvent('beforeapply', this, newSize) !== false){
34197 this.adapter.setElementSize(this, newSize);
34198 this.fireEvent("moved", this, newSize);
34199 this.fireEvent("resize", this, newSize);
34205 * Get the adapter this SplitBar uses
34206 * @return The adapter object
34208 getAdapter : function(){
34209 return this.adapter;
34213 * Set the adapter this SplitBar uses
34214 * @param {Object} adapter A SplitBar adapter object
34216 setAdapter : function(adapter){
34217 this.adapter = adapter;
34218 this.adapter.init(this);
34222 * Gets the minimum size for the resizing element
34223 * @return {Number} The minimum size
34225 getMinimumSize : function(){
34226 return this.minSize;
34230 * Sets the minimum size for the resizing element
34231 * @param {Number} minSize The minimum size
34233 setMinimumSize : function(minSize){
34234 this.minSize = minSize;
34238 * Gets the maximum size for the resizing element
34239 * @return {Number} The maximum size
34241 getMaximumSize : function(){
34242 return this.maxSize;
34246 * Sets the maximum size for the resizing element
34247 * @param {Number} maxSize The maximum size
34249 setMaximumSize : function(maxSize){
34250 this.maxSize = maxSize;
34254 * Sets the initialize size for the resizing element
34255 * @param {Number} size The initial size
34257 setCurrentSize : function(size){
34258 var oldAnimate = this.animate;
34259 this.animate = false;
34260 this.adapter.setElementSize(this, size);
34261 this.animate = oldAnimate;
34265 * Destroy this splitbar.
34266 * @param {Boolean} removeEl True to remove the element
34268 destroy : function(removeEl){
34270 this.shim.remove();
34273 this.proxy.parentNode.removeChild(this.proxy);
34281 * @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.
34283 Roo.bootstrap.SplitBar.createProxy = function(dir){
34284 var proxy = new Roo.Element(document.createElement("div"));
34285 proxy.unselectable();
34286 var cls = 'roo-splitbar-proxy';
34287 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34288 document.body.appendChild(proxy.dom);
34293 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34294 * Default Adapter. It assumes the splitter and resizing element are not positioned
34295 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34297 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34300 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34301 // do nothing for now
34302 init : function(s){
34306 * Called before drag operations to get the current size of the resizing element.
34307 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34309 getElementSize : function(s){
34310 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34311 return s.resizingEl.getWidth();
34313 return s.resizingEl.getHeight();
34318 * Called after drag operations to set the size of the resizing element.
34319 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34320 * @param {Number} newSize The new size to set
34321 * @param {Function} onComplete A function to be invoked when resizing is complete
34323 setElementSize : function(s, newSize, onComplete){
34324 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34326 s.resizingEl.setWidth(newSize);
34328 onComplete(s, newSize);
34331 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34336 s.resizingEl.setHeight(newSize);
34338 onComplete(s, newSize);
34341 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34348 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34349 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34350 * Adapter that moves the splitter element to align with the resized sizing element.
34351 * Used with an absolute positioned SplitBar.
34352 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34353 * document.body, make sure you assign an id to the body element.
34355 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34356 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34357 this.container = Roo.get(container);
34360 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34361 init : function(s){
34362 this.basic.init(s);
34365 getElementSize : function(s){
34366 return this.basic.getElementSize(s);
34369 setElementSize : function(s, newSize, onComplete){
34370 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34373 moveSplitter : function(s){
34374 var yes = Roo.bootstrap.SplitBar;
34375 switch(s.placement){
34377 s.el.setX(s.resizingEl.getRight());
34380 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34383 s.el.setY(s.resizingEl.getBottom());
34386 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34393 * Orientation constant - Create a vertical SplitBar
34397 Roo.bootstrap.SplitBar.VERTICAL = 1;
34400 * Orientation constant - Create a horizontal SplitBar
34404 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34407 * Placement constant - The resizing element is to the left of the splitter element
34411 Roo.bootstrap.SplitBar.LEFT = 1;
34414 * Placement constant - The resizing element is to the right of the splitter element
34418 Roo.bootstrap.SplitBar.RIGHT = 2;
34421 * Placement constant - The resizing element is positioned above the splitter element
34425 Roo.bootstrap.SplitBar.TOP = 3;
34428 * Placement constant - The resizing element is positioned under splitter element
34432 Roo.bootstrap.SplitBar.BOTTOM = 4;
34433 Roo.namespace("Roo.bootstrap.layout");/*
34435 * Ext JS Library 1.1.1
34436 * Copyright(c) 2006-2007, Ext JS, LLC.
34438 * Originally Released Under LGPL - original licence link has changed is not relivant.
34441 * <script type="text/javascript">
34445 * @class Roo.bootstrap.layout.Manager
34446 * @extends Roo.bootstrap.Component
34447 * Base class for layout managers.
34449 Roo.bootstrap.layout.Manager = function(config)
34451 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34457 /** false to disable window resize monitoring @type Boolean */
34458 this.monitorWindowResize = true;
34463 * Fires when a layout is performed.
34464 * @param {Roo.LayoutManager} this
34468 * @event regionresized
34469 * Fires when the user resizes a region.
34470 * @param {Roo.LayoutRegion} region The resized region
34471 * @param {Number} newSize The new size (width for east/west, height for north/south)
34473 "regionresized" : true,
34475 * @event regioncollapsed
34476 * Fires when a region is collapsed.
34477 * @param {Roo.LayoutRegion} region The collapsed region
34479 "regioncollapsed" : true,
34481 * @event regionexpanded
34482 * Fires when a region is expanded.
34483 * @param {Roo.LayoutRegion} region The expanded region
34485 "regionexpanded" : true
34487 this.updating = false;
34490 this.el = Roo.get(config.el);
34496 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34501 monitorWindowResize : true,
34507 onRender : function(ct, position)
34510 this.el = Roo.get(ct);
34513 //this.fireEvent('render',this);
34517 initEvents: function()
34521 // ie scrollbar fix
34522 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34523 document.body.scroll = "no";
34524 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34525 this.el.position('relative');
34527 this.id = this.el.id;
34528 this.el.addClass("roo-layout-container");
34529 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34530 if(this.el.dom != document.body ) {
34531 this.el.on('resize', this.layout,this);
34532 this.el.on('show', this.layout,this);
34538 * Returns true if this layout is currently being updated
34539 * @return {Boolean}
34541 isUpdating : function(){
34542 return this.updating;
34546 * Suspend the LayoutManager from doing auto-layouts while
34547 * making multiple add or remove calls
34549 beginUpdate : function(){
34550 this.updating = true;
34554 * Restore auto-layouts and optionally disable the manager from performing a layout
34555 * @param {Boolean} noLayout true to disable a layout update
34557 endUpdate : function(noLayout){
34558 this.updating = false;
34564 layout: function(){
34568 onRegionResized : function(region, newSize){
34569 this.fireEvent("regionresized", region, newSize);
34573 onRegionCollapsed : function(region){
34574 this.fireEvent("regioncollapsed", region);
34577 onRegionExpanded : function(region){
34578 this.fireEvent("regionexpanded", region);
34582 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34583 * performs box-model adjustments.
34584 * @return {Object} The size as an object {width: (the width), height: (the height)}
34586 getViewSize : function()
34589 if(this.el.dom != document.body){
34590 size = this.el.getSize();
34592 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34594 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34595 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34600 * Returns the Element this layout is bound to.
34601 * @return {Roo.Element}
34603 getEl : function(){
34608 * Returns the specified region.
34609 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34610 * @return {Roo.LayoutRegion}
34612 getRegion : function(target){
34613 return this.regions[target.toLowerCase()];
34616 onWindowResize : function(){
34617 if(this.monitorWindowResize){
34624 * Ext JS Library 1.1.1
34625 * Copyright(c) 2006-2007, Ext JS, LLC.
34627 * Originally Released Under LGPL - original licence link has changed is not relivant.
34630 * <script type="text/javascript">
34633 * @class Roo.bootstrap.layout.Border
34634 * @extends Roo.bootstrap.layout.Manager
34635 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34636 * please see: examples/bootstrap/nested.html<br><br>
34638 <b>The container the layout is rendered into can be either the body element or any other element.
34639 If it is not the body element, the container needs to either be an absolute positioned element,
34640 or you will need to add "position:relative" to the css of the container. You will also need to specify
34641 the container size if it is not the body element.</b>
34644 * Create a new Border
34645 * @param {Object} config Configuration options
34647 Roo.bootstrap.layout.Border = function(config){
34648 config = config || {};
34649 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34653 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34654 if(config[region]){
34655 config[region].region = region;
34656 this.addRegion(config[region]);
34662 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34664 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34666 * Creates and adds a new region if it doesn't already exist.
34667 * @param {String} target The target region key (north, south, east, west or center).
34668 * @param {Object} config The regions config object
34669 * @return {BorderLayoutRegion} The new region
34671 addRegion : function(config)
34673 if(!this.regions[config.region]){
34674 var r = this.factory(config);
34675 this.bindRegion(r);
34677 return this.regions[config.region];
34681 bindRegion : function(r){
34682 this.regions[r.config.region] = r;
34684 r.on("visibilitychange", this.layout, this);
34685 r.on("paneladded", this.layout, this);
34686 r.on("panelremoved", this.layout, this);
34687 r.on("invalidated", this.layout, this);
34688 r.on("resized", this.onRegionResized, this);
34689 r.on("collapsed", this.onRegionCollapsed, this);
34690 r.on("expanded", this.onRegionExpanded, this);
34694 * Performs a layout update.
34696 layout : function()
34698 if(this.updating) {
34702 // render all the rebions if they have not been done alreayd?
34703 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34704 if(this.regions[region] && !this.regions[region].bodyEl){
34705 this.regions[region].onRender(this.el)
34709 var size = this.getViewSize();
34710 var w = size.width;
34711 var h = size.height;
34716 //var x = 0, y = 0;
34718 var rs = this.regions;
34719 var north = rs["north"];
34720 var south = rs["south"];
34721 var west = rs["west"];
34722 var east = rs["east"];
34723 var center = rs["center"];
34724 //if(this.hideOnLayout){ // not supported anymore
34725 //c.el.setStyle("display", "none");
34727 if(north && north.isVisible()){
34728 var b = north.getBox();
34729 var m = north.getMargins();
34730 b.width = w - (m.left+m.right);
34733 centerY = b.height + b.y + m.bottom;
34734 centerH -= centerY;
34735 north.updateBox(this.safeBox(b));
34737 if(south && south.isVisible()){
34738 var b = south.getBox();
34739 var m = south.getMargins();
34740 b.width = w - (m.left+m.right);
34742 var totalHeight = (b.height + m.top + m.bottom);
34743 b.y = h - totalHeight + m.top;
34744 centerH -= totalHeight;
34745 south.updateBox(this.safeBox(b));
34747 if(west && west.isVisible()){
34748 var b = west.getBox();
34749 var m = west.getMargins();
34750 b.height = centerH - (m.top+m.bottom);
34752 b.y = centerY + m.top;
34753 var totalWidth = (b.width + m.left + m.right);
34754 centerX += totalWidth;
34755 centerW -= totalWidth;
34756 west.updateBox(this.safeBox(b));
34758 if(east && east.isVisible()){
34759 var b = east.getBox();
34760 var m = east.getMargins();
34761 b.height = centerH - (m.top+m.bottom);
34762 var totalWidth = (b.width + m.left + m.right);
34763 b.x = w - totalWidth + m.left;
34764 b.y = centerY + m.top;
34765 centerW -= totalWidth;
34766 east.updateBox(this.safeBox(b));
34769 var m = center.getMargins();
34771 x: centerX + m.left,
34772 y: centerY + m.top,
34773 width: centerW - (m.left+m.right),
34774 height: centerH - (m.top+m.bottom)
34776 //if(this.hideOnLayout){
34777 //center.el.setStyle("display", "block");
34779 center.updateBox(this.safeBox(centerBox));
34782 this.fireEvent("layout", this);
34786 safeBox : function(box){
34787 box.width = Math.max(0, box.width);
34788 box.height = Math.max(0, box.height);
34793 * Adds a ContentPanel (or subclass) to this layout.
34794 * @param {String} target The target region key (north, south, east, west or center).
34795 * @param {Roo.ContentPanel} panel The panel to add
34796 * @return {Roo.ContentPanel} The added panel
34798 add : function(target, panel){
34800 target = target.toLowerCase();
34801 return this.regions[target].add(panel);
34805 * Remove a ContentPanel (or subclass) to this layout.
34806 * @param {String} target The target region key (north, south, east, west or center).
34807 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34808 * @return {Roo.ContentPanel} The removed panel
34810 remove : function(target, panel){
34811 target = target.toLowerCase();
34812 return this.regions[target].remove(panel);
34816 * Searches all regions for a panel with the specified id
34817 * @param {String} panelId
34818 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34820 findPanel : function(panelId){
34821 var rs = this.regions;
34822 for(var target in rs){
34823 if(typeof rs[target] != "function"){
34824 var p = rs[target].getPanel(panelId);
34834 * Searches all regions for a panel with the specified id and activates (shows) it.
34835 * @param {String/ContentPanel} panelId The panels id or the panel itself
34836 * @return {Roo.ContentPanel} The shown panel or null
34838 showPanel : function(panelId) {
34839 var rs = this.regions;
34840 for(var target in rs){
34841 var r = rs[target];
34842 if(typeof r != "function"){
34843 if(r.hasPanel(panelId)){
34844 return r.showPanel(panelId);
34852 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34853 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34856 restoreState : function(provider){
34858 provider = Roo.state.Manager;
34860 var sm = new Roo.LayoutStateManager();
34861 sm.init(this, provider);
34867 * Adds a xtype elements to the layout.
34871 xtype : 'ContentPanel',
34878 xtype : 'NestedLayoutPanel',
34884 items : [ ... list of content panels or nested layout panels.. ]
34888 * @param {Object} cfg Xtype definition of item to add.
34890 addxtype : function(cfg)
34892 // basically accepts a pannel...
34893 // can accept a layout region..!?!?
34894 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34897 // theory? children can only be panels??
34899 //if (!cfg.xtype.match(/Panel$/)) {
34904 if (typeof(cfg.region) == 'undefined') {
34905 Roo.log("Failed to add Panel, region was not set");
34909 var region = cfg.region;
34915 xitems = cfg.items;
34922 case 'Content': // ContentPanel (el, cfg)
34923 case 'Scroll': // ContentPanel (el, cfg)
34925 cfg.autoCreate = true;
34926 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34928 // var el = this.el.createChild();
34929 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34932 this.add(region, ret);
34936 case 'TreePanel': // our new panel!
34937 cfg.el = this.el.createChild();
34938 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34939 this.add(region, ret);
34944 // create a new Layout (which is a Border Layout...
34946 var clayout = cfg.layout;
34947 clayout.el = this.el.createChild();
34948 clayout.items = clayout.items || [];
34952 // replace this exitems with the clayout ones..
34953 xitems = clayout.items;
34955 // force background off if it's in center...
34956 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34957 cfg.background = false;
34959 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34962 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34963 //console.log('adding nested layout panel ' + cfg.toSource());
34964 this.add(region, ret);
34965 nb = {}; /// find first...
34970 // needs grid and region
34972 //var el = this.getRegion(region).el.createChild();
34974 *var el = this.el.createChild();
34975 // create the grid first...
34976 cfg.grid.container = el;
34977 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34980 if (region == 'center' && this.active ) {
34981 cfg.background = false;
34984 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34986 this.add(region, ret);
34988 if (cfg.background) {
34989 // render grid on panel activation (if panel background)
34990 ret.on('activate', function(gp) {
34991 if (!gp.grid.rendered) {
34992 // gp.grid.render(el);
34996 // cfg.grid.render(el);
35002 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35003 // it was the old xcomponent building that caused this before.
35004 // espeically if border is the top element in the tree.
35014 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35016 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35017 this.add(region, ret);
35021 throw "Can not add '" + cfg.xtype + "' to Border";
35027 this.beginUpdate();
35031 Roo.each(xitems, function(i) {
35032 region = nb && i.region ? i.region : false;
35034 var add = ret.addxtype(i);
35037 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35038 if (!i.background) {
35039 abn[region] = nb[region] ;
35046 // make the last non-background panel active..
35047 //if (nb) { Roo.log(abn); }
35050 for(var r in abn) {
35051 region = this.getRegion(r);
35053 // tried using nb[r], but it does not work..
35055 region.showPanel(abn[r]);
35066 factory : function(cfg)
35069 var validRegions = Roo.bootstrap.layout.Border.regions;
35071 var target = cfg.region;
35074 var r = Roo.bootstrap.layout;
35078 return new r.North(cfg);
35080 return new r.South(cfg);
35082 return new r.East(cfg);
35084 return new r.West(cfg);
35086 return new r.Center(cfg);
35088 throw 'Layout region "'+target+'" not supported.';
35095 * Ext JS Library 1.1.1
35096 * Copyright(c) 2006-2007, Ext JS, LLC.
35098 * Originally Released Under LGPL - original licence link has changed is not relivant.
35101 * <script type="text/javascript">
35105 * @class Roo.bootstrap.layout.Basic
35106 * @extends Roo.util.Observable
35107 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35108 * and does not have a titlebar, tabs or any other features. All it does is size and position
35109 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35110 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35111 * @cfg {string} region the region that it inhabits..
35112 * @cfg {bool} skipConfig skip config?
35116 Roo.bootstrap.layout.Basic = function(config){
35118 this.mgr = config.mgr;
35120 this.position = config.region;
35122 var skipConfig = config.skipConfig;
35126 * @scope Roo.BasicLayoutRegion
35130 * @event beforeremove
35131 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35132 * @param {Roo.LayoutRegion} this
35133 * @param {Roo.ContentPanel} panel The panel
35134 * @param {Object} e The cancel event object
35136 "beforeremove" : true,
35138 * @event invalidated
35139 * Fires when the layout for this region is changed.
35140 * @param {Roo.LayoutRegion} this
35142 "invalidated" : true,
35144 * @event visibilitychange
35145 * Fires when this region is shown or hidden
35146 * @param {Roo.LayoutRegion} this
35147 * @param {Boolean} visibility true or false
35149 "visibilitychange" : true,
35151 * @event paneladded
35152 * Fires when a panel is added.
35153 * @param {Roo.LayoutRegion} this
35154 * @param {Roo.ContentPanel} panel The panel
35156 "paneladded" : true,
35158 * @event panelremoved
35159 * Fires when a panel is removed.
35160 * @param {Roo.LayoutRegion} this
35161 * @param {Roo.ContentPanel} panel The panel
35163 "panelremoved" : true,
35165 * @event beforecollapse
35166 * Fires when this region before collapse.
35167 * @param {Roo.LayoutRegion} this
35169 "beforecollapse" : true,
35172 * Fires when this region is collapsed.
35173 * @param {Roo.LayoutRegion} this
35175 "collapsed" : true,
35178 * Fires when this region is expanded.
35179 * @param {Roo.LayoutRegion} this
35184 * Fires when this region is slid into view.
35185 * @param {Roo.LayoutRegion} this
35187 "slideshow" : true,
35190 * Fires when this region slides out of view.
35191 * @param {Roo.LayoutRegion} this
35193 "slidehide" : true,
35195 * @event panelactivated
35196 * Fires when a panel is activated.
35197 * @param {Roo.LayoutRegion} this
35198 * @param {Roo.ContentPanel} panel The activated panel
35200 "panelactivated" : true,
35203 * Fires when the user resizes this region.
35204 * @param {Roo.LayoutRegion} this
35205 * @param {Number} newSize The new size (width for east/west, height for north/south)
35209 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35210 this.panels = new Roo.util.MixedCollection();
35211 this.panels.getKey = this.getPanelId.createDelegate(this);
35213 this.activePanel = null;
35214 // ensure listeners are added...
35216 if (config.listeners || config.events) {
35217 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35218 listeners : config.listeners || {},
35219 events : config.events || {}
35223 if(skipConfig !== true){
35224 this.applyConfig(config);
35228 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35230 getPanelId : function(p){
35234 applyConfig : function(config){
35235 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35236 this.config = config;
35241 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35242 * the width, for horizontal (north, south) the height.
35243 * @param {Number} newSize The new width or height
35245 resizeTo : function(newSize){
35246 var el = this.el ? this.el :
35247 (this.activePanel ? this.activePanel.getEl() : null);
35249 switch(this.position){
35252 el.setWidth(newSize);
35253 this.fireEvent("resized", this, newSize);
35257 el.setHeight(newSize);
35258 this.fireEvent("resized", this, newSize);
35264 getBox : function(){
35265 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35268 getMargins : function(){
35269 return this.margins;
35272 updateBox : function(box){
35274 var el = this.activePanel.getEl();
35275 el.dom.style.left = box.x + "px";
35276 el.dom.style.top = box.y + "px";
35277 this.activePanel.setSize(box.width, box.height);
35281 * Returns the container element for this region.
35282 * @return {Roo.Element}
35284 getEl : function(){
35285 return this.activePanel;
35289 * Returns true if this region is currently visible.
35290 * @return {Boolean}
35292 isVisible : function(){
35293 return this.activePanel ? true : false;
35296 setActivePanel : function(panel){
35297 panel = this.getPanel(panel);
35298 if(this.activePanel && this.activePanel != panel){
35299 this.activePanel.setActiveState(false);
35300 this.activePanel.getEl().setLeftTop(-10000,-10000);
35302 this.activePanel = panel;
35303 panel.setActiveState(true);
35305 panel.setSize(this.box.width, this.box.height);
35307 this.fireEvent("panelactivated", this, panel);
35308 this.fireEvent("invalidated");
35312 * Show the specified panel.
35313 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35314 * @return {Roo.ContentPanel} The shown panel or null
35316 showPanel : function(panel){
35317 panel = this.getPanel(panel);
35319 this.setActivePanel(panel);
35325 * Get the active panel for this region.
35326 * @return {Roo.ContentPanel} The active panel or null
35328 getActivePanel : function(){
35329 return this.activePanel;
35333 * Add the passed ContentPanel(s)
35334 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35335 * @return {Roo.ContentPanel} The panel added (if only one was added)
35337 add : function(panel){
35338 if(arguments.length > 1){
35339 for(var i = 0, len = arguments.length; i < len; i++) {
35340 this.add(arguments[i]);
35344 if(this.hasPanel(panel)){
35345 this.showPanel(panel);
35348 var el = panel.getEl();
35349 if(el.dom.parentNode != this.mgr.el.dom){
35350 this.mgr.el.dom.appendChild(el.dom);
35352 if(panel.setRegion){
35353 panel.setRegion(this);
35355 this.panels.add(panel);
35356 el.setStyle("position", "absolute");
35357 if(!panel.background){
35358 this.setActivePanel(panel);
35359 if(this.config.initialSize && this.panels.getCount()==1){
35360 this.resizeTo(this.config.initialSize);
35363 this.fireEvent("paneladded", this, panel);
35368 * Returns true if the panel is in this region.
35369 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35370 * @return {Boolean}
35372 hasPanel : function(panel){
35373 if(typeof panel == "object"){ // must be panel obj
35374 panel = panel.getId();
35376 return this.getPanel(panel) ? true : false;
35380 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35381 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35382 * @param {Boolean} preservePanel Overrides the config preservePanel option
35383 * @return {Roo.ContentPanel} The panel that was removed
35385 remove : function(panel, preservePanel){
35386 panel = this.getPanel(panel);
35391 this.fireEvent("beforeremove", this, panel, e);
35392 if(e.cancel === true){
35395 var panelId = panel.getId();
35396 this.panels.removeKey(panelId);
35401 * Returns the panel specified or null if it's not in this region.
35402 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35403 * @return {Roo.ContentPanel}
35405 getPanel : function(id){
35406 if(typeof id == "object"){ // must be panel obj
35409 return this.panels.get(id);
35413 * Returns this regions position (north/south/east/west/center).
35416 getPosition: function(){
35417 return this.position;
35421 * Ext JS Library 1.1.1
35422 * Copyright(c) 2006-2007, Ext JS, LLC.
35424 * Originally Released Under LGPL - original licence link has changed is not relivant.
35427 * <script type="text/javascript">
35431 * @class Roo.bootstrap.layout.Region
35432 * @extends Roo.bootstrap.layout.Basic
35433 * This class represents a region in a layout manager.
35435 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35436 * @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})
35437 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35438 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35439 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35440 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35441 * @cfg {String} title The title for the region (overrides panel titles)
35442 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35443 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35444 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35445 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35446 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35447 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35448 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35449 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35450 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35451 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35453 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35454 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35455 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35456 * @cfg {Number} width For East/West panels
35457 * @cfg {Number} height For North/South panels
35458 * @cfg {Boolean} split To show the splitter
35459 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35461 * @cfg {string} cls Extra CSS classes to add to region
35463 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35464 * @cfg {string} region the region that it inhabits..
35467 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35468 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35470 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35471 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35472 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35474 Roo.bootstrap.layout.Region = function(config)
35476 this.applyConfig(config);
35478 var mgr = config.mgr;
35479 var pos = config.region;
35480 config.skipConfig = true;
35481 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35484 this.onRender(mgr.el);
35487 this.visible = true;
35488 this.collapsed = false;
35489 this.unrendered_panels = [];
35492 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35494 position: '', // set by wrapper (eg. north/south etc..)
35495 unrendered_panels : null, // unrendered panels.
35496 createBody : function(){
35497 /** This region's body element
35498 * @type Roo.Element */
35499 this.bodyEl = this.el.createChild({
35501 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35505 onRender: function(ctr, pos)
35507 var dh = Roo.DomHelper;
35508 /** This region's container element
35509 * @type Roo.Element */
35510 this.el = dh.append(ctr.dom, {
35512 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35514 /** This region's title element
35515 * @type Roo.Element */
35517 this.titleEl = dh.append(this.el.dom,
35520 unselectable: "on",
35521 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35523 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35524 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35527 this.titleEl.enableDisplayMode();
35528 /** This region's title text element
35529 * @type HTMLElement */
35530 this.titleTextEl = this.titleEl.dom.firstChild;
35531 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35533 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35534 this.closeBtn.enableDisplayMode();
35535 this.closeBtn.on("click", this.closeClicked, this);
35536 this.closeBtn.hide();
35538 this.createBody(this.config);
35539 if(this.config.hideWhenEmpty){
35541 this.on("paneladded", this.validateVisibility, this);
35542 this.on("panelremoved", this.validateVisibility, this);
35544 if(this.autoScroll){
35545 this.bodyEl.setStyle("overflow", "auto");
35547 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35549 //if(c.titlebar !== false){
35550 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35551 this.titleEl.hide();
35553 this.titleEl.show();
35554 if(this.config.title){
35555 this.titleTextEl.innerHTML = this.config.title;
35559 if(this.config.collapsed){
35560 this.collapse(true);
35562 if(this.config.hidden){
35566 if (this.unrendered_panels && this.unrendered_panels.length) {
35567 for (var i =0;i< this.unrendered_panels.length; i++) {
35568 this.add(this.unrendered_panels[i]);
35570 this.unrendered_panels = null;
35576 applyConfig : function(c)
35579 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35580 var dh = Roo.DomHelper;
35581 if(c.titlebar !== false){
35582 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35583 this.collapseBtn.on("click", this.collapse, this);
35584 this.collapseBtn.enableDisplayMode();
35586 if(c.showPin === true || this.showPin){
35587 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35588 this.stickBtn.enableDisplayMode();
35589 this.stickBtn.on("click", this.expand, this);
35590 this.stickBtn.hide();
35595 /** This region's collapsed element
35596 * @type Roo.Element */
35599 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35600 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35603 if(c.floatable !== false){
35604 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35605 this.collapsedEl.on("click", this.collapseClick, this);
35608 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35609 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35610 id: "message", unselectable: "on", style:{"float":"left"}});
35611 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35613 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35614 this.expandBtn.on("click", this.expand, this);
35618 if(this.collapseBtn){
35619 this.collapseBtn.setVisible(c.collapsible == true);
35622 this.cmargins = c.cmargins || this.cmargins ||
35623 (this.position == "west" || this.position == "east" ?
35624 {top: 0, left: 2, right:2, bottom: 0} :
35625 {top: 2, left: 0, right:0, bottom: 2});
35627 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35630 this.bottomTabs = c.tabPosition != "top";
35632 this.autoScroll = c.autoScroll || false;
35637 this.duration = c.duration || .30;
35638 this.slideDuration = c.slideDuration || .45;
35643 * Returns true if this region is currently visible.
35644 * @return {Boolean}
35646 isVisible : function(){
35647 return this.visible;
35651 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35652 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35654 //setCollapsedTitle : function(title){
35655 // title = title || " ";
35656 // if(this.collapsedTitleTextEl){
35657 // this.collapsedTitleTextEl.innerHTML = title;
35661 getBox : function(){
35663 // if(!this.collapsed){
35664 b = this.el.getBox(false, true);
35666 // b = this.collapsedEl.getBox(false, true);
35671 getMargins : function(){
35672 return this.margins;
35673 //return this.collapsed ? this.cmargins : this.margins;
35676 highlight : function(){
35677 this.el.addClass("x-layout-panel-dragover");
35680 unhighlight : function(){
35681 this.el.removeClass("x-layout-panel-dragover");
35684 updateBox : function(box)
35686 if (!this.bodyEl) {
35687 return; // not rendered yet..
35691 if(!this.collapsed){
35692 this.el.dom.style.left = box.x + "px";
35693 this.el.dom.style.top = box.y + "px";
35694 this.updateBody(box.width, box.height);
35696 this.collapsedEl.dom.style.left = box.x + "px";
35697 this.collapsedEl.dom.style.top = box.y + "px";
35698 this.collapsedEl.setSize(box.width, box.height);
35701 this.tabs.autoSizeTabs();
35705 updateBody : function(w, h)
35708 this.el.setWidth(w);
35709 w -= this.el.getBorderWidth("rl");
35710 if(this.config.adjustments){
35711 w += this.config.adjustments[0];
35714 if(h !== null && h > 0){
35715 this.el.setHeight(h);
35716 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35717 h -= this.el.getBorderWidth("tb");
35718 if(this.config.adjustments){
35719 h += this.config.adjustments[1];
35721 this.bodyEl.setHeight(h);
35723 h = this.tabs.syncHeight(h);
35726 if(this.panelSize){
35727 w = w !== null ? w : this.panelSize.width;
35728 h = h !== null ? h : this.panelSize.height;
35730 if(this.activePanel){
35731 var el = this.activePanel.getEl();
35732 w = w !== null ? w : el.getWidth();
35733 h = h !== null ? h : el.getHeight();
35734 this.panelSize = {width: w, height: h};
35735 this.activePanel.setSize(w, h);
35737 if(Roo.isIE && this.tabs){
35738 this.tabs.el.repaint();
35743 * Returns the container element for this region.
35744 * @return {Roo.Element}
35746 getEl : function(){
35751 * Hides this region.
35754 //if(!this.collapsed){
35755 this.el.dom.style.left = "-2000px";
35758 // this.collapsedEl.dom.style.left = "-2000px";
35759 // this.collapsedEl.hide();
35761 this.visible = false;
35762 this.fireEvent("visibilitychange", this, false);
35766 * Shows this region if it was previously hidden.
35769 //if(!this.collapsed){
35772 // this.collapsedEl.show();
35774 this.visible = true;
35775 this.fireEvent("visibilitychange", this, true);
35778 closeClicked : function(){
35779 if(this.activePanel){
35780 this.remove(this.activePanel);
35784 collapseClick : function(e){
35786 e.stopPropagation();
35789 e.stopPropagation();
35795 * Collapses this region.
35796 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35799 collapse : function(skipAnim, skipCheck = false){
35800 if(this.collapsed) {
35804 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35806 this.collapsed = true;
35808 this.split.el.hide();
35810 if(this.config.animate && skipAnim !== true){
35811 this.fireEvent("invalidated", this);
35812 this.animateCollapse();
35814 this.el.setLocation(-20000,-20000);
35816 this.collapsedEl.show();
35817 this.fireEvent("collapsed", this);
35818 this.fireEvent("invalidated", this);
35824 animateCollapse : function(){
35829 * Expands this region if it was previously collapsed.
35830 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35831 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35834 expand : function(e, skipAnim){
35836 e.stopPropagation();
35838 if(!this.collapsed || this.el.hasActiveFx()) {
35842 this.afterSlideIn();
35845 this.collapsed = false;
35846 if(this.config.animate && skipAnim !== true){
35847 this.animateExpand();
35851 this.split.el.show();
35853 this.collapsedEl.setLocation(-2000,-2000);
35854 this.collapsedEl.hide();
35855 this.fireEvent("invalidated", this);
35856 this.fireEvent("expanded", this);
35860 animateExpand : function(){
35864 initTabs : function()
35866 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35868 var ts = new Roo.bootstrap.panel.Tabs({
35869 el: this.bodyEl.dom,
35870 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35871 disableTooltips: this.config.disableTabTips,
35872 toolbar : this.config.toolbar
35875 if(this.config.hideTabs){
35876 ts.stripWrap.setDisplayed(false);
35879 ts.resizeTabs = this.config.resizeTabs === true;
35880 ts.minTabWidth = this.config.minTabWidth || 40;
35881 ts.maxTabWidth = this.config.maxTabWidth || 250;
35882 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35883 ts.monitorResize = false;
35884 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35885 ts.bodyEl.addClass('roo-layout-tabs-body');
35886 this.panels.each(this.initPanelAsTab, this);
35889 initPanelAsTab : function(panel){
35890 var ti = this.tabs.addTab(
35894 this.config.closeOnTab && panel.isClosable(),
35897 if(panel.tabTip !== undefined){
35898 ti.setTooltip(panel.tabTip);
35900 ti.on("activate", function(){
35901 this.setActivePanel(panel);
35904 if(this.config.closeOnTab){
35905 ti.on("beforeclose", function(t, e){
35907 this.remove(panel);
35911 panel.tabItem = ti;
35916 updatePanelTitle : function(panel, title)
35918 if(this.activePanel == panel){
35919 this.updateTitle(title);
35922 var ti = this.tabs.getTab(panel.getEl().id);
35924 if(panel.tabTip !== undefined){
35925 ti.setTooltip(panel.tabTip);
35930 updateTitle : function(title){
35931 if(this.titleTextEl && !this.config.title){
35932 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35936 setActivePanel : function(panel)
35938 panel = this.getPanel(panel);
35939 if(this.activePanel && this.activePanel != panel){
35940 if(this.activePanel.setActiveState(false) === false){
35944 this.activePanel = panel;
35945 panel.setActiveState(true);
35946 if(this.panelSize){
35947 panel.setSize(this.panelSize.width, this.panelSize.height);
35950 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35952 this.updateTitle(panel.getTitle());
35954 this.fireEvent("invalidated", this);
35956 this.fireEvent("panelactivated", this, panel);
35960 * Shows the specified panel.
35961 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35962 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35964 showPanel : function(panel)
35966 panel = this.getPanel(panel);
35969 var tab = this.tabs.getTab(panel.getEl().id);
35970 if(tab.isHidden()){
35971 this.tabs.unhideTab(tab.id);
35975 this.setActivePanel(panel);
35982 * Get the active panel for this region.
35983 * @return {Roo.ContentPanel} The active panel or null
35985 getActivePanel : function(){
35986 return this.activePanel;
35989 validateVisibility : function(){
35990 if(this.panels.getCount() < 1){
35991 this.updateTitle(" ");
35992 this.closeBtn.hide();
35995 if(!this.isVisible()){
36002 * Adds the passed ContentPanel(s) to this region.
36003 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36004 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36006 add : function(panel)
36008 if(arguments.length > 1){
36009 for(var i = 0, len = arguments.length; i < len; i++) {
36010 this.add(arguments[i]);
36015 // if we have not been rendered yet, then we can not really do much of this..
36016 if (!this.bodyEl) {
36017 this.unrendered_panels.push(panel);
36024 if(this.hasPanel(panel)){
36025 this.showPanel(panel);
36028 panel.setRegion(this);
36029 this.panels.add(panel);
36030 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36031 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36032 // and hide them... ???
36033 this.bodyEl.dom.appendChild(panel.getEl().dom);
36034 if(panel.background !== true){
36035 this.setActivePanel(panel);
36037 this.fireEvent("paneladded", this, panel);
36044 this.initPanelAsTab(panel);
36048 if(panel.background !== true){
36049 this.tabs.activate(panel.getEl().id);
36051 this.fireEvent("paneladded", this, panel);
36056 * Hides the tab for the specified panel.
36057 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36059 hidePanel : function(panel){
36060 if(this.tabs && (panel = this.getPanel(panel))){
36061 this.tabs.hideTab(panel.getEl().id);
36066 * Unhides the tab for a previously hidden panel.
36067 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36069 unhidePanel : function(panel){
36070 if(this.tabs && (panel = this.getPanel(panel))){
36071 this.tabs.unhideTab(panel.getEl().id);
36075 clearPanels : function(){
36076 while(this.panels.getCount() > 0){
36077 this.remove(this.panels.first());
36082 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36083 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36084 * @param {Boolean} preservePanel Overrides the config preservePanel option
36085 * @return {Roo.ContentPanel} The panel that was removed
36087 remove : function(panel, preservePanel)
36089 panel = this.getPanel(panel);
36094 this.fireEvent("beforeremove", this, panel, e);
36095 if(e.cancel === true){
36098 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36099 var panelId = panel.getId();
36100 this.panels.removeKey(panelId);
36102 document.body.appendChild(panel.getEl().dom);
36105 this.tabs.removeTab(panel.getEl().id);
36106 }else if (!preservePanel){
36107 this.bodyEl.dom.removeChild(panel.getEl().dom);
36109 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36110 var p = this.panels.first();
36111 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36112 tempEl.appendChild(p.getEl().dom);
36113 this.bodyEl.update("");
36114 this.bodyEl.dom.appendChild(p.getEl().dom);
36116 this.updateTitle(p.getTitle());
36118 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36119 this.setActivePanel(p);
36121 panel.setRegion(null);
36122 if(this.activePanel == panel){
36123 this.activePanel = null;
36125 if(this.config.autoDestroy !== false && preservePanel !== true){
36126 try{panel.destroy();}catch(e){}
36128 this.fireEvent("panelremoved", this, panel);
36133 * Returns the TabPanel component used by this region
36134 * @return {Roo.TabPanel}
36136 getTabs : function(){
36140 createTool : function(parentEl, className){
36141 var btn = Roo.DomHelper.append(parentEl, {
36143 cls: "x-layout-tools-button",
36146 cls: "roo-layout-tools-button-inner " + className,
36150 btn.addClassOnOver("roo-layout-tools-button-over");
36155 * Ext JS Library 1.1.1
36156 * Copyright(c) 2006-2007, Ext JS, LLC.
36158 * Originally Released Under LGPL - original licence link has changed is not relivant.
36161 * <script type="text/javascript">
36167 * @class Roo.SplitLayoutRegion
36168 * @extends Roo.LayoutRegion
36169 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36171 Roo.bootstrap.layout.Split = function(config){
36172 this.cursor = config.cursor;
36173 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36176 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36178 splitTip : "Drag to resize.",
36179 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36180 useSplitTips : false,
36182 applyConfig : function(config){
36183 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36186 onRender : function(ctr,pos) {
36188 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36189 if(!this.config.split){
36194 var splitEl = Roo.DomHelper.append(ctr.dom, {
36196 id: this.el.id + "-split",
36197 cls: "roo-layout-split roo-layout-split-"+this.position,
36200 /** The SplitBar for this region
36201 * @type Roo.SplitBar */
36202 // does not exist yet...
36203 Roo.log([this.position, this.orientation]);
36205 this.split = new Roo.bootstrap.SplitBar({
36206 dragElement : splitEl,
36207 resizingElement: this.el,
36208 orientation : this.orientation
36211 this.split.on("moved", this.onSplitMove, this);
36212 this.split.useShim = this.config.useShim === true;
36213 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36214 if(this.useSplitTips){
36215 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36217 //if(config.collapsible){
36218 // this.split.el.on("dblclick", this.collapse, this);
36221 if(typeof this.config.minSize != "undefined"){
36222 this.split.minSize = this.config.minSize;
36224 if(typeof this.config.maxSize != "undefined"){
36225 this.split.maxSize = this.config.maxSize;
36227 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36228 this.hideSplitter();
36233 getHMaxSize : function(){
36234 var cmax = this.config.maxSize || 10000;
36235 var center = this.mgr.getRegion("center");
36236 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36239 getVMaxSize : function(){
36240 var cmax = this.config.maxSize || 10000;
36241 var center = this.mgr.getRegion("center");
36242 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36245 onSplitMove : function(split, newSize){
36246 this.fireEvent("resized", this, newSize);
36250 * Returns the {@link Roo.SplitBar} for this region.
36251 * @return {Roo.SplitBar}
36253 getSplitBar : function(){
36258 this.hideSplitter();
36259 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36262 hideSplitter : function(){
36264 this.split.el.setLocation(-2000,-2000);
36265 this.split.el.hide();
36271 this.split.el.show();
36273 Roo.bootstrap.layout.Split.superclass.show.call(this);
36276 beforeSlide: function(){
36277 if(Roo.isGecko){// firefox overflow auto bug workaround
36278 this.bodyEl.clip();
36280 this.tabs.bodyEl.clip();
36282 if(this.activePanel){
36283 this.activePanel.getEl().clip();
36285 if(this.activePanel.beforeSlide){
36286 this.activePanel.beforeSlide();
36292 afterSlide : function(){
36293 if(Roo.isGecko){// firefox overflow auto bug workaround
36294 this.bodyEl.unclip();
36296 this.tabs.bodyEl.unclip();
36298 if(this.activePanel){
36299 this.activePanel.getEl().unclip();
36300 if(this.activePanel.afterSlide){
36301 this.activePanel.afterSlide();
36307 initAutoHide : function(){
36308 if(this.autoHide !== false){
36309 if(!this.autoHideHd){
36310 var st = new Roo.util.DelayedTask(this.slideIn, this);
36311 this.autoHideHd = {
36312 "mouseout": function(e){
36313 if(!e.within(this.el, true)){
36317 "mouseover" : function(e){
36323 this.el.on(this.autoHideHd);
36327 clearAutoHide : function(){
36328 if(this.autoHide !== false){
36329 this.el.un("mouseout", this.autoHideHd.mouseout);
36330 this.el.un("mouseover", this.autoHideHd.mouseover);
36334 clearMonitor : function(){
36335 Roo.get(document).un("click", this.slideInIf, this);
36338 // these names are backwards but not changed for compat
36339 slideOut : function(){
36340 if(this.isSlid || this.el.hasActiveFx()){
36343 this.isSlid = true;
36344 if(this.collapseBtn){
36345 this.collapseBtn.hide();
36347 this.closeBtnState = this.closeBtn.getStyle('display');
36348 this.closeBtn.hide();
36350 this.stickBtn.show();
36353 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36354 this.beforeSlide();
36355 this.el.setStyle("z-index", 10001);
36356 this.el.slideIn(this.getSlideAnchor(), {
36357 callback: function(){
36359 this.initAutoHide();
36360 Roo.get(document).on("click", this.slideInIf, this);
36361 this.fireEvent("slideshow", this);
36368 afterSlideIn : function(){
36369 this.clearAutoHide();
36370 this.isSlid = false;
36371 this.clearMonitor();
36372 this.el.setStyle("z-index", "");
36373 if(this.collapseBtn){
36374 this.collapseBtn.show();
36376 this.closeBtn.setStyle('display', this.closeBtnState);
36378 this.stickBtn.hide();
36380 this.fireEvent("slidehide", this);
36383 slideIn : function(cb){
36384 if(!this.isSlid || this.el.hasActiveFx()){
36388 this.isSlid = false;
36389 this.beforeSlide();
36390 this.el.slideOut(this.getSlideAnchor(), {
36391 callback: function(){
36392 this.el.setLeftTop(-10000, -10000);
36394 this.afterSlideIn();
36402 slideInIf : function(e){
36403 if(!e.within(this.el)){
36408 animateCollapse : function(){
36409 this.beforeSlide();
36410 this.el.setStyle("z-index", 20000);
36411 var anchor = this.getSlideAnchor();
36412 this.el.slideOut(anchor, {
36413 callback : function(){
36414 this.el.setStyle("z-index", "");
36415 this.collapsedEl.slideIn(anchor, {duration:.3});
36417 this.el.setLocation(-10000,-10000);
36419 this.fireEvent("collapsed", this);
36426 animateExpand : function(){
36427 this.beforeSlide();
36428 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36429 this.el.setStyle("z-index", 20000);
36430 this.collapsedEl.hide({
36433 this.el.slideIn(this.getSlideAnchor(), {
36434 callback : function(){
36435 this.el.setStyle("z-index", "");
36438 this.split.el.show();
36440 this.fireEvent("invalidated", this);
36441 this.fireEvent("expanded", this);
36469 getAnchor : function(){
36470 return this.anchors[this.position];
36473 getCollapseAnchor : function(){
36474 return this.canchors[this.position];
36477 getSlideAnchor : function(){
36478 return this.sanchors[this.position];
36481 getAlignAdj : function(){
36482 var cm = this.cmargins;
36483 switch(this.position){
36499 getExpandAdj : function(){
36500 var c = this.collapsedEl, cm = this.cmargins;
36501 switch(this.position){
36503 return [-(cm.right+c.getWidth()+cm.left), 0];
36506 return [cm.right+c.getWidth()+cm.left, 0];
36509 return [0, -(cm.top+cm.bottom+c.getHeight())];
36512 return [0, cm.top+cm.bottom+c.getHeight()];
36518 * Ext JS Library 1.1.1
36519 * Copyright(c) 2006-2007, Ext JS, LLC.
36521 * Originally Released Under LGPL - original licence link has changed is not relivant.
36524 * <script type="text/javascript">
36527 * These classes are private internal classes
36529 Roo.bootstrap.layout.Center = function(config){
36530 config.region = "center";
36531 Roo.bootstrap.layout.Region.call(this, config);
36532 this.visible = true;
36533 this.minWidth = config.minWidth || 20;
36534 this.minHeight = config.minHeight || 20;
36537 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36539 // center panel can't be hidden
36543 // center panel can't be hidden
36546 getMinWidth: function(){
36547 return this.minWidth;
36550 getMinHeight: function(){
36551 return this.minHeight;
36564 Roo.bootstrap.layout.North = function(config)
36566 config.region = 'north';
36567 config.cursor = 'n-resize';
36569 Roo.bootstrap.layout.Split.call(this, config);
36573 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36574 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36575 this.split.el.addClass("roo-layout-split-v");
36577 var size = config.initialSize || config.height;
36578 if(typeof size != "undefined"){
36579 this.el.setHeight(size);
36582 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36584 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36588 getBox : function(){
36589 if(this.collapsed){
36590 return this.collapsedEl.getBox();
36592 var box = this.el.getBox();
36594 box.height += this.split.el.getHeight();
36599 updateBox : function(box){
36600 if(this.split && !this.collapsed){
36601 box.height -= this.split.el.getHeight();
36602 this.split.el.setLeft(box.x);
36603 this.split.el.setTop(box.y+box.height);
36604 this.split.el.setWidth(box.width);
36606 if(this.collapsed){
36607 this.updateBody(box.width, null);
36609 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36617 Roo.bootstrap.layout.South = function(config){
36618 config.region = 'south';
36619 config.cursor = 's-resize';
36620 Roo.bootstrap.layout.Split.call(this, config);
36622 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36623 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36624 this.split.el.addClass("roo-layout-split-v");
36626 var size = config.initialSize || config.height;
36627 if(typeof size != "undefined"){
36628 this.el.setHeight(size);
36632 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36633 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36634 getBox : function(){
36635 if(this.collapsed){
36636 return this.collapsedEl.getBox();
36638 var box = this.el.getBox();
36640 var sh = this.split.el.getHeight();
36647 updateBox : function(box){
36648 if(this.split && !this.collapsed){
36649 var sh = this.split.el.getHeight();
36652 this.split.el.setLeft(box.x);
36653 this.split.el.setTop(box.y-sh);
36654 this.split.el.setWidth(box.width);
36656 if(this.collapsed){
36657 this.updateBody(box.width, null);
36659 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36663 Roo.bootstrap.layout.East = function(config){
36664 config.region = "east";
36665 config.cursor = "e-resize";
36666 Roo.bootstrap.layout.Split.call(this, config);
36668 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36669 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36670 this.split.el.addClass("roo-layout-split-h");
36672 var size = config.initialSize || config.width;
36673 if(typeof size != "undefined"){
36674 this.el.setWidth(size);
36677 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36678 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36679 getBox : function(){
36680 if(this.collapsed){
36681 return this.collapsedEl.getBox();
36683 var box = this.el.getBox();
36685 var sw = this.split.el.getWidth();
36692 updateBox : function(box){
36693 if(this.split && !this.collapsed){
36694 var sw = this.split.el.getWidth();
36696 this.split.el.setLeft(box.x);
36697 this.split.el.setTop(box.y);
36698 this.split.el.setHeight(box.height);
36701 if(this.collapsed){
36702 this.updateBody(null, box.height);
36704 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36708 Roo.bootstrap.layout.West = function(config){
36709 config.region = "west";
36710 config.cursor = "w-resize";
36712 Roo.bootstrap.layout.Split.call(this, config);
36714 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36715 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36716 this.split.el.addClass("roo-layout-split-h");
36720 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36721 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36723 onRender: function(ctr, pos)
36725 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36726 var size = this.config.initialSize || this.config.width;
36727 if(typeof size != "undefined"){
36728 this.el.setWidth(size);
36732 getBox : function(){
36733 if(this.collapsed){
36734 return this.collapsedEl.getBox();
36736 var box = this.el.getBox();
36738 box.width += this.split.el.getWidth();
36743 updateBox : function(box){
36744 if(this.split && !this.collapsed){
36745 var sw = this.split.el.getWidth();
36747 this.split.el.setLeft(box.x+box.width);
36748 this.split.el.setTop(box.y);
36749 this.split.el.setHeight(box.height);
36751 if(this.collapsed){
36752 this.updateBody(null, box.height);
36754 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36757 Roo.namespace("Roo.bootstrap.panel");/*
36759 * Ext JS Library 1.1.1
36760 * Copyright(c) 2006-2007, Ext JS, LLC.
36762 * Originally Released Under LGPL - original licence link has changed is not relivant.
36765 * <script type="text/javascript">
36768 * @class Roo.ContentPanel
36769 * @extends Roo.util.Observable
36770 * A basic ContentPanel element.
36771 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36772 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36773 * @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
36774 * @cfg {Boolean} closable True if the panel can be closed/removed
36775 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36776 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36777 * @cfg {Toolbar} toolbar A toolbar for this panel
36778 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36779 * @cfg {String} title The title for this panel
36780 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36781 * @cfg {String} url Calls {@link #setUrl} with this value
36782 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36783 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36784 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36785 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36786 * @cfg {Boolean} badges render the badges
36789 * Create a new ContentPanel.
36790 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36791 * @param {String/Object} config A string to set only the title or a config object
36792 * @param {String} content (optional) Set the HTML content for this panel
36793 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36795 Roo.bootstrap.panel.Content = function( config){
36797 this.tpl = config.tpl || false;
36799 var el = config.el;
36800 var content = config.content;
36802 if(config.autoCreate){ // xtype is available if this is called from factory
36805 this.el = Roo.get(el);
36806 if(!this.el && config && config.autoCreate){
36807 if(typeof config.autoCreate == "object"){
36808 if(!config.autoCreate.id){
36809 config.autoCreate.id = config.id||el;
36811 this.el = Roo.DomHelper.append(document.body,
36812 config.autoCreate, true);
36814 var elcfg = { tag: "div",
36815 cls: "roo-layout-inactive-content",
36819 elcfg.html = config.html;
36823 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36826 this.closable = false;
36827 this.loaded = false;
36828 this.active = false;
36831 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36833 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36835 this.wrapEl = this.el; //this.el.wrap();
36837 if (config.toolbar.items) {
36838 ti = config.toolbar.items ;
36839 delete config.toolbar.items ;
36843 this.toolbar.render(this.wrapEl, 'before');
36844 for(var i =0;i < ti.length;i++) {
36845 // Roo.log(['add child', items[i]]);
36846 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36848 this.toolbar.items = nitems;
36849 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36850 delete config.toolbar;
36854 // xtype created footer. - not sure if will work as we normally have to render first..
36855 if (this.footer && !this.footer.el && this.footer.xtype) {
36856 if (!this.wrapEl) {
36857 this.wrapEl = this.el.wrap();
36860 this.footer.container = this.wrapEl.createChild();
36862 this.footer = Roo.factory(this.footer, Roo);
36867 if(typeof config == "string"){
36868 this.title = config;
36870 Roo.apply(this, config);
36874 this.resizeEl = Roo.get(this.resizeEl, true);
36876 this.resizeEl = this.el;
36878 // handle view.xtype
36886 * Fires when this panel is activated.
36887 * @param {Roo.ContentPanel} this
36891 * @event deactivate
36892 * Fires when this panel is activated.
36893 * @param {Roo.ContentPanel} this
36895 "deactivate" : true,
36899 * Fires when this panel is resized if fitToFrame is true.
36900 * @param {Roo.ContentPanel} this
36901 * @param {Number} width The width after any component adjustments
36902 * @param {Number} height The height after any component adjustments
36908 * Fires when this tab is created
36909 * @param {Roo.ContentPanel} this
36920 if(this.autoScroll){
36921 this.resizeEl.setStyle("overflow", "auto");
36923 // fix randome scrolling
36924 //this.el.on('scroll', function() {
36925 // Roo.log('fix random scolling');
36926 // this.scrollTo('top',0);
36929 content = content || this.content;
36931 this.setContent(content);
36933 if(config && config.url){
36934 this.setUrl(this.url, this.params, this.loadOnce);
36939 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36941 if (this.view && typeof(this.view.xtype) != 'undefined') {
36942 this.view.el = this.el.appendChild(document.createElement("div"));
36943 this.view = Roo.factory(this.view);
36944 this.view.render && this.view.render(false, '');
36948 this.fireEvent('render', this);
36951 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36955 setRegion : function(region){
36956 this.region = region;
36957 this.setActiveClass(region && !this.background);
36961 setActiveClass: function(state)
36964 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36965 this.el.setStyle('position','relative');
36967 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36968 this.el.setStyle('position', 'absolute');
36973 * Returns the toolbar for this Panel if one was configured.
36974 * @return {Roo.Toolbar}
36976 getToolbar : function(){
36977 return this.toolbar;
36980 setActiveState : function(active)
36982 this.active = active;
36983 this.setActiveClass(active);
36985 if(this.fireEvent("deactivate", this) === false){
36990 this.fireEvent("activate", this);
36994 * Updates this panel's element
36995 * @param {String} content The new content
36996 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36998 setContent : function(content, loadScripts){
36999 this.el.update(content, loadScripts);
37002 ignoreResize : function(w, h){
37003 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37006 this.lastSize = {width: w, height: h};
37011 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37012 * @return {Roo.UpdateManager} The UpdateManager
37014 getUpdateManager : function(){
37015 return this.el.getUpdateManager();
37018 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37019 * @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:
37022 url: "your-url.php",
37023 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37024 callback: yourFunction,
37025 scope: yourObject, //(optional scope)
37028 text: "Loading...",
37033 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37034 * 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.
37035 * @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}
37036 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37037 * @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.
37038 * @return {Roo.ContentPanel} this
37041 var um = this.el.getUpdateManager();
37042 um.update.apply(um, arguments);
37048 * 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.
37049 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37050 * @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)
37051 * @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)
37052 * @return {Roo.UpdateManager} The UpdateManager
37054 setUrl : function(url, params, loadOnce){
37055 if(this.refreshDelegate){
37056 this.removeListener("activate", this.refreshDelegate);
37058 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37059 this.on("activate", this.refreshDelegate);
37060 return this.el.getUpdateManager();
37063 _handleRefresh : function(url, params, loadOnce){
37064 if(!loadOnce || !this.loaded){
37065 var updater = this.el.getUpdateManager();
37066 updater.update(url, params, this._setLoaded.createDelegate(this));
37070 _setLoaded : function(){
37071 this.loaded = true;
37075 * Returns this panel's id
37078 getId : function(){
37083 * Returns this panel's element - used by regiosn to add.
37084 * @return {Roo.Element}
37086 getEl : function(){
37087 return this.wrapEl || this.el;
37092 adjustForComponents : function(width, height)
37094 //Roo.log('adjustForComponents ');
37095 if(this.resizeEl != this.el){
37096 width -= this.el.getFrameWidth('lr');
37097 height -= this.el.getFrameWidth('tb');
37100 var te = this.toolbar.getEl();
37101 te.setWidth(width);
37102 height -= te.getHeight();
37105 var te = this.footer.getEl();
37106 te.setWidth(width);
37107 height -= te.getHeight();
37111 if(this.adjustments){
37112 width += this.adjustments[0];
37113 height += this.adjustments[1];
37115 return {"width": width, "height": height};
37118 setSize : function(width, height){
37119 if(this.fitToFrame && !this.ignoreResize(width, height)){
37120 if(this.fitContainer && this.resizeEl != this.el){
37121 this.el.setSize(width, height);
37123 var size = this.adjustForComponents(width, height);
37124 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37125 this.fireEvent('resize', this, size.width, size.height);
37130 * Returns this panel's title
37133 getTitle : function(){
37135 if (typeof(this.title) != 'object') {
37140 for (var k in this.title) {
37141 if (!this.title.hasOwnProperty(k)) {
37145 if (k.indexOf('-') >= 0) {
37146 var s = k.split('-');
37147 for (var i = 0; i<s.length; i++) {
37148 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37151 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37158 * Set this panel's title
37159 * @param {String} title
37161 setTitle : function(title){
37162 this.title = title;
37164 this.region.updatePanelTitle(this, title);
37169 * Returns true is this panel was configured to be closable
37170 * @return {Boolean}
37172 isClosable : function(){
37173 return this.closable;
37176 beforeSlide : function(){
37178 this.resizeEl.clip();
37181 afterSlide : function(){
37183 this.resizeEl.unclip();
37187 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37188 * Will fail silently if the {@link #setUrl} method has not been called.
37189 * This does not activate the panel, just updates its content.
37191 refresh : function(){
37192 if(this.refreshDelegate){
37193 this.loaded = false;
37194 this.refreshDelegate();
37199 * Destroys this panel
37201 destroy : function(){
37202 this.el.removeAllListeners();
37203 var tempEl = document.createElement("span");
37204 tempEl.appendChild(this.el.dom);
37205 tempEl.innerHTML = "";
37211 * form - if the content panel contains a form - this is a reference to it.
37212 * @type {Roo.form.Form}
37216 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37217 * This contains a reference to it.
37223 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37233 * @param {Object} cfg Xtype definition of item to add.
37237 getChildContainer: function () {
37238 return this.getEl();
37243 var ret = new Roo.factory(cfg);
37248 if (cfg.xtype.match(/^Form$/)) {
37251 //if (this.footer) {
37252 // el = this.footer.container.insertSibling(false, 'before');
37254 el = this.el.createChild();
37257 this.form = new Roo.form.Form(cfg);
37260 if ( this.form.allItems.length) {
37261 this.form.render(el.dom);
37265 // should only have one of theses..
37266 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37267 // views.. should not be just added - used named prop 'view''
37269 cfg.el = this.el.appendChild(document.createElement("div"));
37272 var ret = new Roo.factory(cfg);
37274 ret.render && ret.render(false, ''); // render blank..
37284 * @class Roo.bootstrap.panel.Grid
37285 * @extends Roo.bootstrap.panel.Content
37287 * Create a new GridPanel.
37288 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37289 * @param {Object} config A the config object
37295 Roo.bootstrap.panel.Grid = function(config)
37299 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37300 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37302 config.el = this.wrapper;
37303 //this.el = this.wrapper;
37305 if (config.container) {
37306 // ctor'ed from a Border/panel.grid
37309 this.wrapper.setStyle("overflow", "hidden");
37310 this.wrapper.addClass('roo-grid-container');
37315 if(config.toolbar){
37316 var tool_el = this.wrapper.createChild();
37317 this.toolbar = Roo.factory(config.toolbar);
37319 if (config.toolbar.items) {
37320 ti = config.toolbar.items ;
37321 delete config.toolbar.items ;
37325 this.toolbar.render(tool_el);
37326 for(var i =0;i < ti.length;i++) {
37327 // Roo.log(['add child', items[i]]);
37328 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37330 this.toolbar.items = nitems;
37332 delete config.toolbar;
37335 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37336 config.grid.scrollBody = true;;
37337 config.grid.monitorWindowResize = false; // turn off autosizing
37338 config.grid.autoHeight = false;
37339 config.grid.autoWidth = false;
37341 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37343 if (config.background) {
37344 // render grid on panel activation (if panel background)
37345 this.on('activate', function(gp) {
37346 if (!gp.grid.rendered) {
37347 gp.grid.render(this.wrapper);
37348 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37353 this.grid.render(this.wrapper);
37354 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37357 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37358 // ??? needed ??? config.el = this.wrapper;
37363 // xtype created footer. - not sure if will work as we normally have to render first..
37364 if (this.footer && !this.footer.el && this.footer.xtype) {
37366 var ctr = this.grid.getView().getFooterPanel(true);
37367 this.footer.dataSource = this.grid.dataSource;
37368 this.footer = Roo.factory(this.footer, Roo);
37369 this.footer.render(ctr);
37379 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37380 getId : function(){
37381 return this.grid.id;
37385 * Returns the grid for this panel
37386 * @return {Roo.bootstrap.Table}
37388 getGrid : function(){
37392 setSize : function(width, height){
37393 if(!this.ignoreResize(width, height)){
37394 var grid = this.grid;
37395 var size = this.adjustForComponents(width, height);
37396 var gridel = grid.getGridEl();
37397 gridel.setSize(size.width, size.height);
37399 var thd = grid.getGridEl().select('thead',true).first();
37400 var tbd = grid.getGridEl().select('tbody', true).first();
37402 tbd.setSize(width, height - thd.getHeight());
37411 beforeSlide : function(){
37412 this.grid.getView().scroller.clip();
37415 afterSlide : function(){
37416 this.grid.getView().scroller.unclip();
37419 destroy : function(){
37420 this.grid.destroy();
37422 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37427 * @class Roo.bootstrap.panel.Nest
37428 * @extends Roo.bootstrap.panel.Content
37430 * Create a new Panel, that can contain a layout.Border.
37433 * @param {Roo.BorderLayout} layout The layout for this panel
37434 * @param {String/Object} config A string to set only the title or a config object
37436 Roo.bootstrap.panel.Nest = function(config)
37438 // construct with only one argument..
37439 /* FIXME - implement nicer consturctors
37440 if (layout.layout) {
37442 layout = config.layout;
37443 delete config.layout;
37445 if (layout.xtype && !layout.getEl) {
37446 // then layout needs constructing..
37447 layout = Roo.factory(layout, Roo);
37451 config.el = config.layout.getEl();
37453 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37455 config.layout.monitorWindowResize = false; // turn off autosizing
37456 this.layout = config.layout;
37457 this.layout.getEl().addClass("roo-layout-nested-layout");
37464 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37466 setSize : function(width, height){
37467 if(!this.ignoreResize(width, height)){
37468 var size = this.adjustForComponents(width, height);
37469 var el = this.layout.getEl();
37470 if (size.height < 1) {
37471 el.setWidth(size.width);
37473 el.setSize(size.width, size.height);
37475 var touch = el.dom.offsetWidth;
37476 this.layout.layout();
37477 // ie requires a double layout on the first pass
37478 if(Roo.isIE && !this.initialized){
37479 this.initialized = true;
37480 this.layout.layout();
37485 // activate all subpanels if not currently active..
37487 setActiveState : function(active){
37488 this.active = active;
37489 this.setActiveClass(active);
37492 this.fireEvent("deactivate", this);
37496 this.fireEvent("activate", this);
37497 // not sure if this should happen before or after..
37498 if (!this.layout) {
37499 return; // should not happen..
37502 for (var r in this.layout.regions) {
37503 reg = this.layout.getRegion(r);
37504 if (reg.getActivePanel()) {
37505 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37506 reg.setActivePanel(reg.getActivePanel());
37509 if (!reg.panels.length) {
37512 reg.showPanel(reg.getPanel(0));
37521 * Returns the nested BorderLayout for this panel
37522 * @return {Roo.BorderLayout}
37524 getLayout : function(){
37525 return this.layout;
37529 * Adds a xtype elements to the layout of the nested panel
37533 xtype : 'ContentPanel',
37540 xtype : 'NestedLayoutPanel',
37546 items : [ ... list of content panels or nested layout panels.. ]
37550 * @param {Object} cfg Xtype definition of item to add.
37552 addxtype : function(cfg) {
37553 return this.layout.addxtype(cfg);
37558 * Ext JS Library 1.1.1
37559 * Copyright(c) 2006-2007, Ext JS, LLC.
37561 * Originally Released Under LGPL - original licence link has changed is not relivant.
37564 * <script type="text/javascript">
37567 * @class Roo.TabPanel
37568 * @extends Roo.util.Observable
37569 * A lightweight tab container.
37573 // basic tabs 1, built from existing content
37574 var tabs = new Roo.TabPanel("tabs1");
37575 tabs.addTab("script", "View Script");
37576 tabs.addTab("markup", "View Markup");
37577 tabs.activate("script");
37579 // more advanced tabs, built from javascript
37580 var jtabs = new Roo.TabPanel("jtabs");
37581 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37583 // set up the UpdateManager
37584 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37585 var updater = tab2.getUpdateManager();
37586 updater.setDefaultUrl("ajax1.htm");
37587 tab2.on('activate', updater.refresh, updater, true);
37589 // Use setUrl for Ajax loading
37590 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37591 tab3.setUrl("ajax2.htm", null, true);
37594 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37597 jtabs.activate("jtabs-1");
37600 * Create a new TabPanel.
37601 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37602 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37604 Roo.bootstrap.panel.Tabs = function(config){
37606 * The container element for this TabPanel.
37607 * @type Roo.Element
37609 this.el = Roo.get(config.el);
37612 if(typeof config == "boolean"){
37613 this.tabPosition = config ? "bottom" : "top";
37615 Roo.apply(this, config);
37619 if(this.tabPosition == "bottom"){
37620 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37621 this.el.addClass("roo-tabs-bottom");
37623 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37624 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37625 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37627 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37629 if(this.tabPosition != "bottom"){
37630 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37631 * @type Roo.Element
37633 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37634 this.el.addClass("roo-tabs-top");
37638 this.bodyEl.setStyle("position", "relative");
37640 this.active = null;
37641 this.activateDelegate = this.activate.createDelegate(this);
37646 * Fires when the active tab changes
37647 * @param {Roo.TabPanel} this
37648 * @param {Roo.TabPanelItem} activePanel The new active tab
37652 * @event beforetabchange
37653 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37654 * @param {Roo.TabPanel} this
37655 * @param {Object} e Set cancel to true on this object to cancel the tab change
37656 * @param {Roo.TabPanelItem} tab The tab being changed to
37658 "beforetabchange" : true
37661 Roo.EventManager.onWindowResize(this.onResize, this);
37662 this.cpad = this.el.getPadding("lr");
37663 this.hiddenCount = 0;
37666 // toolbar on the tabbar support...
37667 if (this.toolbar) {
37668 alert("no toolbar support yet");
37669 this.toolbar = false;
37671 var tcfg = this.toolbar;
37672 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37673 this.toolbar = new Roo.Toolbar(tcfg);
37674 if (Roo.isSafari) {
37675 var tbl = tcfg.container.child('table', true);
37676 tbl.setAttribute('width', '100%');
37684 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37687 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37689 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37691 tabPosition : "top",
37693 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37695 currentTabWidth : 0,
37697 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37701 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37705 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37707 preferredTabWidth : 175,
37709 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37711 resizeTabs : false,
37713 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37715 monitorResize : true,
37717 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37722 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37723 * @param {String} id The id of the div to use <b>or create</b>
37724 * @param {String} text The text for the tab
37725 * @param {String} content (optional) Content to put in the TabPanelItem body
37726 * @param {Boolean} closable (optional) True to create a close icon on the tab
37727 * @return {Roo.TabPanelItem} The created TabPanelItem
37729 addTab : function(id, text, content, closable, tpl)
37731 var item = new Roo.bootstrap.panel.TabItem({
37735 closable : closable,
37738 this.addTabItem(item);
37740 item.setContent(content);
37746 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37747 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37748 * @return {Roo.TabPanelItem}
37750 getTab : function(id){
37751 return this.items[id];
37755 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37756 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37758 hideTab : function(id){
37759 var t = this.items[id];
37762 this.hiddenCount++;
37763 this.autoSizeTabs();
37768 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37769 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37771 unhideTab : function(id){
37772 var t = this.items[id];
37774 t.setHidden(false);
37775 this.hiddenCount--;
37776 this.autoSizeTabs();
37781 * Adds an existing {@link Roo.TabPanelItem}.
37782 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37784 addTabItem : function(item){
37785 this.items[item.id] = item;
37786 this.items.push(item);
37787 // if(this.resizeTabs){
37788 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37789 // this.autoSizeTabs();
37791 // item.autoSize();
37796 * Removes a {@link Roo.TabPanelItem}.
37797 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37799 removeTab : function(id){
37800 var items = this.items;
37801 var tab = items[id];
37802 if(!tab) { return; }
37803 var index = items.indexOf(tab);
37804 if(this.active == tab && items.length > 1){
37805 var newTab = this.getNextAvailable(index);
37810 this.stripEl.dom.removeChild(tab.pnode.dom);
37811 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37812 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37814 items.splice(index, 1);
37815 delete this.items[tab.id];
37816 tab.fireEvent("close", tab);
37817 tab.purgeListeners();
37818 this.autoSizeTabs();
37821 getNextAvailable : function(start){
37822 var items = this.items;
37824 // look for a next tab that will slide over to
37825 // replace the one being removed
37826 while(index < items.length){
37827 var item = items[++index];
37828 if(item && !item.isHidden()){
37832 // if one isn't found select the previous tab (on the left)
37835 var item = items[--index];
37836 if(item && !item.isHidden()){
37844 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37845 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37847 disableTab : function(id){
37848 var tab = this.items[id];
37849 if(tab && this.active != tab){
37855 * Enables a {@link Roo.TabPanelItem} that is disabled.
37856 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37858 enableTab : function(id){
37859 var tab = this.items[id];
37864 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37865 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37866 * @return {Roo.TabPanelItem} The TabPanelItem.
37868 activate : function(id){
37869 var tab = this.items[id];
37873 if(tab == this.active || tab.disabled){
37877 this.fireEvent("beforetabchange", this, e, tab);
37878 if(e.cancel !== true && !tab.disabled){
37880 this.active.hide();
37882 this.active = this.items[id];
37883 this.active.show();
37884 this.fireEvent("tabchange", this, this.active);
37890 * Gets the active {@link Roo.TabPanelItem}.
37891 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37893 getActiveTab : function(){
37894 return this.active;
37898 * Updates the tab body element to fit the height of the container element
37899 * for overflow scrolling
37900 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37902 syncHeight : function(targetHeight){
37903 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37904 var bm = this.bodyEl.getMargins();
37905 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37906 this.bodyEl.setHeight(newHeight);
37910 onResize : function(){
37911 if(this.monitorResize){
37912 this.autoSizeTabs();
37917 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37919 beginUpdate : function(){
37920 this.updating = true;
37924 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37926 endUpdate : function(){
37927 this.updating = false;
37928 this.autoSizeTabs();
37932 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37934 autoSizeTabs : function(){
37935 var count = this.items.length;
37936 var vcount = count - this.hiddenCount;
37937 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37940 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37941 var availWidth = Math.floor(w / vcount);
37942 var b = this.stripBody;
37943 if(b.getWidth() > w){
37944 var tabs = this.items;
37945 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37946 if(availWidth < this.minTabWidth){
37947 /*if(!this.sleft){ // incomplete scrolling code
37948 this.createScrollButtons();
37951 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37954 if(this.currentTabWidth < this.preferredTabWidth){
37955 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37961 * Returns the number of tabs in this TabPanel.
37964 getCount : function(){
37965 return this.items.length;
37969 * Resizes all the tabs to the passed width
37970 * @param {Number} The new width
37972 setTabWidth : function(width){
37973 this.currentTabWidth = width;
37974 for(var i = 0, len = this.items.length; i < len; i++) {
37975 if(!this.items[i].isHidden()) {
37976 this.items[i].setWidth(width);
37982 * Destroys this TabPanel
37983 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37985 destroy : function(removeEl){
37986 Roo.EventManager.removeResizeListener(this.onResize, this);
37987 for(var i = 0, len = this.items.length; i < len; i++){
37988 this.items[i].purgeListeners();
37990 if(removeEl === true){
37991 this.el.update("");
37996 createStrip : function(container)
37998 var strip = document.createElement("nav");
37999 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38000 container.appendChild(strip);
38004 createStripList : function(strip)
38006 // div wrapper for retard IE
38007 // returns the "tr" element.
38008 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38009 //'<div class="x-tabs-strip-wrap">'+
38010 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38011 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38012 return strip.firstChild; //.firstChild.firstChild.firstChild;
38014 createBody : function(container)
38016 var body = document.createElement("div");
38017 Roo.id(body, "tab-body");
38018 //Roo.fly(body).addClass("x-tabs-body");
38019 Roo.fly(body).addClass("tab-content");
38020 container.appendChild(body);
38023 createItemBody :function(bodyEl, id){
38024 var body = Roo.getDom(id);
38026 body = document.createElement("div");
38029 //Roo.fly(body).addClass("x-tabs-item-body");
38030 Roo.fly(body).addClass("tab-pane");
38031 bodyEl.insertBefore(body, bodyEl.firstChild);
38035 createStripElements : function(stripEl, text, closable, tpl)
38037 var td = document.createElement("li"); // was td..
38040 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38043 stripEl.appendChild(td);
38045 td.className = "x-tabs-closable";
38046 if(!this.closeTpl){
38047 this.closeTpl = new Roo.Template(
38048 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38049 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38050 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38053 var el = this.closeTpl.overwrite(td, {"text": text});
38054 var close = el.getElementsByTagName("div")[0];
38055 var inner = el.getElementsByTagName("em")[0];
38056 return {"el": el, "close": close, "inner": inner};
38059 // not sure what this is..
38060 // if(!this.tabTpl){
38061 //this.tabTpl = new Roo.Template(
38062 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38063 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38065 // this.tabTpl = new Roo.Template(
38066 // '<a href="#">' +
38067 // '<span unselectable="on"' +
38068 // (this.disableTooltips ? '' : ' title="{text}"') +
38069 // ' >{text}</span></a>'
38075 var template = tpl || this.tabTpl || false;
38079 template = new Roo.Template(
38081 '<span unselectable="on"' +
38082 (this.disableTooltips ? '' : ' title="{text}"') +
38083 ' >{text}</span></a>'
38087 switch (typeof(template)) {
38091 template = new Roo.Template(template);
38097 var el = template.overwrite(td, {"text": text});
38099 var inner = el.getElementsByTagName("span")[0];
38101 return {"el": el, "inner": inner};
38109 * @class Roo.TabPanelItem
38110 * @extends Roo.util.Observable
38111 * Represents an individual item (tab plus body) in a TabPanel.
38112 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38113 * @param {String} id The id of this TabPanelItem
38114 * @param {String} text The text for the tab of this TabPanelItem
38115 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38117 Roo.bootstrap.panel.TabItem = function(config){
38119 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38120 * @type Roo.TabPanel
38122 this.tabPanel = config.panel;
38124 * The id for this TabPanelItem
38127 this.id = config.id;
38129 this.disabled = false;
38131 this.text = config.text;
38133 this.loaded = false;
38134 this.closable = config.closable;
38137 * The body element for this TabPanelItem.
38138 * @type Roo.Element
38140 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38141 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38142 this.bodyEl.setStyle("display", "block");
38143 this.bodyEl.setStyle("zoom", "1");
38144 //this.hideAction();
38146 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38148 this.el = Roo.get(els.el);
38149 this.inner = Roo.get(els.inner, true);
38150 this.textEl = Roo.get(this.el.dom.firstChild, true);
38151 this.pnode = Roo.get(els.el.parentNode, true);
38152 // this.el.on("mousedown", this.onTabMouseDown, this);
38153 this.el.on("click", this.onTabClick, this);
38155 if(config.closable){
38156 var c = Roo.get(els.close, true);
38157 c.dom.title = this.closeText;
38158 c.addClassOnOver("close-over");
38159 c.on("click", this.closeClick, this);
38165 * Fires when this tab becomes the active tab.
38166 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38167 * @param {Roo.TabPanelItem} this
38171 * @event beforeclose
38172 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38173 * @param {Roo.TabPanelItem} this
38174 * @param {Object} e Set cancel to true on this object to cancel the close.
38176 "beforeclose": true,
38179 * Fires when this tab is closed.
38180 * @param {Roo.TabPanelItem} this
38184 * @event deactivate
38185 * Fires when this tab is no longer the active tab.
38186 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38187 * @param {Roo.TabPanelItem} this
38189 "deactivate" : true
38191 this.hidden = false;
38193 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38196 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38198 purgeListeners : function(){
38199 Roo.util.Observable.prototype.purgeListeners.call(this);
38200 this.el.removeAllListeners();
38203 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38206 this.pnode.addClass("active");
38209 this.tabPanel.stripWrap.repaint();
38211 this.fireEvent("activate", this.tabPanel, this);
38215 * Returns true if this tab is the active tab.
38216 * @return {Boolean}
38218 isActive : function(){
38219 return this.tabPanel.getActiveTab() == this;
38223 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38226 this.pnode.removeClass("active");
38228 this.fireEvent("deactivate", this.tabPanel, this);
38231 hideAction : function(){
38232 this.bodyEl.hide();
38233 this.bodyEl.setStyle("position", "absolute");
38234 this.bodyEl.setLeft("-20000px");
38235 this.bodyEl.setTop("-20000px");
38238 showAction : function(){
38239 this.bodyEl.setStyle("position", "relative");
38240 this.bodyEl.setTop("");
38241 this.bodyEl.setLeft("");
38242 this.bodyEl.show();
38246 * Set the tooltip for the tab.
38247 * @param {String} tooltip The tab's tooltip
38249 setTooltip : function(text){
38250 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38251 this.textEl.dom.qtip = text;
38252 this.textEl.dom.removeAttribute('title');
38254 this.textEl.dom.title = text;
38258 onTabClick : function(e){
38259 e.preventDefault();
38260 this.tabPanel.activate(this.id);
38263 onTabMouseDown : function(e){
38264 e.preventDefault();
38265 this.tabPanel.activate(this.id);
38268 getWidth : function(){
38269 return this.inner.getWidth();
38272 setWidth : function(width){
38273 var iwidth = width - this.pnode.getPadding("lr");
38274 this.inner.setWidth(iwidth);
38275 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38276 this.pnode.setWidth(width);
38280 * Show or hide the tab
38281 * @param {Boolean} hidden True to hide or false to show.
38283 setHidden : function(hidden){
38284 this.hidden = hidden;
38285 this.pnode.setStyle("display", hidden ? "none" : "");
38289 * Returns true if this tab is "hidden"
38290 * @return {Boolean}
38292 isHidden : function(){
38293 return this.hidden;
38297 * Returns the text for this tab
38300 getText : function(){
38304 autoSize : function(){
38305 //this.el.beginMeasure();
38306 this.textEl.setWidth(1);
38308 * #2804 [new] Tabs in Roojs
38309 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38311 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38312 //this.el.endMeasure();
38316 * Sets the text for the tab (Note: this also sets the tooltip text)
38317 * @param {String} text The tab's text and tooltip
38319 setText : function(text){
38321 this.textEl.update(text);
38322 this.setTooltip(text);
38323 //if(!this.tabPanel.resizeTabs){
38324 // this.autoSize();
38328 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38330 activate : function(){
38331 this.tabPanel.activate(this.id);
38335 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38337 disable : function(){
38338 if(this.tabPanel.active != this){
38339 this.disabled = true;
38340 this.pnode.addClass("disabled");
38345 * Enables this TabPanelItem if it was previously disabled.
38347 enable : function(){
38348 this.disabled = false;
38349 this.pnode.removeClass("disabled");
38353 * Sets the content for this TabPanelItem.
38354 * @param {String} content The content
38355 * @param {Boolean} loadScripts true to look for and load scripts
38357 setContent : function(content, loadScripts){
38358 this.bodyEl.update(content, loadScripts);
38362 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38363 * @return {Roo.UpdateManager} The UpdateManager
38365 getUpdateManager : function(){
38366 return this.bodyEl.getUpdateManager();
38370 * Set a URL to be used to load the content for this TabPanelItem.
38371 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38372 * @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)
38373 * @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)
38374 * @return {Roo.UpdateManager} The UpdateManager
38376 setUrl : function(url, params, loadOnce){
38377 if(this.refreshDelegate){
38378 this.un('activate', this.refreshDelegate);
38380 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38381 this.on("activate", this.refreshDelegate);
38382 return this.bodyEl.getUpdateManager();
38386 _handleRefresh : function(url, params, loadOnce){
38387 if(!loadOnce || !this.loaded){
38388 var updater = this.bodyEl.getUpdateManager();
38389 updater.update(url, params, this._setLoaded.createDelegate(this));
38394 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38395 * Will fail silently if the setUrl method has not been called.
38396 * This does not activate the panel, just updates its content.
38398 refresh : function(){
38399 if(this.refreshDelegate){
38400 this.loaded = false;
38401 this.refreshDelegate();
38406 _setLoaded : function(){
38407 this.loaded = true;
38411 closeClick : function(e){
38414 this.fireEvent("beforeclose", this, o);
38415 if(o.cancel !== true){
38416 this.tabPanel.removeTab(this.id);
38420 * The text displayed in the tooltip for the close icon.
38423 closeText : "Close this tab"
38426 * This script refer to:
38427 * Title: International Telephone Input
38428 * Author: Jack O'Connor
38429 * Code version: v12.1.12
38430 * Availability: https://github.com/jackocnr/intl-tel-input.git
38433 Roo.bootstrap.PhoneInputData = function() {
38436 "Afghanistan (افغانستان)",
38441 "Albania (Shqipëri)",
38446 "Algeria (الجزائر)",
38471 "Antigua and Barbuda",
38481 "Armenia (Հայաստան)",
38497 "Austria (Österreich)",
38502 "Azerbaijan (Azərbaycan)",
38512 "Bahrain (البحرين)",
38517 "Bangladesh (বাংলাদেশ)",
38527 "Belarus (Беларусь)",
38532 "Belgium (België)",
38562 "Bosnia and Herzegovina (Босна и Херцеговина)",
38577 "British Indian Ocean Territory",
38582 "British Virgin Islands",
38592 "Bulgaria (България)",
38602 "Burundi (Uburundi)",
38607 "Cambodia (កម្ពុជា)",
38612 "Cameroon (Cameroun)",
38621 ["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"]
38624 "Cape Verde (Kabu Verdi)",
38629 "Caribbean Netherlands",
38640 "Central African Republic (République centrafricaine)",
38660 "Christmas Island",
38666 "Cocos (Keeling) Islands",
38677 "Comoros (جزر القمر)",
38682 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38687 "Congo (Republic) (Congo-Brazzaville)",
38707 "Croatia (Hrvatska)",
38728 "Czech Republic (Česká republika)",
38733 "Denmark (Danmark)",
38748 "Dominican Republic (República Dominicana)",
38752 ["809", "829", "849"]
38770 "Equatorial Guinea (Guinea Ecuatorial)",
38790 "Falkland Islands (Islas Malvinas)",
38795 "Faroe Islands (Føroyar)",
38816 "French Guiana (Guyane française)",
38821 "French Polynesia (Polynésie française)",
38836 "Georgia (საქართველო)",
38841 "Germany (Deutschland)",
38861 "Greenland (Kalaallit Nunaat)",
38898 "Guinea-Bissau (Guiné Bissau)",
38923 "Hungary (Magyarország)",
38928 "Iceland (Ísland)",
38948 "Iraq (العراق)",
38964 "Israel (ישראל)",
38991 "Jordan (الأردن)",
38996 "Kazakhstan (Казахстан)",
39017 "Kuwait (الكويت)",
39022 "Kyrgyzstan (Кыргызстан)",
39032 "Latvia (Latvija)",
39037 "Lebanon (لبنان)",
39052 "Libya (ليبيا)",
39062 "Lithuania (Lietuva)",
39077 "Macedonia (FYROM) (Македонија)",
39082 "Madagascar (Madagasikara)",
39112 "Marshall Islands",
39122 "Mauritania (موريتانيا)",
39127 "Mauritius (Moris)",
39148 "Moldova (Republica Moldova)",
39158 "Mongolia (Монгол)",
39163 "Montenegro (Crna Gora)",
39173 "Morocco (المغرب)",
39179 "Mozambique (Moçambique)",
39184 "Myanmar (Burma) (မြန်မာ)",
39189 "Namibia (Namibië)",
39204 "Netherlands (Nederland)",
39209 "New Caledonia (Nouvelle-Calédonie)",
39244 "North Korea (조선 민주주의 인민 공화국)",
39249 "Northern Mariana Islands",
39265 "Pakistan (پاکستان)",
39275 "Palestine (فلسطين)",
39285 "Papua New Guinea",
39327 "Réunion (La Réunion)",
39333 "Romania (România)",
39349 "Saint Barthélemy",
39360 "Saint Kitts and Nevis",
39370 "Saint Martin (Saint-Martin (partie française))",
39376 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39381 "Saint Vincent and the Grenadines",
39396 "São Tomé and Príncipe (São Tomé e Príncipe)",
39401 "Saudi Arabia (المملكة العربية السعودية)",
39406 "Senegal (Sénégal)",
39436 "Slovakia (Slovensko)",
39441 "Slovenia (Slovenija)",
39451 "Somalia (Soomaaliya)",
39461 "South Korea (대한민국)",
39466 "South Sudan (جنوب السودان)",
39476 "Sri Lanka (ශ්රී ලංකාව)",
39481 "Sudan (السودان)",
39491 "Svalbard and Jan Mayen",
39502 "Sweden (Sverige)",
39507 "Switzerland (Schweiz)",
39512 "Syria (سوريا)",
39557 "Trinidad and Tobago",
39562 "Tunisia (تونس)",
39567 "Turkey (Türkiye)",
39577 "Turks and Caicos Islands",
39587 "U.S. Virgin Islands",
39597 "Ukraine (Україна)",
39602 "United Arab Emirates (الإمارات العربية المتحدة)",
39624 "Uzbekistan (Oʻzbekiston)",
39634 "Vatican City (Città del Vaticano)",
39645 "Vietnam (Việt Nam)",
39650 "Wallis and Futuna (Wallis-et-Futuna)",
39655 "Western Sahara (الصحراء الغربية)",
39661 "Yemen (اليمن)",
39685 * This script refer to:
39686 * Title: International Telephone Input
39687 * Author: Jack O'Connor
39688 * Code version: v12.1.12
39689 * Availability: https://github.com/jackocnr/intl-tel-input.git
39693 * @class Roo.bootstrap.PhoneInput
39694 * @extends Roo.bootstrap.TriggerField
39695 * An input with International dial-code selection
39697 * @cfg {String} defaultDialCode default '+852'
39698 * @cfg {Array} preferedCountries default []
39701 * Create a new PhoneInput.
39702 * @param {Object} config Configuration options
39705 Roo.bootstrap.PhoneInput = function(config) {
39706 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39709 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39711 listWidth: undefined,
39713 selectedClass: 'active',
39715 invalidClass : "has-warning",
39717 validClass: 'has-success',
39719 allowed: '0123456789',
39722 * @cfg {String} defaultDialCode The default dial code when initializing the input
39724 defaultDialCode: '+852',
39727 * @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
39729 preferedCountries: false,
39731 getAutoCreate : function()
39733 var data = Roo.bootstrap.PhoneInputData();
39734 var align = this.labelAlign || this.parentLabelAlign();
39737 this.allCountries = [];
39738 this.dialCodeMapping = [];
39740 for (var i = 0; i < data.length; i++) {
39742 this.allCountries[i] = {
39746 priority: c[3] || 0,
39747 areaCodes: c[4] || null
39749 this.dialCodeMapping[c[2]] = {
39752 priority: c[3] || 0,
39753 areaCodes: c[4] || null
39765 cls : 'form-control tel-input',
39766 autocomplete: 'new-password'
39769 var hiddenInput = {
39772 cls: 'hidden-tel-input'
39776 hiddenInput.name = this.name;
39779 if (this.disabled) {
39780 input.disabled = true;
39783 var flag_container = {
39800 cls: this.hasFeedback ? 'has-feedback' : '',
39806 cls: 'dial-code-holder',
39813 cls: 'roo-select2-container input-group',
39820 if (this.fieldLabel.length) {
39823 tooltip: 'This field is required'
39829 cls: 'control-label',
39835 html: this.fieldLabel
39838 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39844 if(this.indicatorpos == 'right') {
39845 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39852 if(align == 'left') {
39860 if(this.labelWidth > 12){
39861 label.style = "width: " + this.labelWidth + 'px';
39863 if(this.labelWidth < 13 && this.labelmd == 0){
39864 this.labelmd = this.labelWidth;
39866 if(this.labellg > 0){
39867 label.cls += ' col-lg-' + this.labellg;
39868 input.cls += ' col-lg-' + (12 - this.labellg);
39870 if(this.labelmd > 0){
39871 label.cls += ' col-md-' + this.labelmd;
39872 container.cls += ' col-md-' + (12 - this.labelmd);
39874 if(this.labelsm > 0){
39875 label.cls += ' col-sm-' + this.labelsm;
39876 container.cls += ' col-sm-' + (12 - this.labelsm);
39878 if(this.labelxs > 0){
39879 label.cls += ' col-xs-' + this.labelxs;
39880 container.cls += ' col-xs-' + (12 - this.labelxs);
39890 var settings = this;
39892 ['xs','sm','md','lg'].map(function(size){
39893 if (settings[size]) {
39894 cfg.cls += ' col-' + size + '-' + settings[size];
39898 this.store = new Roo.data.Store({
39899 proxy : new Roo.data.MemoryProxy({}),
39900 reader : new Roo.data.JsonReader({
39911 'name' : 'dialCode',
39915 'name' : 'priority',
39919 'name' : 'areaCodes',
39926 if(!this.preferedCountries) {
39927 this.preferedCountries = [
39934 var p = this.preferedCountries.reverse();
39937 for (var i = 0; i < p.length; i++) {
39938 for (var j = 0; j < this.allCountries.length; j++) {
39939 if(this.allCountries[j].iso2 == p[i]) {
39940 var t = this.allCountries[j];
39941 this.allCountries.splice(j,1);
39942 this.allCountries.unshift(t);
39948 this.store.proxy.data = {
39950 data: this.allCountries
39956 initEvents : function()
39959 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39961 this.indicator = this.indicatorEl();
39962 this.flag = this.flagEl();
39963 this.dialCodeHolder = this.dialCodeHolderEl();
39965 this.trigger = this.el.select('div.flag-box',true).first();
39966 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39971 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39972 _this.list.setWidth(lw);
39975 this.list.on('mouseover', this.onViewOver, this);
39976 this.list.on('mousemove', this.onViewMove, this);
39977 this.inputEl().on("keyup", this.onKeyUp, this);
39979 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39981 this.view = new Roo.View(this.list, this.tpl, {
39982 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39985 this.view.on('click', this.onViewClick, this);
39986 this.setValue(this.defaultDialCode);
39989 onTriggerClick : function(e)
39991 Roo.log('trigger click');
39996 if(this.isExpanded()){
39998 this.hasFocus = false;
40000 this.store.load({});
40001 this.hasFocus = true;
40006 isExpanded : function()
40008 return this.list.isVisible();
40011 collapse : function()
40013 if(!this.isExpanded()){
40017 Roo.get(document).un('mousedown', this.collapseIf, this);
40018 Roo.get(document).un('mousewheel', this.collapseIf, this);
40019 this.fireEvent('collapse', this);
40023 expand : function()
40027 if(this.isExpanded() || !this.hasFocus){
40031 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40032 this.list.setWidth(lw);
40035 this.restrictHeight();
40037 Roo.get(document).on('mousedown', this.collapseIf, this);
40038 Roo.get(document).on('mousewheel', this.collapseIf, this);
40040 this.fireEvent('expand', this);
40043 restrictHeight : function()
40045 this.list.alignTo(this.inputEl(), this.listAlign);
40046 this.list.alignTo(this.inputEl(), this.listAlign);
40049 onViewOver : function(e, t)
40051 if(this.inKeyMode){
40054 var item = this.view.findItemFromChild(t);
40057 var index = this.view.indexOf(item);
40058 this.select(index, false);
40063 onViewClick : function(view, doFocus, el, e)
40065 var index = this.view.getSelectedIndexes()[0];
40067 var r = this.store.getAt(index);
40070 this.onSelect(r, index);
40072 if(doFocus !== false && !this.blockFocus){
40073 this.inputEl().focus();
40077 onViewMove : function(e, t)
40079 this.inKeyMode = false;
40082 select : function(index, scrollIntoView)
40084 this.selectedIndex = index;
40085 this.view.select(index);
40086 if(scrollIntoView !== false){
40087 var el = this.view.getNode(index);
40089 this.list.scrollChildIntoView(el, false);
40094 createList : function()
40096 this.list = Roo.get(document.body).createChild({
40098 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40099 style: 'display:none'
40102 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40105 collapseIf : function(e)
40107 var in_combo = e.within(this.el);
40108 var in_list = e.within(this.list);
40109 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40111 if (in_combo || in_list || is_list) {
40117 onSelect : function(record, index)
40119 if(this.fireEvent('beforeselect', this, record, index) !== false){
40121 this.setFlagClass(record.data.iso2);
40122 this.setDialCode(record.data.dialCode);
40123 this.hasFocus = false;
40125 this.fireEvent('select', this, record, index);
40129 flagEl : function()
40131 var flag = this.el.select('div.flag',true).first();
40138 dialCodeHolderEl : function()
40140 var d = this.el.select('input.dial-code-holder',true).first();
40147 setDialCode : function(v)
40149 this.dialCodeHolder.dom.value = '+'+v;
40152 setFlagClass : function(n)
40154 this.flag.dom.className = 'flag '+n;
40157 getValue : function()
40159 var v = this.inputEl().getValue();
40160 if(this.dialCodeHolder) {
40161 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40166 setValue : function(v)
40168 var d = this.getDialCode(v);
40170 //invalid dial code
40171 if(v.length == 0 || !d || d.length == 0) {
40173 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40174 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40180 this.setFlagClass(this.dialCodeMapping[d].iso2);
40181 this.setDialCode(d);
40182 this.inputEl().dom.value = v.replace('+'+d,'');
40183 this.hiddenEl().dom.value = this.getValue();
40188 getDialCode : function(v)
40192 if (v.length == 0) {
40193 return this.dialCodeHolder.dom.value;
40197 if (v.charAt(0) != "+") {
40200 var numericChars = "";
40201 for (var i = 1; i < v.length; i++) {
40202 var c = v.charAt(i);
40205 if (this.dialCodeMapping[numericChars]) {
40206 dialCode = v.substr(1, i);
40208 if (numericChars.length == 4) {
40218 this.setValue(this.defaultDialCode);
40222 hiddenEl : function()
40224 return this.el.select('input.hidden-tel-input',true).first();
40227 onKeyUp : function(e){
40229 var k = e.getKey();
40230 var c = e.getCharCode();
40233 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40234 this.allowed.indexOf(String.fromCharCode(c)) === -1
40239 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40242 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40246 this.setValue(this.getValue());
40251 * @class Roo.bootstrap.MoneyField
40252 * @extends Roo.bootstrap.ComboBox
40253 * Bootstrap MoneyField class
40256 * Create a new MoneyField.
40257 * @param {Object} config Configuration options
40260 Roo.bootstrap.MoneyField = function(config) {
40262 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40266 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40269 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40271 allowDecimals : true,
40273 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40275 decimalSeparator : ".",
40277 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40279 decimalPrecision : 0,
40281 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40283 allowNegative : true,
40285 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40289 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40291 minValue : Number.NEGATIVE_INFINITY,
40293 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40295 maxValue : Number.MAX_VALUE,
40297 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40299 minText : "The minimum value for this field is {0}",
40301 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40303 maxText : "The maximum value for this field is {0}",
40305 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40306 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40308 nanText : "{0} is not a valid number",
40310 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40314 * @cfg {String} defaults currency of the MoneyField
40315 * value should be in lkey
40317 defaultCurrency : false,
40319 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40321 thousandsDelimiter : false,
40331 getAutoCreate : function()
40333 var align = this.labelAlign || this.parentLabelAlign();
40345 cls : 'form-control roo-money-amount-input',
40346 autocomplete: 'new-password'
40349 var hiddenInput = {
40353 cls: 'hidden-number-input'
40357 hiddenInput.name = this.name;
40360 if (this.disabled) {
40361 input.disabled = true;
40364 var clg = 12 - this.inputlg;
40365 var cmd = 12 - this.inputmd;
40366 var csm = 12 - this.inputsm;
40367 var cxs = 12 - this.inputxs;
40371 cls : 'row roo-money-field',
40375 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40379 cls: 'roo-select2-container input-group',
40383 cls : 'form-control roo-money-currency-input',
40384 autocomplete: 'new-password',
40386 name : this.currencyName
40390 cls : 'input-group-addon',
40404 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40408 cls: this.hasFeedback ? 'has-feedback' : '',
40419 if (this.fieldLabel.length) {
40422 tooltip: 'This field is required'
40428 cls: 'control-label',
40434 html: this.fieldLabel
40437 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40443 if(this.indicatorpos == 'right') {
40444 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40451 if(align == 'left') {
40459 if(this.labelWidth > 12){
40460 label.style = "width: " + this.labelWidth + 'px';
40462 if(this.labelWidth < 13 && this.labelmd == 0){
40463 this.labelmd = this.labelWidth;
40465 if(this.labellg > 0){
40466 label.cls += ' col-lg-' + this.labellg;
40467 input.cls += ' col-lg-' + (12 - this.labellg);
40469 if(this.labelmd > 0){
40470 label.cls += ' col-md-' + this.labelmd;
40471 container.cls += ' col-md-' + (12 - this.labelmd);
40473 if(this.labelsm > 0){
40474 label.cls += ' col-sm-' + this.labelsm;
40475 container.cls += ' col-sm-' + (12 - this.labelsm);
40477 if(this.labelxs > 0){
40478 label.cls += ' col-xs-' + this.labelxs;
40479 container.cls += ' col-xs-' + (12 - this.labelxs);
40490 var settings = this;
40492 ['xs','sm','md','lg'].map(function(size){
40493 if (settings[size]) {
40494 cfg.cls += ' col-' + size + '-' + settings[size];
40501 initEvents : function()
40503 this.indicator = this.indicatorEl();
40505 this.initCurrencyEvent();
40507 this.initNumberEvent();
40510 initCurrencyEvent : function()
40513 throw "can not find store for combo";
40516 this.store = Roo.factory(this.store, Roo.data);
40517 this.store.parent = this;
40521 this.triggerEl = this.el.select('.input-group-addon', true).first();
40523 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40528 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40529 _this.list.setWidth(lw);
40532 this.list.on('mouseover', this.onViewOver, this);
40533 this.list.on('mousemove', this.onViewMove, this);
40534 this.list.on('scroll', this.onViewScroll, this);
40537 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40540 this.view = new Roo.View(this.list, this.tpl, {
40541 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40544 this.view.on('click', this.onViewClick, this);
40546 this.store.on('beforeload', this.onBeforeLoad, this);
40547 this.store.on('load', this.onLoad, this);
40548 this.store.on('loadexception', this.onLoadException, this);
40550 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40551 "up" : function(e){
40552 this.inKeyMode = true;
40556 "down" : function(e){
40557 if(!this.isExpanded()){
40558 this.onTriggerClick();
40560 this.inKeyMode = true;
40565 "enter" : function(e){
40568 if(this.fireEvent("specialkey", this, e)){
40569 this.onViewClick(false);
40575 "esc" : function(e){
40579 "tab" : function(e){
40582 if(this.fireEvent("specialkey", this, e)){
40583 this.onViewClick(false);
40591 doRelay : function(foo, bar, hname){
40592 if(hname == 'down' || this.scope.isExpanded()){
40593 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40601 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40605 initNumberEvent : function(e)
40607 this.inputEl().on("keydown" , this.fireKey, this);
40608 this.inputEl().on("focus", this.onFocus, this);
40609 this.inputEl().on("blur", this.onBlur, this);
40611 this.inputEl().relayEvent('keyup', this);
40613 if(this.indicator){
40614 this.indicator.addClass('invisible');
40617 this.originalValue = this.getValue();
40619 if(this.validationEvent == 'keyup'){
40620 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40621 this.inputEl().on('keyup', this.filterValidation, this);
40623 else if(this.validationEvent !== false){
40624 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40627 if(this.selectOnFocus){
40628 this.on("focus", this.preFocus, this);
40631 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40632 this.inputEl().on("keypress", this.filterKeys, this);
40634 this.inputEl().relayEvent('keypress', this);
40637 var allowed = "0123456789";
40639 if(this.allowDecimals){
40640 allowed += this.decimalSeparator;
40643 if(this.allowNegative){
40647 if(this.thousandsDelimiter) {
40651 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40653 var keyPress = function(e){
40655 var k = e.getKey();
40657 var c = e.getCharCode();
40660 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40661 allowed.indexOf(String.fromCharCode(c)) === -1
40667 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40671 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40676 this.inputEl().on("keypress", keyPress, this);
40680 onTriggerClick : function(e)
40687 this.loadNext = false;
40689 if(this.isExpanded()){
40694 this.hasFocus = true;
40696 if(this.triggerAction == 'all') {
40697 this.doQuery(this.allQuery, true);
40701 this.doQuery(this.getRawValue());
40704 getCurrency : function()
40706 var v = this.currencyEl().getValue();
40711 restrictHeight : function()
40713 this.list.alignTo(this.currencyEl(), this.listAlign);
40714 this.list.alignTo(this.currencyEl(), this.listAlign);
40717 onViewClick : function(view, doFocus, el, e)
40719 var index = this.view.getSelectedIndexes()[0];
40721 var r = this.store.getAt(index);
40724 this.onSelect(r, index);
40728 onSelect : function(record, index){
40730 if(this.fireEvent('beforeselect', this, record, index) !== false){
40732 this.setFromCurrencyData(index > -1 ? record.data : false);
40736 this.fireEvent('select', this, record, index);
40740 setFromCurrencyData : function(o)
40744 this.lastCurrency = o;
40746 if (this.currencyField) {
40747 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40749 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40752 this.lastSelectionText = currency;
40754 //setting default currency
40755 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40756 this.setCurrency(this.defaultCurrency);
40760 this.setCurrency(currency);
40763 setFromData : function(o)
40767 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40769 this.setFromCurrencyData(c);
40774 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40776 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40779 this.setValue(value);
40783 setCurrency : function(v)
40785 this.currencyValue = v;
40788 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40793 setValue : function(v)
40795 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40801 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40803 this.inputEl().dom.value = (v == '') ? '' :
40804 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40806 if(!this.allowZero && v === '0') {
40807 this.hiddenEl().dom.value = '';
40808 this.inputEl().dom.value = '';
40815 getRawValue : function()
40817 var v = this.inputEl().getValue();
40822 getValue : function()
40824 return this.fixPrecision(this.parseValue(this.getRawValue()));
40827 parseValue : function(value)
40829 if(this.thousandsDelimiter) {
40831 r = new RegExp(",", "g");
40832 value = value.replace(r, "");
40835 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40836 return isNaN(value) ? '' : value;
40840 fixPrecision : function(value)
40842 if(this.thousandsDelimiter) {
40844 r = new RegExp(",", "g");
40845 value = value.replace(r, "");
40848 var nan = isNaN(value);
40850 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40851 return nan ? '' : value;
40853 return parseFloat(value).toFixed(this.decimalPrecision);
40856 decimalPrecisionFcn : function(v)
40858 return Math.floor(v);
40861 validateValue : function(value)
40863 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40867 var num = this.parseValue(value);
40870 this.markInvalid(String.format(this.nanText, value));
40874 if(num < this.minValue){
40875 this.markInvalid(String.format(this.minText, this.minValue));
40879 if(num > this.maxValue){
40880 this.markInvalid(String.format(this.maxText, this.maxValue));
40887 validate : function()
40889 if(this.disabled || this.allowBlank){
40894 var currency = this.getCurrency();
40896 if(this.validateValue(this.getRawValue()) && currency.length){
40901 this.markInvalid();
40905 getName: function()
40910 beforeBlur : function()
40916 var v = this.parseValue(this.getRawValue());
40923 onBlur : function()
40927 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40928 //this.el.removeClass(this.focusClass);
40931 this.hasFocus = false;
40933 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40937 var v = this.getValue();
40939 if(String(v) !== String(this.startValue)){
40940 this.fireEvent('change', this, v, this.startValue);
40943 this.fireEvent("blur", this);
40946 inputEl : function()
40948 return this.el.select('.roo-money-amount-input', true).first();
40951 currencyEl : function()
40953 return this.el.select('.roo-money-currency-input', true).first();
40956 hiddenEl : function()
40958 return this.el.select('input.hidden-number-input',true).first();