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;
921 setWeight : function(str)
923 this.el.removeClass(this.weightClass);
924 this.el.addClass('btn-' + str);
938 * @class Roo.bootstrap.Column
939 * @extends Roo.bootstrap.Component
940 * Bootstrap Column class
941 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
951 * @cfg {Boolean} hidden (true|false) hide the element
952 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953 * @cfg {String} fa (ban|check|...) font awesome icon
954 * @cfg {Number} fasize (1|2|....) font awsome size
956 * @cfg {String} icon (info-sign|check|...) glyphicon name
958 * @cfg {String} html content of column.
961 * Create a new Column
962 * @param {Object} config The config object
965 Roo.bootstrap.Column = function(config){
966 Roo.bootstrap.Column.superclass.constructor.call(this, config);
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
987 getAutoCreate : function(){
988 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
996 ['xs','sm','md','lg'].map(function(size){
997 //Roo.log( size + ':' + settings[size]);
999 if (settings[size+'off'] !== false) {
1000 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1003 if (settings[size] === false) {
1007 if (!settings[size]) { // 0 = hidden
1008 cfg.cls += ' hidden-' + size;
1011 cfg.cls += ' col-' + size + '-' + settings[size];
1016 cfg.cls += ' hidden';
1019 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020 cfg.cls +=' alert alert-' + this.alert;
1024 if (this.html.length) {
1025 cfg.html = this.html;
1029 if (this.fasize > 1) {
1030 fasize = ' fa-' + this.fasize + 'x';
1032 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1056 * @class Roo.bootstrap.Container
1057 * @extends Roo.bootstrap.Component
1058 * Bootstrap Container class
1059 * @cfg {Boolean} jumbotron is it a jumbotron element
1060 * @cfg {String} html content of element
1061 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1063 * @cfg {String} header content of header (for panel)
1064 * @cfg {String} footer content of footer (for panel)
1065 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066 * @cfg {String} tag (header|aside|section) type of HTML tag.
1067 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068 * @cfg {String} fa font awesome icon
1069 * @cfg {String} icon (info-sign|check|...) glyphicon name
1070 * @cfg {Boolean} hidden (true|false) hide the element
1071 * @cfg {Boolean} expandable (true|false) default false
1072 * @cfg {Boolean} expanded (true|false) default true
1073 * @cfg {String} rheader contet on the right of header
1074 * @cfg {Boolean} clickable (true|false) default false
1078 * Create a new Container
1079 * @param {Object} config The config object
1082 Roo.bootstrap.Container = function(config){
1083 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1089 * After the panel has been expand
1091 * @param {Roo.bootstrap.Container} this
1096 * After the panel has been collapsed
1098 * @param {Roo.bootstrap.Container} this
1103 * When a element is chick
1104 * @param {Roo.bootstrap.Container} this
1105 * @param {Roo.EventObject} e
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1129 getChildContainer : function() {
1135 if (this.panel.length) {
1136 return this.el.select('.panel-body',true).first();
1143 getAutoCreate : function(){
1146 tag : this.tag || 'div',
1150 if (this.jumbotron) {
1151 cfg.cls = 'jumbotron';
1156 // - this is applied by the parent..
1158 // cfg.cls = this.cls + '';
1161 if (this.sticky.length) {
1163 var bd = Roo.get(document.body);
1164 if (!bd.hasClass('bootstrap-sticky')) {
1165 bd.addClass('bootstrap-sticky');
1166 Roo.select('html',true).setStyle('height', '100%');
1169 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1173 if (this.well.length) {
1174 switch (this.well) {
1177 cfg.cls +=' well well-' +this.well;
1186 cfg.cls += ' hidden';
1190 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191 cfg.cls +=' alert alert-' + this.alert;
1196 if (this.panel.length) {
1197 cfg.cls += ' panel panel-' + this.panel;
1199 if (this.header.length) {
1203 if(this.expandable){
1205 cfg.cls = cfg.cls + ' expandable';
1209 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1217 cls : 'panel-title',
1218 html : (this.expandable ? ' ' : '') + this.header
1222 cls: 'panel-header-right',
1228 cls : 'panel-heading',
1229 style : this.expandable ? 'cursor: pointer' : '',
1237 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242 if (this.footer.length) {
1244 cls : 'panel-footer',
1253 body.html = this.html || cfg.html;
1254 // prefix with the icons..
1256 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1259 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265 cfg.cls = 'container';
1271 initEvents: function()
1273 if(this.expandable){
1274 var headerEl = this.headerEl();
1277 headerEl.on('click', this.onToggleClick, this);
1282 this.el.on('click', this.onClick, this);
1287 onToggleClick : function()
1289 var headerEl = this.headerEl();
1305 if(this.fireEvent('expand', this)) {
1307 this.expanded = true;
1309 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1311 this.el.select('.panel-body',true).first().removeClass('hide');
1313 var toggleEl = this.toggleEl();
1319 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324 collapse : function()
1326 if(this.fireEvent('collapse', this)) {
1328 this.expanded = false;
1330 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331 this.el.select('.panel-body',true).first().addClass('hide');
1333 var toggleEl = this.toggleEl();
1339 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1343 toggleEl : function()
1345 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1349 return this.el.select('.panel-heading .fa',true).first();
1352 headerEl : function()
1354 if(!this.el || !this.panel.length || !this.header.length){
1358 return this.el.select('.panel-heading',true).first()
1363 if(!this.el || !this.panel.length){
1367 return this.el.select('.panel-body',true).first()
1370 titleEl : function()
1372 if(!this.el || !this.panel.length || !this.header.length){
1376 return this.el.select('.panel-title',true).first();
1379 setTitle : function(v)
1381 var titleEl = this.titleEl();
1387 titleEl.dom.innerHTML = v;
1390 getTitle : function()
1393 var titleEl = this.titleEl();
1399 return titleEl.dom.innerHTML;
1402 setRightTitle : function(v)
1404 var t = this.el.select('.panel-header-right',true).first();
1410 t.dom.innerHTML = v;
1413 onClick : function(e)
1417 this.fireEvent('click', this, e);
1430 * @class Roo.bootstrap.Img
1431 * @extends Roo.bootstrap.Component
1432 * Bootstrap Img class
1433 * @cfg {Boolean} imgResponsive false | true
1434 * @cfg {String} border rounded | circle | thumbnail
1435 * @cfg {String} src image source
1436 * @cfg {String} alt image alternative text
1437 * @cfg {String} href a tag href
1438 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439 * @cfg {String} xsUrl xs image source
1440 * @cfg {String} smUrl sm image source
1441 * @cfg {String} mdUrl md image source
1442 * @cfg {String} lgUrl lg image source
1445 * Create a new Input
1446 * @param {Object} config The config object
1449 Roo.bootstrap.Img = function(config){
1450 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1456 * The img click event for the img.
1457 * @param {Roo.EventObject} e
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1465 imgResponsive: true,
1475 getAutoCreate : function()
1477 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478 return this.createSingleImg();
1483 cls: 'roo-image-responsive-group',
1488 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1490 if(!_this[size + 'Url']){
1496 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497 html: _this.html || cfg.html,
1498 src: _this[size + 'Url']
1501 img.cls += ' roo-image-responsive-' + size;
1503 var s = ['xs', 'sm', 'md', 'lg'];
1505 s.splice(s.indexOf(size), 1);
1507 Roo.each(s, function(ss){
1508 img.cls += ' hidden-' + ss;
1511 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512 cfg.cls += ' img-' + _this.border;
1516 cfg.alt = _this.alt;
1529 a.target = _this.target;
1533 cfg.cn.push((_this.href) ? a : img);
1540 createSingleImg : function()
1544 cls: (this.imgResponsive) ? 'img-responsive' : '',
1546 src : 'about:blank' // just incase src get's set to undefined?!?
1549 cfg.html = this.html || cfg.html;
1551 cfg.src = this.src || cfg.src;
1553 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554 cfg.cls += ' img-' + this.border;
1571 a.target = this.target;
1576 return (this.href) ? a : cfg;
1579 initEvents: function()
1582 this.el.on('click', this.onClick, this);
1587 onClick : function(e)
1589 Roo.log('img onclick');
1590 this.fireEvent('click', this, e);
1593 * Sets the url of the image - used to update it
1594 * @param {String} url the url of the image
1597 setSrc : function(url)
1601 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602 this.el.dom.src = url;
1606 this.el.select('img', true).first().dom.src = url;
1622 * @class Roo.bootstrap.Link
1623 * @extends Roo.bootstrap.Component
1624 * Bootstrap Link Class
1625 * @cfg {String} alt image alternative text
1626 * @cfg {String} href a tag href
1627 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628 * @cfg {String} html the content of the link.
1629 * @cfg {String} anchor name for the anchor link
1630 * @cfg {String} fa - favicon
1632 * @cfg {Boolean} preventDefault (true | false) default false
1636 * Create a new Input
1637 * @param {Object} config The config object
1640 Roo.bootstrap.Link = function(config){
1641 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1647 * The img click event for the img.
1648 * @param {Roo.EventObject} e
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1658 preventDefault: false,
1664 getAutoCreate : function()
1666 var html = this.html || '';
1668 if (this.fa !== false) {
1669 html = '<i class="fa fa-' + this.fa + '"></i>';
1674 // anchor's do not require html/href...
1675 if (this.anchor === false) {
1677 cfg.href = this.href || '#';
1679 cfg.name = this.anchor;
1680 if (this.html !== false || this.fa !== false) {
1683 if (this.href !== false) {
1684 cfg.href = this.href;
1688 if(this.alt !== false){
1693 if(this.target !== false) {
1694 cfg.target = this.target;
1700 initEvents: function() {
1702 if(!this.href || this.preventDefault){
1703 this.el.on('click', this.onClick, this);
1707 onClick : function(e)
1709 if(this.preventDefault){
1712 //Roo.log('img onclick');
1713 this.fireEvent('click', this, e);
1726 * @class Roo.bootstrap.Header
1727 * @extends Roo.bootstrap.Component
1728 * Bootstrap Header class
1729 * @cfg {String} html content of header
1730 * @cfg {Number} level (1|2|3|4|5|6) default 1
1733 * Create a new Header
1734 * @param {Object} config The config object
1738 Roo.bootstrap.Header = function(config){
1739 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1750 getAutoCreate : function(){
1755 tag: 'h' + (1 *this.level),
1756 html: this.html || ''
1768 * Ext JS Library 1.1.1
1769 * Copyright(c) 2006-2007, Ext JS, LLC.
1771 * Originally Released Under LGPL - original licence link has changed is not relivant.
1774 * <script type="text/javascript">
1778 * @class Roo.bootstrap.MenuMgr
1779 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1782 Roo.bootstrap.MenuMgr = function(){
1783 var menus, active, groups = {}, attached = false, lastShow = new Date();
1785 // private - called when first menu is created
1788 active = new Roo.util.MixedCollection();
1789 Roo.get(document).addKeyListener(27, function(){
1790 if(active.length > 0){
1798 if(active && active.length > 0){
1799 var c = active.clone();
1809 if(active.length < 1){
1810 Roo.get(document).un("mouseup", onMouseDown);
1818 var last = active.last();
1819 lastShow = new Date();
1822 Roo.get(document).on("mouseup", onMouseDown);
1827 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828 m.parentMenu.activeChild = m;
1829 }else if(last && last.isVisible()){
1830 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835 function onBeforeHide(m){
1837 m.activeChild.hide();
1839 if(m.autoHideTimer){
1840 clearTimeout(m.autoHideTimer);
1841 delete m.autoHideTimer;
1846 function onBeforeShow(m){
1847 var pm = m.parentMenu;
1848 if(!pm && !m.allowOtherMenus){
1850 }else if(pm && pm.activeChild && active != m){
1851 pm.activeChild.hide();
1855 // private this should really trigger on mouseup..
1856 function onMouseDown(e){
1857 Roo.log("on Mouse Up");
1859 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860 Roo.log("MenuManager hideAll");
1869 function onBeforeCheck(mi, state){
1871 var g = groups[mi.group];
1872 for(var i = 0, l = g.length; i < l; i++){
1874 g[i].setChecked(false);
1883 * Hides all menus that are currently visible
1885 hideAll : function(){
1890 register : function(menu){
1894 menus[menu.id] = menu;
1895 menu.on("beforehide", onBeforeHide);
1896 menu.on("hide", onHide);
1897 menu.on("beforeshow", onBeforeShow);
1898 menu.on("show", onShow);
1900 if(g && menu.events["checkchange"]){
1904 groups[g].push(menu);
1905 menu.on("checkchange", onCheck);
1910 * Returns a {@link Roo.menu.Menu} object
1911 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912 * be used to generate and return a new Menu instance.
1914 get : function(menu){
1915 if(typeof menu == "string"){ // menu id
1917 }else if(menu.events){ // menu instance
1920 /*else if(typeof menu.length == 'number'){ // array of menu items?
1921 return new Roo.bootstrap.Menu({items:menu});
1922 }else{ // otherwise, must be a config
1923 return new Roo.bootstrap.Menu(menu);
1930 unregister : function(menu){
1931 delete menus[menu.id];
1932 menu.un("beforehide", onBeforeHide);
1933 menu.un("hide", onHide);
1934 menu.un("beforeshow", onBeforeShow);
1935 menu.un("show", onShow);
1937 if(g && menu.events["checkchange"]){
1938 groups[g].remove(menu);
1939 menu.un("checkchange", onCheck);
1944 registerCheckable : function(menuItem){
1945 var g = menuItem.group;
1950 groups[g].push(menuItem);
1951 menuItem.on("beforecheckchange", onBeforeCheck);
1956 unregisterCheckable : function(menuItem){
1957 var g = menuItem.group;
1959 groups[g].remove(menuItem);
1960 menuItem.un("beforecheckchange", onBeforeCheck);
1972 * @class Roo.bootstrap.Menu
1973 * @extends Roo.bootstrap.Component
1974 * Bootstrap Menu class - container for MenuItems
1975 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976 * @cfg {bool} hidden if the menu should be hidden when rendered.
1977 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1978 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1982 * @param {Object} config The config object
1986 Roo.bootstrap.Menu = function(config){
1987 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988 if (this.registerMenu && this.type != 'treeview') {
1989 Roo.bootstrap.MenuMgr.register(this);
1994 * Fires before this menu is displayed
1995 * @param {Roo.menu.Menu} this
2000 * Fires before this menu is hidden
2001 * @param {Roo.menu.Menu} this
2006 * Fires after this menu is displayed
2007 * @param {Roo.menu.Menu} this
2012 * Fires after this menu is hidden
2013 * @param {Roo.menu.Menu} this
2018 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019 * @param {Roo.menu.Menu} this
2020 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021 * @param {Roo.EventObject} e
2026 * Fires when the mouse is hovering over this menu
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.EventObject} e
2029 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034 * Fires when the mouse exits 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 a menu item contained in this menu is clicked
2043 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044 * @param {Roo.EventObject} e
2048 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2055 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2058 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2060 registerMenu : true,
2062 menuItems :false, // stores the menu items..
2072 getChildContainer : function() {
2076 getAutoCreate : function(){
2078 //if (['right'].indexOf(this.align)!==-1) {
2079 // cfg.cn[1].cls += ' pull-right'
2085 cls : 'dropdown-menu' ,
2086 style : 'z-index:1000'
2090 if (this.type === 'submenu') {
2091 cfg.cls = 'submenu active';
2093 if (this.type === 'treeview') {
2094 cfg.cls = 'treeview-menu';
2099 initEvents : function() {
2101 // Roo.log("ADD event");
2102 // Roo.log(this.triggerEl.dom);
2104 this.triggerEl.on('click', this.onTriggerClick, this);
2106 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2108 this.triggerEl.addClass('dropdown-toggle');
2111 this.el.on('touchstart' , this.onTouch, this);
2113 this.el.on('click' , this.onClick, this);
2115 this.el.on("mouseover", this.onMouseOver, this);
2116 this.el.on("mouseout", this.onMouseOut, this);
2120 findTargetItem : function(e)
2122 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2126 //Roo.log(t); Roo.log(t.id);
2128 //Roo.log(this.menuitems);
2129 return this.menuitems.get(t.id);
2131 //return this.items.get(t.menuItemId);
2137 onTouch : function(e)
2139 Roo.log("menu.onTouch");
2140 //e.stopEvent(); this make the user popdown broken
2144 onClick : function(e)
2146 Roo.log("menu.onClick");
2148 var t = this.findTargetItem(e);
2149 if(!t || t.isContainer){
2154 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2155 if(t == this.activeItem && t.shouldDeactivate(e)){
2156 this.activeItem.deactivate();
2157 delete this.activeItem;
2161 this.setActiveItem(t, true);
2169 Roo.log('pass click event');
2173 this.fireEvent("click", this, t, e);
2177 if(!t.href.length || t.href == '#'){
2178 (function() { _this.hide(); }).defer(100);
2183 onMouseOver : function(e){
2184 var t = this.findTargetItem(e);
2187 // if(t.canActivate && !t.disabled){
2188 // this.setActiveItem(t, true);
2192 this.fireEvent("mouseover", this, e, t);
2194 isVisible : function(){
2195 return !this.hidden;
2197 onMouseOut : function(e){
2198 var t = this.findTargetItem(e);
2201 // if(t == this.activeItem && t.shouldDeactivate(e)){
2202 // this.activeItem.deactivate();
2203 // delete this.activeItem;
2206 this.fireEvent("mouseout", this, e, t);
2211 * Displays this menu relative to another element
2212 * @param {String/HTMLElement/Roo.Element} element The element to align to
2213 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214 * the element (defaults to this.defaultAlign)
2215 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2217 show : function(el, pos, parentMenu){
2218 this.parentMenu = parentMenu;
2222 this.fireEvent("beforeshow", this);
2223 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2226 * Displays this menu at a specific xy position
2227 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2230 showAt : function(xy, parentMenu, /* private: */_e){
2231 this.parentMenu = parentMenu;
2236 this.fireEvent("beforeshow", this);
2237 //xy = this.el.adjustForConstraints(xy);
2241 this.hideMenuItems();
2242 this.hidden = false;
2243 this.triggerEl.addClass('open');
2245 // reassign x when hitting right
2246 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2250 // reassign y when hitting bottom
2251 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2255 // but the list may align on trigger left or trigger top... should it be a properity?
2257 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2262 this.fireEvent("show", this);
2268 this.doFocus.defer(50, this);
2272 doFocus : function(){
2274 this.focusEl.focus();
2279 * Hides this menu and optionally all parent menus
2280 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2282 hide : function(deep)
2285 this.hideMenuItems();
2286 if(this.el && this.isVisible()){
2287 this.fireEvent("beforehide", this);
2288 if(this.activeItem){
2289 this.activeItem.deactivate();
2290 this.activeItem = null;
2292 this.triggerEl.removeClass('open');;
2294 this.fireEvent("hide", this);
2296 if(deep === true && this.parentMenu){
2297 this.parentMenu.hide(true);
2301 onTriggerClick : function(e)
2303 Roo.log('trigger click');
2305 var target = e.getTarget();
2307 Roo.log(target.nodeName.toLowerCase());
2309 if(target.nodeName.toLowerCase() === 'i'){
2315 onTriggerPress : function(e)
2317 Roo.log('trigger press');
2318 //Roo.log(e.getTarget());
2319 // Roo.log(this.triggerEl.dom);
2321 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322 var pel = Roo.get(e.getTarget());
2323 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324 Roo.log('is treeview or dropdown?');
2328 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2332 if (this.isVisible()) {
2337 this.show(this.triggerEl, false, false);
2340 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2347 hideMenuItems : function()
2349 Roo.log("hide Menu Items");
2353 //$(backdrop).remove()
2354 this.el.select('.open',true).each(function(aa) {
2356 aa.removeClass('open');
2357 //var parent = getParent($(this))
2358 //var relatedTarget = { relatedTarget: this }
2360 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361 //if (e.isDefaultPrevented()) return
2362 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2365 addxtypeChild : function (tree, cntr) {
2366 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2368 this.menuitems.add(comp);
2380 this.getEl().dom.innerHTML = '';
2381 this.menuitems.clear();
2395 * @class Roo.bootstrap.MenuItem
2396 * @extends Roo.bootstrap.Component
2397 * Bootstrap MenuItem class
2398 * @cfg {String} html the menu label
2399 * @cfg {String} href the link
2400 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402 * @cfg {Boolean} active used on sidebars to highlight active itesm
2403 * @cfg {String} fa favicon to show on left of menu item.
2404 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2408 * Create a new MenuItem
2409 * @param {Object} config The config object
2413 Roo.bootstrap.MenuItem = function(config){
2414 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2419 * The raw click event for the entire grid.
2420 * @param {Roo.bootstrap.MenuItem} this
2421 * @param {Roo.EventObject} e
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2431 preventDefault: false,
2432 isContainer : false,
2436 getAutoCreate : function(){
2438 if(this.isContainer){
2441 cls: 'dropdown-menu-item'
2455 if (this.fa !== false) {
2458 cls : 'fa fa-' + this.fa
2467 cls: 'dropdown-menu-item',
2470 if (this.parent().type == 'treeview') {
2471 cfg.cls = 'treeview-menu';
2474 cfg.cls += ' active';
2479 anc.href = this.href || cfg.cn[0].href ;
2480 ctag.html = this.html || cfg.cn[0].html ;
2484 initEvents: function()
2486 if (this.parent().type == 'treeview') {
2487 this.el.select('a').on('click', this.onClick, this);
2491 this.menu.parentType = this.xtype;
2492 this.menu.triggerEl = this.el;
2493 this.menu = this.addxtype(Roo.apply({}, this.menu));
2497 onClick : function(e)
2499 Roo.log('item on click ');
2501 if(this.preventDefault){
2504 //this.parent().hideMenuItems();
2506 this.fireEvent('click', this, e);
2525 * @class Roo.bootstrap.MenuSeparator
2526 * @extends Roo.bootstrap.Component
2527 * Bootstrap MenuSeparator class
2530 * Create a new MenuItem
2531 * @param {Object} config The config object
2535 Roo.bootstrap.MenuSeparator = function(config){
2536 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2541 getAutoCreate : function(){
2560 * @class Roo.bootstrap.Modal
2561 * @extends Roo.bootstrap.Component
2562 * Bootstrap Modal class
2563 * @cfg {String} title Title of dialog
2564 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2566 * @cfg {Boolean} specificTitle default false
2567 * @cfg {Array} buttons Array of buttons or standard button set..
2568 * @cfg {String} buttonPosition (left|right|center) default right
2569 * @cfg {Boolean} animate default true
2570 * @cfg {Boolean} allow_close default true
2571 * @cfg {Boolean} fitwindow default false
2572 * @cfg {String} size (sm|lg) default empty
2573 * @cfg {Number} max_width set the max width of modal
2577 * Create a new Modal Dialog
2578 * @param {Object} config The config object
2581 Roo.bootstrap.Modal = function(config){
2582 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2587 * The raw btnclick event for the button
2588 * @param {Roo.EventObject} e
2593 * Fire when dialog resize
2594 * @param {Roo.bootstrap.Modal} this
2595 * @param {Roo.EventObject} e
2599 this.buttons = this.buttons || [];
2602 this.tmpl = Roo.factory(this.tmpl);
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2609 title : 'test dialog',
2619 specificTitle: false,
2621 buttonPosition: 'right',
2644 onRender : function(ct, position)
2646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2649 var cfg = Roo.apply({}, this.getAutoCreate());
2652 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2654 //if (!cfg.name.length) {
2658 cfg.cls += ' ' + this.cls;
2661 cfg.style = this.style;
2663 this.el = Roo.get(document.body).createChild(cfg, position);
2665 //var type = this.el.dom.type;
2668 if(this.tabIndex !== undefined){
2669 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2672 this.dialogEl = this.el.select('.modal-dialog',true).first();
2673 this.bodyEl = this.el.select('.modal-body',true).first();
2674 this.closeEl = this.el.select('.modal-header .close', true).first();
2675 this.headerEl = this.el.select('.modal-header',true).first();
2676 this.titleEl = this.el.select('.modal-title',true).first();
2677 this.footerEl = this.el.select('.modal-footer',true).first();
2679 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2681 //this.el.addClass("x-dlg-modal");
2683 if (this.buttons.length) {
2684 Roo.each(this.buttons, function(bb) {
2685 var b = Roo.apply({}, bb);
2686 b.xns = b.xns || Roo.bootstrap;
2687 b.xtype = b.xtype || 'Button';
2688 if (typeof(b.listeners) == 'undefined') {
2689 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2692 var btn = Roo.factory(b);
2694 btn.render(this.el.select('.modal-footer div').first());
2698 // render the children.
2701 if(typeof(this.items) != 'undefined'){
2702 var items = this.items;
2705 for(var i =0;i < items.length;i++) {
2706 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2710 this.items = nitems;
2712 // where are these used - they used to be body/close/footer
2716 //this.el.addClass([this.fieldClass, this.cls]);
2720 getAutoCreate : function()
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(
2819 Roo.lib.Dom.getViewWidth(true),
2820 Roo.lib.Dom.getViewHeight(true)
2823 if (this.fitwindow) {
2825 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2831 if(this.max_width !== 0) {
2833 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2836 this.setSize(w, this.height);
2840 if(this.max_height) {
2841 this.setSize(w,Math.min(
2843 Roo.lib.Dom.getViewportHeight(true) - 60
2849 if(!this.fit_content) {
2850 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2854 this.setSize(w, Math.min(
2856 this.headerEl.getHeight() +
2857 this.footerEl.getHeight() +
2858 this.getChildHeight(this.bodyEl.dom.childNodes),
2859 Roo.lib.Dom.getViewportHeight(true) - 60)
2865 setSize : function(w,h)
2876 if (!this.rendered) {
2880 //this.el.setStyle('display', 'block');
2881 this.el.removeClass('hideing');
2882 this.el.addClass('show');
2884 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2887 this.el.addClass('in');
2890 this.el.addClass('in');
2893 // not sure how we can show data in here..
2895 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898 Roo.get(document.body).addClass("x-body-masked");
2900 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2901 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902 this.maskEl.addClass('show');
2906 this.fireEvent('show', this);
2908 // set zindex here - otherwise it appears to be ignored...
2909 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912 this.items.forEach( function(e) {
2913 e.layout ? e.layout() : false;
2921 if(this.fireEvent("beforehide", this) !== false){
2922 this.maskEl.removeClass('show');
2923 Roo.get(document.body).removeClass("x-body-masked");
2924 this.el.removeClass('in');
2925 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2927 if(this.animate){ // why
2928 this.el.addClass('hideing');
2930 if (!this.el.hasClass('hideing')) {
2931 return; // it's been shown again...
2933 this.el.removeClass('show');
2934 this.el.removeClass('hideing');
2938 this.el.removeClass('show');
2940 this.fireEvent('hide', this);
2943 isVisible : function()
2946 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950 addButton : function(str, cb)
2954 var b = Roo.apply({}, { html : str } );
2955 b.xns = b.xns || Roo.bootstrap;
2956 b.xtype = b.xtype || 'Button';
2957 if (typeof(b.listeners) == 'undefined') {
2958 b.listeners = { click : cb.createDelegate(this) };
2961 var btn = Roo.factory(b);
2963 btn.render(this.el.select('.modal-footer div').first());
2969 setDefaultButton : function(btn)
2971 //this.el.select('.modal-footer').()
2975 resizeTo: function(w,h)
2979 this.dialogEl.setWidth(w);
2980 if (this.diff === false) {
2981 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984 this.bodyEl.setHeight(h - this.diff);
2986 this.fireEvent('resize', this);
2989 setContentSize : function(w, h)
2993 onButtonClick: function(btn,e)
2996 this.fireEvent('btnclick', btn.name, e);
2999 * Set the title of the Dialog
3000 * @param {String} str new Title
3002 setTitle: function(str) {
3003 this.titleEl.dom.innerHTML = str;
3006 * Set the body of the Dialog
3007 * @param {String} str new Title
3009 setBody: function(str) {
3010 this.bodyEl.dom.innerHTML = str;
3013 * Set the body of the Dialog using the template
3014 * @param {Obj} data - apply this data to the template and replace the body contents.
3016 applyBody: function(obj)
3019 Roo.log("Error - using apply Body without a template");
3022 this.tmpl.overwrite(this.bodyEl, obj);
3025 getChildHeight : function(child_nodes)
3029 child_nodes.length == 0
3034 var child_height = 0;
3036 for(var i = 0; i < child_nodes.length; i++) {
3039 * for modal with tabs...
3040 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042 var layout_childs = child_nodes[i].childNodes;
3044 for(var j = 0; j < layout_childs.length; j++) {
3046 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048 var layout_body_childs = layout_childs[j].childNodes;
3050 for(var k = 0; k < layout_body_childs.length; k++) {
3052 if(layout_body_childs[k].classList.contains('navbar')) {
3053 child_height += layout_body_childs[k].offsetHeight;
3057 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3059 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3061 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3063 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3079 child_height += child_nodes[i].offsetHeight;
3080 // Roo.log(child_nodes[i].offsetHeight);
3083 return child_height;
3089 Roo.apply(Roo.bootstrap.Modal, {
3091 * Button config that displays a single OK button
3100 * Button config that displays Yes and No buttons
3116 * Button config that displays OK and Cancel buttons
3131 * Button config that displays Yes, No and Cancel buttons
3155 * messagebox - can be used as a replace
3159 * @class Roo.MessageBox
3160 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169 // process text value...
3173 // Show a dialog using config options:
3175 title:'Save Changes?',
3176 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177 buttons: Roo.Msg.YESNOCANCEL,
3184 Roo.bootstrap.MessageBox = function(){
3185 var dlg, opt, mask, waitTimer;
3186 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187 var buttons, activeTextEl, bwidth;
3191 var handleButton = function(button){
3193 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3197 var handleHide = function(){
3199 dlg.el.removeClass(opt.cls);
3202 // Roo.TaskMgr.stop(waitTimer);
3203 // waitTimer = null;
3208 var updateButtons = function(b){
3211 buttons["ok"].hide();
3212 buttons["cancel"].hide();
3213 buttons["yes"].hide();
3214 buttons["no"].hide();
3215 //dlg.footer.dom.style.display = 'none';
3218 dlg.footerEl.dom.style.display = '';
3219 for(var k in buttons){
3220 if(typeof buttons[k] != "function"){
3223 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224 width += buttons[k].el.getWidth()+15;
3234 var handleEsc = function(d, k, e){
3235 if(opt && opt.closable !== false){
3245 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246 * @return {Roo.BasicDialog} The BasicDialog element
3248 getDialog : function(){
3250 dlg = new Roo.bootstrap.Modal( {
3253 //constraintoviewport:false,
3255 //collapsible : false,
3260 //buttonAlign:"center",
3261 closeClick : function(){
3262 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3265 handleButton("cancel");
3270 dlg.on("hide", handleHide);
3272 //dlg.addKeyListener(27, handleEsc);
3274 this.buttons = buttons;
3275 var bt = this.buttonText;
3276 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281 bodyEl = dlg.bodyEl.createChild({
3283 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284 '<textarea class="roo-mb-textarea"></textarea>' +
3285 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3287 msgEl = bodyEl.dom.firstChild;
3288 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289 textboxEl.enableDisplayMode();
3290 textboxEl.addKeyListener([10,13], function(){
3291 if(dlg.isVisible() && opt && opt.buttons){
3294 }else if(opt.buttons.yes){
3295 handleButton("yes");
3299 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300 textareaEl.enableDisplayMode();
3301 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302 progressEl.enableDisplayMode();
3304 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305 var pf = progressEl.dom.firstChild;
3307 pp = Roo.get(pf.firstChild);
3308 pp.setHeight(pf.offsetHeight);
3316 * Updates the message box body text
3317 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318 * the XHTML-compliant non-breaking space character '&#160;')
3319 * @return {Roo.MessageBox} This message box
3321 updateText : function(text)
3323 if(!dlg.isVisible() && !opt.width){
3324 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327 msgEl.innerHTML = text || ' ';
3329 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332 Math.min(opt.width || cw , this.maxWidth),
3333 Math.max(opt.minWidth || this.minWidth, bwidth)
3336 activeTextEl.setWidth(w);
3338 if(dlg.isVisible()){
3339 dlg.fixedcenter = false;
3341 // to big, make it scroll. = But as usual stupid IE does not support
3344 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348 bodyEl.dom.style.height = '';
3349 bodyEl.dom.style.overflowY = '';
3352 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354 bodyEl.dom.style.overflowX = '';
3357 dlg.setContentSize(w, bodyEl.getHeight());
3358 if(dlg.isVisible()){
3359 dlg.fixedcenter = true;
3365 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3366 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369 * @return {Roo.MessageBox} This message box
3371 updateProgress : function(value, text){
3373 this.updateText(text);
3376 if (pp) { // weird bug on my firefox - for some reason this is not defined
3377 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3384 * Returns true if the message box is currently displayed
3385 * @return {Boolean} True if the message box is visible, else false
3387 isVisible : function(){
3388 return dlg && dlg.isVisible();
3392 * Hides the message box if it is displayed
3395 if(this.isVisible()){
3401 * Displays a new message box, or reinitializes an existing message box, based on the config options
3402 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403 * The following config object properties are supported:
3405 Property Type Description
3406 ---------- --------------- ------------------------------------------------------------------------------------
3407 animEl String/Element An id or Element from which the message box should animate as it opens and
3408 closes (defaults to undefined)
3409 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable Boolean False to hide the top-right close button (defaults to true). Note that
3412 progress and wait dialogs will ignore this property and always hide the
3413 close button as they can only be closed programmatically.
3414 cls String A custom CSS class to apply to the message box element
3415 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3416 displayed (defaults to 75)
3417 fn Function A callback function to execute after closing the dialog. The arguments to the
3418 function will be btn (the name of the button that was clicked, if applicable,
3419 e.g. "ok"), and text (the value of the active text field, if applicable).
3420 Progress and wait dialogs will ignore this option since they do not respond to
3421 user actions and can only be closed programmatically, so any required function
3422 should be called by the same code after it closes the dialog.
3423 icon String A CSS class that provides a background image to be used as an icon for
3424 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3426 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3427 modal Boolean False to allow user interaction with the page while the message box is
3428 displayed (defaults to true)
3429 msg String A string that will replace the existing message box body text (defaults
3430 to the XHTML-compliant non-breaking space character ' ')
3431 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3432 progress Boolean True to display a progress bar (defaults to false)
3433 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3436 title String The title text
3437 value String The string value to set into the active textbox element if displayed
3438 wait Boolean True to display a progress bar (defaults to false)
3439 width Number The width of the dialog in pixels
3446 msg: 'Please enter your address:',
3448 buttons: Roo.MessageBox.OKCANCEL,
3451 animEl: 'addAddressBtn'
3454 * @param {Object} config Configuration options
3455 * @return {Roo.MessageBox} This message box
3457 show : function(options)
3460 // this causes nightmares if you show one dialog after another
3461 // especially on callbacks..
3463 if(this.isVisible()){
3466 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3468 Roo.log("New Dialog Message:" + options.msg )
3469 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3473 var d = this.getDialog();
3475 d.setTitle(opt.title || " ");
3476 d.closeEl.setDisplayed(opt.closable !== false);
3477 activeTextEl = textboxEl;
3478 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3483 textareaEl.setHeight(typeof opt.multiline == "number" ?
3484 opt.multiline : this.defaultTextHeight);
3485 activeTextEl = textareaEl;
3494 progressEl.setDisplayed(opt.progress === true);
3495 this.updateProgress(0);
3496 activeTextEl.dom.value = opt.value || "";
3498 dlg.setDefaultButton(activeTextEl);
3500 var bs = opt.buttons;
3504 }else if(bs && bs.yes){
3505 db = buttons["yes"];
3507 dlg.setDefaultButton(db);
3509 bwidth = updateButtons(opt.buttons);
3510 this.updateText(opt.msg);
3512 d.el.addClass(opt.cls);
3514 d.proxyDrag = opt.proxyDrag === true;
3515 d.modal = opt.modal !== false;
3516 d.mask = opt.modal !== false ? mask : false;
3518 // force it to the end of the z-index stack so it gets a cursor in FF
3519 document.body.appendChild(dlg.el.dom);
3520 d.animateTarget = null;
3521 d.show(options.animEl);
3527 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3528 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529 * and closing the message box when the process is complete.
3530 * @param {String} title The title bar text
3531 * @param {String} msg The message box body text
3532 * @return {Roo.MessageBox} This message box
3534 progress : function(title, msg){
3541 minWidth: this.minProgressWidth,
3548 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549 * If a callback function is passed it will be called after the user clicks the button, and the
3550 * id of the button that was clicked will be passed as the only parameter to the callback
3551 * (could also be the top-right close button).
3552 * @param {String} title The title bar text
3553 * @param {String} msg The message box body text
3554 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555 * @param {Object} scope (optional) The scope of the callback function
3556 * @return {Roo.MessageBox} This message box
3558 alert : function(title, msg, fn, scope)
3573 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3574 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575 * You are responsible for closing the message box when the process is complete.
3576 * @param {String} msg The message box body text
3577 * @param {String} title (optional) The title bar text
3578 * @return {Roo.MessageBox} This message box
3580 wait : function(msg, title){
3591 waitTimer = Roo.TaskMgr.start({
3593 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3601 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604 * @param {String} title The title bar text
3605 * @param {String} msg The message box body text
3606 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607 * @param {Object} scope (optional) The scope of the callback function
3608 * @return {Roo.MessageBox} This message box
3610 confirm : function(title, msg, fn, scope){
3614 buttons: this.YESNO,
3623 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3625 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626 * (could also be the top-right close button) and the text that was entered will be passed as the two
3627 * parameters to the callback.
3628 * @param {String} title The title bar text
3629 * @param {String} msg The message box body text
3630 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631 * @param {Object} scope (optional) The scope of the callback function
3632 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634 * @return {Roo.MessageBox} This message box
3636 prompt : function(title, msg, fn, scope, multiline){
3640 buttons: this.OKCANCEL,
3645 multiline: multiline,
3652 * Button config that displays a single OK button
3657 * Button config that displays Yes and No buttons
3660 YESNO : {yes:true, no:true},
3662 * Button config that displays OK and Cancel buttons
3665 OKCANCEL : {ok:true, cancel:true},
3667 * Button config that displays Yes, No and Cancel buttons
3670 YESNOCANCEL : {yes:true, no:true, cancel:true},
3673 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3676 defaultTextHeight : 75,
3678 * The maximum width in pixels of the message box (defaults to 600)
3683 * The minimum width in pixels of the message box (defaults to 100)
3688 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3689 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3692 minProgressWidth : 250,
3694 * An object containing the default button text strings that can be overriden for localized language support.
3695 * Supported properties are: ok, cancel, yes and no.
3696 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3709 * Shorthand for {@link Roo.MessageBox}
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3721 * @class Roo.bootstrap.Navbar
3722 * @extends Roo.bootstrap.Component
3723 * Bootstrap Navbar class
3726 * Create a new Navbar
3727 * @param {Object} config The config object
3731 Roo.bootstrap.Navbar = function(config){
3732 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3736 * @event beforetoggle
3737 * Fire before toggle the menu
3738 * @param {Roo.EventObject} e
3740 "beforetoggle" : true
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3753 getAutoCreate : function(){
3756 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3760 initEvents :function ()
3762 //Roo.log(this.el.select('.navbar-toggle',true));
3763 this.el.select('.navbar-toggle',true).on('click', function() {
3764 if(this.fireEvent('beforetoggle', this) !== false){
3765 this.el.select('.navbar-collapse',true).toggleClass('in');
3775 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777 var size = this.el.getSize();
3778 this.maskEl.setSize(size.width, size.height);
3779 this.maskEl.enableDisplayMode("block");
3788 getChildContainer : function()
3790 if (this.el.select('.collapse').getCount()) {
3791 return this.el.select('.collapse',true).first();
3824 * @class Roo.bootstrap.NavSimplebar
3825 * @extends Roo.bootstrap.Navbar
3826 * Bootstrap Sidebar class
3828 * @cfg {Boolean} inverse is inverted color
3830 * @cfg {String} type (nav | pills | tabs)
3831 * @cfg {Boolean} arrangement stacked | justified
3832 * @cfg {String} align (left | right) alignment
3834 * @cfg {Boolean} main (true|false) main nav bar? default false
3835 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837 * @cfg {String} tag (header|footer|nav|div) default is nav
3843 * Create a new Sidebar
3844 * @param {Object} config The config object
3848 Roo.bootstrap.NavSimplebar = function(config){
3849 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3868 getAutoCreate : function(){
3872 tag : this.tag || 'div',
3885 this.type = this.type || 'nav';
3886 if (['tabs','pills'].indexOf(this.type)!==-1) {
3887 cfg.cn[0].cls += ' nav-' + this.type
3891 if (this.type!=='nav') {
3892 Roo.log('nav type must be nav/tabs/pills')
3894 cfg.cn[0].cls += ' navbar-nav'
3900 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901 cfg.cn[0].cls += ' nav-' + this.arrangement;
3905 if (this.align === 'right') {
3906 cfg.cn[0].cls += ' navbar-right';
3910 cfg.cls += ' navbar-inverse';
3937 * @class Roo.bootstrap.NavHeaderbar
3938 * @extends Roo.bootstrap.NavSimplebar
3939 * Bootstrap Sidebar class
3941 * @cfg {String} brand what is brand
3942 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943 * @cfg {String} brand_href href of the brand
3944 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3945 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3950 * Create a new Sidebar
3951 * @param {Object} config The config object
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3967 desktopCenter : false,
3970 getAutoCreate : function(){
3973 tag: this.nav || 'nav',
3980 if (this.desktopCenter) {
3981 cn.push({cls : 'container', cn : []});
3988 cls: 'navbar-header',
3993 cls: 'navbar-toggle',
3994 'data-toggle': 'collapse',
3999 html: 'Toggle navigation'
4021 cls: 'collapse navbar-collapse',
4025 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028 cfg.cls += ' navbar-' + this.position;
4030 // tag can override this..
4032 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4035 if (this.brand !== '') {
4038 href: this.brand_href ? this.brand_href : '#',
4039 cls: 'navbar-brand',
4047 cfg.cls += ' main-nav';
4055 getHeaderChildContainer : function()
4057 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058 return this.el.select('.navbar-header',true).first();
4061 return this.getChildContainer();
4065 initEvents : function()
4067 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069 if (this.autohide) {
4074 Roo.get(document).on('scroll',function(e) {
4075 var ns = Roo.get(document).getScroll().top;
4076 var os = prevScroll;
4080 ft.removeClass('slideDown');
4081 ft.addClass('slideUp');
4084 ft.removeClass('slideUp');
4085 ft.addClass('slideDown');
4106 * @class Roo.bootstrap.NavSidebar
4107 * @extends Roo.bootstrap.Navbar
4108 * Bootstrap Sidebar class
4111 * Create a new Sidebar
4112 * @param {Object} config The config object
4116 Roo.bootstrap.NavSidebar = function(config){
4117 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4122 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124 getAutoCreate : function(){
4129 cls: 'sidebar sidebar-nav'
4151 * @class Roo.bootstrap.NavGroup
4152 * @extends Roo.bootstrap.Component
4153 * Bootstrap NavGroup class
4154 * @cfg {String} align (left|right)
4155 * @cfg {Boolean} inverse
4156 * @cfg {String} type (nav|pills|tab) default nav
4157 * @cfg {String} navId - reference Id for navbar.
4161 * Create a new nav group
4162 * @param {Object} config The config object
4165 Roo.bootstrap.NavGroup = function(config){
4166 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4169 Roo.bootstrap.NavGroup.register(this);
4173 * Fires when the active item changes
4174 * @param {Roo.bootstrap.NavGroup} this
4175 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4194 getAutoCreate : function()
4196 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4203 if (['tabs','pills'].indexOf(this.type)!==-1) {
4204 cfg.cls += ' nav-' + this.type
4206 if (this.type!=='nav') {
4207 Roo.log('nav type must be nav/tabs/pills')
4209 cfg.cls += ' navbar-nav'
4212 if (this.parent() && this.parent().sidebar) {
4215 cls: 'dashboard-menu sidebar-menu'
4221 if (this.form === true) {
4227 if (this.align === 'right') {
4228 cfg.cls += ' navbar-right';
4230 cfg.cls += ' navbar-left';
4234 if (this.align === 'right') {
4235 cfg.cls += ' navbar-right';
4239 cfg.cls += ' navbar-inverse';
4247 * sets the active Navigation item
4248 * @param {Roo.bootstrap.NavItem} the new current navitem
4250 setActiveItem : function(item)
4253 Roo.each(this.navItems, function(v){
4258 v.setActive(false, true);
4265 item.setActive(true, true);
4266 this.fireEvent('changed', this, item, prev);
4271 * gets the active Navigation item
4272 * @return {Roo.bootstrap.NavItem} the current navitem
4274 getActive : function()
4278 Roo.each(this.navItems, function(v){
4289 indexOfNav : function()
4293 Roo.each(this.navItems, function(v,i){
4304 * adds a Navigation item
4305 * @param {Roo.bootstrap.NavItem} the navitem to add
4307 addItem : function(cfg)
4309 var cn = new Roo.bootstrap.NavItem(cfg);
4311 cn.parentId = this.id;
4312 cn.onRender(this.el, null);
4316 * register a Navigation item
4317 * @param {Roo.bootstrap.NavItem} the navitem to add
4319 register : function(item)
4321 this.navItems.push( item);
4322 item.navId = this.navId;
4327 * clear all the Navigation item
4330 clearAll : function()
4333 this.el.dom.innerHTML = '';
4336 getNavItem: function(tabId)
4339 Roo.each(this.navItems, function(e) {
4340 if (e.tabId == tabId) {
4350 setActiveNext : function()
4352 var i = this.indexOfNav(this.getActive());
4353 if (i > this.navItems.length) {
4356 this.setActiveItem(this.navItems[i+1]);
4358 setActivePrev : function()
4360 var i = this.indexOfNav(this.getActive());
4364 this.setActiveItem(this.navItems[i-1]);
4366 clearWasActive : function(except) {
4367 Roo.each(this.navItems, function(e) {
4368 if (e.tabId != except.tabId && e.was_active) {
4369 e.was_active = false;
4376 getWasActive : function ()
4379 Roo.each(this.navItems, function(e) {
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4398 * register a Navigation Group
4399 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401 register : function(navgrp)
4403 this.groups[navgrp.navId] = navgrp;
4407 * fetch a Navigation Group based on the navigation ID
4408 * @param {string} the navgroup to add
4409 * @returns {Roo.bootstrap.NavGroup} the navgroup
4411 get: function(navId) {
4412 if (typeof(this.groups[navId]) == 'undefined') {
4414 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416 return this.groups[navId] ;
4431 * @class Roo.bootstrap.NavItem
4432 * @extends Roo.bootstrap.Component
4433 * Bootstrap Navbar.NavItem class
4434 * @cfg {String} href link to
4435 * @cfg {String} html content of button
4436 * @cfg {String} badge text inside badge
4437 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438 * @cfg {String} glyphicon name of glyphicon
4439 * @cfg {String} icon name of font awesome icon
4440 * @cfg {Boolean} active Is item active
4441 * @cfg {Boolean} disabled Is item disabled
4443 * @cfg {Boolean} preventDefault (true | false) default false
4444 * @cfg {String} tabId the tab that this item activates.
4445 * @cfg {String} tagtype (a|span) render as a href or span?
4446 * @cfg {Boolean} animateRef (true|false) link to element default false
4449 * Create a new Navbar Item
4450 * @param {Object} config The config object
4452 Roo.bootstrap.NavItem = function(config){
4453 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4458 * The raw click event for the entire grid.
4459 * @param {Roo.EventObject} e
4464 * Fires when the active item active state changes
4465 * @param {Roo.bootstrap.NavItem} this
4466 * @param {boolean} state the new state
4472 * Fires when scroll to element
4473 * @param {Roo.bootstrap.NavItem} this
4474 * @param {Object} options
4475 * @param {Roo.EventObject} e
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4491 preventDefault : false,
4498 getAutoCreate : function(){
4507 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509 if (this.disabled) {
4510 cfg.cls += ' disabled';
4513 if (this.href || this.html || this.glyphicon || this.icon) {
4517 href : this.href || "#",
4518 html: this.html || ''
4523 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4526 if(this.glyphicon) {
4527 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4532 cfg.cn[0].html += " <span class='caret'></span>";
4536 if (this.badge !== '') {
4538 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4546 initEvents: function()
4548 if (typeof (this.menu) != 'undefined') {
4549 this.menu.parentType = this.xtype;
4550 this.menu.triggerEl = this.el;
4551 this.menu = this.addxtype(Roo.apply({}, this.menu));
4554 this.el.select('a',true).on('click', this.onClick, this);
4556 if(this.tagtype == 'span'){
4557 this.el.select('span',true).on('click', this.onClick, this);
4560 // at this point parent should be available..
4561 this.parent().register(this);
4564 onClick : function(e)
4566 if (e.getTarget('.dropdown-menu-item')) {
4567 // did you click on a menu itemm.... - then don't trigger onclick..
4572 this.preventDefault ||
4575 Roo.log("NavItem - prevent Default?");
4579 if (this.disabled) {
4583 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584 if (tg && tg.transition) {
4585 Roo.log("waiting for the transitionend");
4591 //Roo.log("fire event clicked");
4592 if(this.fireEvent('click', this, e) === false){
4596 if(this.tagtype == 'span'){
4600 //Roo.log(this.href);
4601 var ael = this.el.select('a',true).first();
4604 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607 return; // ignore... - it's a 'hash' to another page.
4609 Roo.log("NavItem - prevent Default?");
4611 this.scrollToElement(e);
4615 var p = this.parent();
4617 if (['tabs','pills'].indexOf(p.type)!==-1) {
4618 if (typeof(p.setActiveItem) !== 'undefined') {
4619 p.setActiveItem(this);
4623 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625 // remove the collapsed menu expand...
4626 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4630 isActive: function () {
4633 setActive : function(state, fire, is_was_active)
4635 if (this.active && !state && this.navId) {
4636 this.was_active = true;
4637 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639 nv.clearWasActive(this);
4643 this.active = state;
4646 this.el.removeClass('active');
4647 } else if (!this.el.hasClass('active')) {
4648 this.el.addClass('active');
4651 this.fireEvent('changed', this, state);
4654 // show a panel if it's registered and related..
4656 if (!this.navId || !this.tabId || !state || is_was_active) {
4660 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4664 var pan = tg.getPanelByName(this.tabId);
4668 // if we can not flip to new panel - go back to old nav highlight..
4669 if (false == tg.showPanel(pan)) {
4670 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672 var onav = nv.getWasActive();
4674 onav.setActive(true, false, true);
4683 // this should not be here...
4684 setDisabled : function(state)
4686 this.disabled = state;
4688 this.el.removeClass('disabled');
4689 } else if (!this.el.hasClass('disabled')) {
4690 this.el.addClass('disabled');
4696 * Fetch the element to display the tooltip on.
4697 * @return {Roo.Element} defaults to this.el
4699 tooltipEl : function()
4701 return this.el.select('' + this.tagtype + '', true).first();
4704 scrollToElement : function(e)
4706 var c = document.body;
4709 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712 c = document.documentElement;
4715 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4721 var o = target.calcOffsetsTo(c);
4728 this.fireEvent('scrollto', this, options, e);
4730 Roo.get(c).scrollTo('top', options.value, true);
4743 * <span> icon </span>
4744 * <span> text </span>
4745 * <span>badge </span>
4749 * @class Roo.bootstrap.NavSidebarItem
4750 * @extends Roo.bootstrap.NavItem
4751 * Bootstrap Navbar.NavSidebarItem class
4752 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753 * {Boolean} open is the menu open
4754 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756 * {String} buttonSize (sm|md|lg)the extra classes for the button
4757 * {Boolean} showArrow show arrow next to the text (default true)
4759 * Create a new Navbar Button
4760 * @param {Object} config The config object
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4768 * The raw click event for the entire grid.
4769 * @param {Roo.EventObject} e
4774 * Fires when the active item active state changes
4775 * @param {Roo.bootstrap.NavSidebarItem} this
4776 * @param {boolean} state the new state
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4786 badgeWeight : 'default',
4792 buttonWeight : 'default',
4798 getAutoCreate : function(){
4803 href : this.href || '#',
4809 if(this.buttonView){
4812 href : this.href || '#',
4813 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4826 cfg.cls += ' active';
4829 if (this.disabled) {
4830 cfg.cls += ' disabled';
4833 cfg.cls += ' open x-open';
4836 if (this.glyphicon || this.icon) {
4837 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4838 a.cn.push({ tag : 'i', cls : c }) ;
4841 if(!this.buttonView){
4844 html : this.html || ''
4851 if (this.badge !== '') {
4852 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4858 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4861 a.cls += ' dropdown-toggle treeview' ;
4867 initEvents : function()
4869 if (typeof (this.menu) != 'undefined') {
4870 this.menu.parentType = this.xtype;
4871 this.menu.triggerEl = this.el;
4872 this.menu = this.addxtype(Roo.apply({}, this.menu));
4875 this.el.on('click', this.onClick, this);
4877 if(this.badge !== ''){
4878 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4883 onClick : function(e)
4890 if(this.preventDefault){
4894 this.fireEvent('click', this);
4897 disable : function()
4899 this.setDisabled(true);
4904 this.setDisabled(false);
4907 setDisabled : function(state)
4909 if(this.disabled == state){
4913 this.disabled = state;
4916 this.el.addClass('disabled');
4920 this.el.removeClass('disabled');
4925 setActive : function(state)
4927 if(this.active == state){
4931 this.active = state;
4934 this.el.addClass('active');
4938 this.el.removeClass('active');
4943 isActive: function ()
4948 setBadge : function(str)
4954 this.badgeEl.dom.innerHTML = str;
4971 * @class Roo.bootstrap.Row
4972 * @extends Roo.bootstrap.Component
4973 * Bootstrap Row class (contains columns...)
4977 * @param {Object} config The config object
4980 Roo.bootstrap.Row = function(config){
4981 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4986 getAutoCreate : function(){
5005 * @class Roo.bootstrap.Element
5006 * @extends Roo.bootstrap.Component
5007 * Bootstrap Element class
5008 * @cfg {String} html contents of the element
5009 * @cfg {String} tag tag of the element
5010 * @cfg {String} cls class of the element
5011 * @cfg {Boolean} preventDefault (true|false) default false
5012 * @cfg {Boolean} clickable (true|false) default false
5015 * Create a new Element
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Element = function(config){
5020 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5026 * When a element is chick
5027 * @param {Roo.bootstrap.Element} this
5028 * @param {Roo.EventObject} e
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5039 preventDefault: false,
5042 getAutoCreate : function(){
5046 // cls: this.cls, double assign in parent class Component.js :: onRender
5053 initEvents: function()
5055 Roo.bootstrap.Element.superclass.initEvents.call(this);
5058 this.el.on('click', this.onClick, this);
5063 onClick : function(e)
5065 if(this.preventDefault){
5069 this.fireEvent('click', this, e);
5072 getValue : function()
5074 return this.el.dom.innerHTML;
5077 setValue : function(value)
5079 this.el.dom.innerHTML = value;
5094 * @class Roo.bootstrap.Pagination
5095 * @extends Roo.bootstrap.Component
5096 * Bootstrap Pagination class
5097 * @cfg {String} size xs | sm | md | lg
5098 * @cfg {Boolean} inverse false | true
5101 * Create a new Pagination
5102 * @param {Object} config The config object
5105 Roo.bootstrap.Pagination = function(config){
5106 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5115 getAutoCreate : function(){
5121 cfg.cls += ' inverse';
5127 cfg.cls += " " + this.cls;
5145 * @class Roo.bootstrap.PaginationItem
5146 * @extends Roo.bootstrap.Component
5147 * Bootstrap PaginationItem class
5148 * @cfg {String} html text
5149 * @cfg {String} href the link
5150 * @cfg {Boolean} preventDefault (true | false) default true
5151 * @cfg {Boolean} active (true | false) default false
5152 * @cfg {Boolean} disabled default false
5156 * Create a new PaginationItem
5157 * @param {Object} config The config object
5161 Roo.bootstrap.PaginationItem = function(config){
5162 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5167 * The raw click event for the entire grid.
5168 * @param {Roo.EventObject} e
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5178 preventDefault: true,
5183 getAutoCreate : function(){
5189 href : this.href ? this.href : '#',
5190 html : this.html ? this.html : ''
5200 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5204 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5210 initEvents: function() {
5212 this.el.on('click', this.onClick, this);
5215 onClick : function(e)
5217 Roo.log('PaginationItem on click ');
5218 if(this.preventDefault){
5226 this.fireEvent('click', this, e);
5242 * @class Roo.bootstrap.Slider
5243 * @extends Roo.bootstrap.Component
5244 * Bootstrap Slider class
5247 * Create a new Slider
5248 * @param {Object} config The config object
5251 Roo.bootstrap.Slider = function(config){
5252 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5257 getAutoCreate : function(){
5261 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5265 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5277 * Ext JS Library 1.1.1
5278 * Copyright(c) 2006-2007, Ext JS, LLC.
5280 * Originally Released Under LGPL - original licence link has changed is not relivant.
5283 * <script type="text/javascript">
5288 * @class Roo.grid.ColumnModel
5289 * @extends Roo.util.Observable
5290 * This is the default implementation of a ColumnModel used by the Grid. It defines
5291 * the columns in the grid.
5294 var colModel = new Roo.grid.ColumnModel([
5295 {header: "Ticker", width: 60, sortable: true, locked: true},
5296 {header: "Company Name", width: 150, sortable: true},
5297 {header: "Market Cap.", width: 100, sortable: true},
5298 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299 {header: "Employees", width: 100, sortable: true, resizable: false}
5304 * The config options listed for this class are options which may appear in each
5305 * individual column definition.
5306 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308 * @param {Object} config An Array of column config objects. See this class's
5309 * config objects for details.
5311 Roo.grid.ColumnModel = function(config){
5313 * The config passed into the constructor
5315 this.config = config;
5318 // if no id, create one
5319 // if the column does not have a dataIndex mapping,
5320 // map it to the order it is in the config
5321 for(var i = 0, len = config.length; i < len; i++){
5323 if(typeof c.dataIndex == "undefined"){
5326 if(typeof c.renderer == "string"){
5327 c.renderer = Roo.util.Format[c.renderer];
5329 if(typeof c.id == "undefined"){
5332 if(c.editor && c.editor.xtype){
5333 c.editor = Roo.factory(c.editor, Roo.grid);
5335 if(c.editor && c.editor.isFormField){
5336 c.editor = new Roo.grid.GridEditor(c.editor);
5338 this.lookup[c.id] = c;
5342 * The width of columns which have no width specified (defaults to 100)
5345 this.defaultWidth = 100;
5348 * Default sortable of columns which have no sortable specified (defaults to false)
5351 this.defaultSortable = false;
5355 * @event widthchange
5356 * Fires when the width of a column changes.
5357 * @param {ColumnModel} this
5358 * @param {Number} columnIndex The column index
5359 * @param {Number} newWidth The new width
5361 "widthchange": true,
5363 * @event headerchange
5364 * Fires when the text of a header changes.
5365 * @param {ColumnModel} this
5366 * @param {Number} columnIndex The column index
5367 * @param {Number} newText The new header text
5369 "headerchange": true,
5371 * @event hiddenchange
5372 * Fires when a column is hidden or "unhidden".
5373 * @param {ColumnModel} this
5374 * @param {Number} columnIndex The column index
5375 * @param {Boolean} hidden true if hidden, false otherwise
5377 "hiddenchange": true,
5379 * @event columnmoved
5380 * Fires when a column is moved.
5381 * @param {ColumnModel} this
5382 * @param {Number} oldIndex
5383 * @param {Number} newIndex
5385 "columnmoved" : true,
5387 * @event columlockchange
5388 * Fires when a column's locked state is changed
5389 * @param {ColumnModel} this
5390 * @param {Number} colIndex
5391 * @param {Boolean} locked true if locked
5393 "columnlockchange" : true
5395 Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399 * @cfg {String} header The header text to display in the Grid view.
5402 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404 * specified, the column's index is used as an index into the Record's data Array.
5407 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5411 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412 * Defaults to the value of the {@link #defaultSortable} property.
5413 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5416 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5419 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5422 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5425 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5428 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5434 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5437 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5440 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5443 * @cfg {String} cursor (Optional)
5446 * @cfg {String} tooltip (Optional)
5449 * @cfg {Number} xs (Optional)
5452 * @cfg {Number} sm (Optional)
5455 * @cfg {Number} md (Optional)
5458 * @cfg {Number} lg (Optional)
5461 * Returns the id of the column at the specified index.
5462 * @param {Number} index The column index
5463 * @return {String} the id
5465 getColumnId : function(index){
5466 return this.config[index].id;
5470 * Returns the column for a specified id.
5471 * @param {String} id The column id
5472 * @return {Object} the column
5474 getColumnById : function(id){
5475 return this.lookup[id];
5480 * Returns the column for a specified dataIndex.
5481 * @param {String} dataIndex The column dataIndex
5482 * @return {Object|Boolean} the column or false if not found
5484 getColumnByDataIndex: function(dataIndex){
5485 var index = this.findColumnIndex(dataIndex);
5486 return index > -1 ? this.config[index] : false;
5490 * Returns the index for a specified column id.
5491 * @param {String} id The column id
5492 * @return {Number} the index, or -1 if not found
5494 getIndexById : function(id){
5495 for(var i = 0, len = this.config.length; i < len; i++){
5496 if(this.config[i].id == id){
5504 * Returns the index for a specified column dataIndex.
5505 * @param {String} dataIndex The column dataIndex
5506 * @return {Number} the index, or -1 if not found
5509 findColumnIndex : function(dataIndex){
5510 for(var i = 0, len = this.config.length; i < len; i++){
5511 if(this.config[i].dataIndex == dataIndex){
5519 moveColumn : function(oldIndex, newIndex){
5520 var c = this.config[oldIndex];
5521 this.config.splice(oldIndex, 1);
5522 this.config.splice(newIndex, 0, c);
5523 this.dataMap = null;
5524 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5527 isLocked : function(colIndex){
5528 return this.config[colIndex].locked === true;
5531 setLocked : function(colIndex, value, suppressEvent){
5532 if(this.isLocked(colIndex) == value){
5535 this.config[colIndex].locked = value;
5537 this.fireEvent("columnlockchange", this, colIndex, value);
5541 getTotalLockedWidth : function(){
5543 for(var i = 0; i < this.config.length; i++){
5544 if(this.isLocked(i) && !this.isHidden(i)){
5545 this.totalWidth += this.getColumnWidth(i);
5551 getLockedCount : function(){
5552 for(var i = 0, len = this.config.length; i < len; i++){
5553 if(!this.isLocked(i)){
5558 return this.config.length;
5562 * Returns the number of columns.
5565 getColumnCount : function(visibleOnly){
5566 if(visibleOnly === true){
5568 for(var i = 0, len = this.config.length; i < len; i++){
5569 if(!this.isHidden(i)){
5575 return this.config.length;
5579 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580 * @param {Function} fn
5581 * @param {Object} scope (optional)
5582 * @return {Array} result
5584 getColumnsBy : function(fn, scope){
5586 for(var i = 0, len = this.config.length; i < len; i++){
5587 var c = this.config[i];
5588 if(fn.call(scope||this, c, i) === true){
5596 * Returns true if the specified column is sortable.
5597 * @param {Number} col The column index
5600 isSortable : function(col){
5601 if(typeof this.config[col].sortable == "undefined"){
5602 return this.defaultSortable;
5604 return this.config[col].sortable;
5608 * Returns the rendering (formatting) function defined for the column.
5609 * @param {Number} col The column index.
5610 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612 getRenderer : function(col){
5613 if(!this.config[col].renderer){
5614 return Roo.grid.ColumnModel.defaultRenderer;
5616 return this.config[col].renderer;
5620 * Sets the rendering (formatting) function for a column.
5621 * @param {Number} col The column index
5622 * @param {Function} fn The function to use to process the cell's raw data
5623 * to return HTML markup for the grid view. The render function is called with
5624 * the following parameters:<ul>
5625 * <li>Data value.</li>
5626 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627 * <li>css A CSS style string to apply to the table cell.</li>
5628 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630 * <li>Row index</li>
5631 * <li>Column index</li>
5632 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634 setRenderer : function(col, fn){
5635 this.config[col].renderer = fn;
5639 * Returns the width for the specified column.
5640 * @param {Number} col The column index
5643 getColumnWidth : function(col){
5644 return this.config[col].width * 1 || this.defaultWidth;
5648 * Sets the width for a column.
5649 * @param {Number} col The column index
5650 * @param {Number} width The new width
5652 setColumnWidth : function(col, width, suppressEvent){
5653 this.config[col].width = width;
5654 this.totalWidth = null;
5656 this.fireEvent("widthchange", this, col, width);
5661 * Returns the total width of all columns.
5662 * @param {Boolean} includeHidden True to include hidden column widths
5665 getTotalWidth : function(includeHidden){
5666 if(!this.totalWidth){
5667 this.totalWidth = 0;
5668 for(var i = 0, len = this.config.length; i < len; i++){
5669 if(includeHidden || !this.isHidden(i)){
5670 this.totalWidth += this.getColumnWidth(i);
5674 return this.totalWidth;
5678 * Returns the header for the specified column.
5679 * @param {Number} col The column index
5682 getColumnHeader : function(col){
5683 return this.config[col].header;
5687 * Sets the header for a column.
5688 * @param {Number} col The column index
5689 * @param {String} header The new header
5691 setColumnHeader : function(col, header){
5692 this.config[col].header = header;
5693 this.fireEvent("headerchange", this, col, header);
5697 * Returns the tooltip for the specified column.
5698 * @param {Number} col The column index
5701 getColumnTooltip : function(col){
5702 return this.config[col].tooltip;
5705 * Sets the tooltip for a column.
5706 * @param {Number} col The column index
5707 * @param {String} tooltip The new tooltip
5709 setColumnTooltip : function(col, tooltip){
5710 this.config[col].tooltip = tooltip;
5714 * Returns the dataIndex for the specified column.
5715 * @param {Number} col The column index
5718 getDataIndex : function(col){
5719 return this.config[col].dataIndex;
5723 * Sets the dataIndex for a column.
5724 * @param {Number} col The column index
5725 * @param {Number} dataIndex The new dataIndex
5727 setDataIndex : function(col, dataIndex){
5728 this.config[col].dataIndex = dataIndex;
5734 * Returns true if the cell is editable.
5735 * @param {Number} colIndex The column index
5736 * @param {Number} rowIndex The row index - this is nto actually used..?
5739 isCellEditable : function(colIndex, rowIndex){
5740 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5744 * Returns the editor defined for the cell/column.
5745 * return false or null to disable editing.
5746 * @param {Number} colIndex The column index
5747 * @param {Number} rowIndex The row index
5750 getCellEditor : function(colIndex, rowIndex){
5751 return this.config[colIndex].editor;
5755 * Sets if a column is editable.
5756 * @param {Number} col The column index
5757 * @param {Boolean} editable True if the column is editable
5759 setEditable : function(col, editable){
5760 this.config[col].editable = editable;
5765 * Returns true if the column is hidden.
5766 * @param {Number} colIndex The column index
5769 isHidden : function(colIndex){
5770 return this.config[colIndex].hidden;
5775 * Returns true if the column width cannot be changed
5777 isFixed : function(colIndex){
5778 return this.config[colIndex].fixed;
5782 * Returns true if the column can be resized
5785 isResizable : function(colIndex){
5786 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5789 * Sets if a column is hidden.
5790 * @param {Number} colIndex The column index
5791 * @param {Boolean} hidden True if the column is hidden
5793 setHidden : function(colIndex, hidden){
5794 this.config[colIndex].hidden = hidden;
5795 this.totalWidth = null;
5796 this.fireEvent("hiddenchange", this, colIndex, hidden);
5800 * Sets the editor for a column.
5801 * @param {Number} col The column index
5802 * @param {Object} editor The editor object
5804 setEditor : function(col, editor){
5805 this.config[col].editor = editor;
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 if(typeof value == "object") {
5814 if(typeof value == "string" && value.length < 1){
5818 return String.format("{0}", value);
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5825 * Ext JS Library 1.1.1
5826 * Copyright(c) 2006-2007, Ext JS, LLC.
5828 * Originally Released Under LGPL - original licence link has changed is not relivant.
5831 * <script type="text/javascript">
5835 * @class Roo.LoadMask
5836 * A simple utility class for generically masking elements while loading data. If the element being masked has
5837 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5839 * element's UpdateManager load indicator and will be destroyed after the initial load.
5841 * Create a new LoadMask
5842 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843 * @param {Object} config The config object
5845 Roo.LoadMask = function(el, config){
5846 this.el = Roo.get(el);
5847 Roo.apply(this, config);
5849 this.store.on('beforeload', this.onBeforeLoad, this);
5850 this.store.on('load', this.onLoad, this);
5851 this.store.on('loadexception', this.onLoadException, this);
5852 this.removeMask = false;
5854 var um = this.el.getUpdateManager();
5855 um.showLoadIndicator = false; // disable the default indicator
5856 um.on('beforeupdate', this.onBeforeLoad, this);
5857 um.on('update', this.onLoad, this);
5858 um.on('failure', this.onLoad, this);
5859 this.removeMask = true;
5863 Roo.LoadMask.prototype = {
5865 * @cfg {Boolean} removeMask
5866 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5871 * The text to display in a centered loading message box (defaults to 'Loading...')
5875 * @cfg {String} msgCls
5876 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878 msgCls : 'x-mask-loading',
5881 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5887 * Disables the mask to prevent it from being displayed
5889 disable : function(){
5890 this.disabled = true;
5894 * Enables the mask so that it can be displayed
5896 enable : function(){
5897 this.disabled = false;
5900 onLoadException : function()
5904 if (typeof(arguments[3]) != 'undefined') {
5905 Roo.MessageBox.alert("Error loading",arguments[3]);
5909 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5917 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5922 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5926 onBeforeLoad : function(){
5928 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5933 destroy : function(){
5935 this.store.un('beforeload', this.onBeforeLoad, this);
5936 this.store.un('load', this.onLoad, this);
5937 this.store.un('loadexception', this.onLoadException, this);
5939 var um = this.el.getUpdateManager();
5940 um.un('beforeupdate', this.onBeforeLoad, this);
5941 um.un('update', this.onLoad, this);
5942 um.un('failure', this.onLoad, this);
5953 * @class Roo.bootstrap.Table
5954 * @extends Roo.bootstrap.Component
5955 * Bootstrap Table class
5956 * @cfg {String} cls table class
5957 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958 * @cfg {String} bgcolor Specifies the background color for a table
5959 * @cfg {Number} border Specifies whether the table cells should have borders or not
5960 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961 * @cfg {Number} cellspacing Specifies the space between cells
5962 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964 * @cfg {String} sortable Specifies that the table should be sortable
5965 * @cfg {String} summary Specifies a summary of the content of a table
5966 * @cfg {Number} width Specifies the width of a table
5967 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969 * @cfg {boolean} striped Should the rows be alternative striped
5970 * @cfg {boolean} bordered Add borders to the table
5971 * @cfg {boolean} hover Add hover highlighting
5972 * @cfg {boolean} condensed Format condensed
5973 * @cfg {boolean} responsive Format condensed
5974 * @cfg {Boolean} loadMask (true|false) default false
5975 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977 * @cfg {Boolean} rowSelection (true|false) default false
5978 * @cfg {Boolean} cellSelection (true|false) default false
5979 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5981 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5982 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5986 * Create a new Table
5987 * @param {Object} config The config object
5990 Roo.bootstrap.Table = function(config){
5991 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5996 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003 this.sm.grid = this;
6004 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005 this.sm = this.selModel;
6006 this.sm.xmodule = this.xmodule || false;
6009 if (this.cm && typeof(this.cm.config) == 'undefined') {
6010 this.colModel = new Roo.grid.ColumnModel(this.cm);
6011 this.cm = this.colModel;
6012 this.cm.xmodule = this.xmodule || false;
6015 this.store= Roo.factory(this.store, Roo.data);
6016 this.ds = this.store;
6017 this.ds.xmodule = this.xmodule || false;
6020 if (this.footer && this.store) {
6021 this.footer.dataSource = this.ds;
6022 this.footer = Roo.factory(this.footer);
6029 * Fires when a cell is clicked
6030 * @param {Roo.bootstrap.Table} this
6031 * @param {Roo.Element} el
6032 * @param {Number} rowIndex
6033 * @param {Number} columnIndex
6034 * @param {Roo.EventObject} e
6038 * @event celldblclick
6039 * Fires when a cell is double clicked
6040 * @param {Roo.bootstrap.Table} this
6041 * @param {Roo.Element} el
6042 * @param {Number} rowIndex
6043 * @param {Number} columnIndex
6044 * @param {Roo.EventObject} e
6046 "celldblclick" : true,
6049 * Fires when a row is clicked
6050 * @param {Roo.bootstrap.Table} this
6051 * @param {Roo.Element} el
6052 * @param {Number} rowIndex
6053 * @param {Roo.EventObject} e
6057 * @event rowdblclick
6058 * Fires when a row is double clicked
6059 * @param {Roo.bootstrap.Table} this
6060 * @param {Roo.Element} el
6061 * @param {Number} rowIndex
6062 * @param {Roo.EventObject} e
6064 "rowdblclick" : true,
6067 * Fires when a mouseover occur
6068 * @param {Roo.bootstrap.Table} this
6069 * @param {Roo.Element} el
6070 * @param {Number} rowIndex
6071 * @param {Number} columnIndex
6072 * @param {Roo.EventObject} e
6077 * Fires when a mouseout occur
6078 * @param {Roo.bootstrap.Table} this
6079 * @param {Roo.Element} el
6080 * @param {Number} rowIndex
6081 * @param {Number} columnIndex
6082 * @param {Roo.EventObject} e
6087 * Fires when a row is rendered, so you can change add a style to it.
6088 * @param {Roo.bootstrap.Table} this
6089 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6093 * @event rowsrendered
6094 * Fires when all the rows have been rendered
6095 * @param {Roo.bootstrap.Table} this
6097 'rowsrendered' : true,
6099 * @event contextmenu
6100 * The raw contextmenu event for the entire grid.
6101 * @param {Roo.EventObject} e
6103 "contextmenu" : true,
6105 * @event rowcontextmenu
6106 * Fires when a row is right clicked
6107 * @param {Roo.bootstrap.Table} this
6108 * @param {Number} rowIndex
6109 * @param {Roo.EventObject} e
6111 "rowcontextmenu" : true,
6113 * @event cellcontextmenu
6114 * Fires when a cell is right clicked
6115 * @param {Roo.bootstrap.Table} this
6116 * @param {Number} rowIndex
6117 * @param {Number} cellIndex
6118 * @param {Roo.EventObject} e
6120 "cellcontextmenu" : true,
6122 * @event headercontextmenu
6123 * Fires when a header is right clicked
6124 * @param {Roo.bootstrap.Table} this
6125 * @param {Number} columnIndex
6126 * @param {Roo.EventObject} e
6128 "headercontextmenu" : true
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6158 rowSelection : false,
6159 cellSelection : false,
6162 // Roo.Element - the tbody
6164 // Roo.Element - thead element
6167 container: false, // used by gridpanel...
6173 auto_hide_footer : false,
6175 getAutoCreate : function()
6177 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6184 if (this.scrollBody) {
6185 cfg.cls += ' table-body-fixed';
6188 cfg.cls += ' table-striped';
6192 cfg.cls += ' table-hover';
6194 if (this.bordered) {
6195 cfg.cls += ' table-bordered';
6197 if (this.condensed) {
6198 cfg.cls += ' table-condensed';
6200 if (this.responsive) {
6201 cfg.cls += ' table-responsive';
6205 cfg.cls+= ' ' +this.cls;
6208 // this lot should be simplifed...
6221 ].forEach(function(k) {
6229 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6232 if(this.store || this.cm){
6233 if(this.headerShow){
6234 cfg.cn.push(this.renderHeader());
6237 cfg.cn.push(this.renderBody());
6239 if(this.footerShow){
6240 cfg.cn.push(this.renderFooter());
6242 // where does this come from?
6243 //cfg.cls+= ' TableGrid';
6246 return { cn : [ cfg ] };
6249 initEvents : function()
6251 if(!this.store || !this.cm){
6254 if (this.selModel) {
6255 this.selModel.initEvents();
6259 //Roo.log('initEvents with ds!!!!');
6261 this.mainBody = this.el.select('tbody', true).first();
6262 this.mainHead = this.el.select('thead', true).first();
6263 this.mainFoot = this.el.select('tfoot', true).first();
6269 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270 e.on('click', _this.sort, _this);
6273 this.mainBody.on("click", this.onClick, this);
6274 this.mainBody.on("dblclick", this.onDblClick, this);
6276 // why is this done????? = it breaks dialogs??
6277 //this.parent().el.setStyle('position', 'relative');
6281 this.footer.parentId = this.id;
6282 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6285 this.el.select('tfoot tr td').first().addClass('hide');
6290 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6293 this.store.on('load', this.onLoad, this);
6294 this.store.on('beforeload', this.onBeforeLoad, this);
6295 this.store.on('update', this.onUpdate, this);
6296 this.store.on('add', this.onAdd, this);
6297 this.store.on("clear", this.clear, this);
6299 this.el.on("contextmenu", this.onContextMenu, this);
6301 this.mainBody.on('scroll', this.onBodyScroll, this);
6303 this.cm.on("headerchange", this.onHeaderChange, this);
6305 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6309 onContextMenu : function(e, t)
6311 this.processEvent("contextmenu", e);
6314 processEvent : function(name, e)
6316 if (name != 'touchstart' ) {
6317 this.fireEvent(name, e);
6320 var t = e.getTarget();
6322 var cell = Roo.get(t);
6328 if(cell.findParent('tfoot', false, true)){
6332 if(cell.findParent('thead', false, true)){
6334 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335 cell = Roo.get(t).findParent('th', false, true);
6337 Roo.log("failed to find th in thead?");
6338 Roo.log(e.getTarget());
6343 var cellIndex = cell.dom.cellIndex;
6345 var ename = name == 'touchstart' ? 'click' : name;
6346 this.fireEvent("header" + ename, this, cellIndex, e);
6351 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352 cell = Roo.get(t).findParent('td', false, true);
6354 Roo.log("failed to find th in tbody?");
6355 Roo.log(e.getTarget());
6360 var row = cell.findParent('tr', false, true);
6361 var cellIndex = cell.dom.cellIndex;
6362 var rowIndex = row.dom.rowIndex - 1;
6366 this.fireEvent("row" + name, this, rowIndex, e);
6370 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6376 onMouseover : function(e, el)
6378 var cell = Roo.get(el);
6384 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385 cell = cell.findParent('td', false, true);
6388 var row = cell.findParent('tr', false, true);
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = row.dom.rowIndex - 1; // start from 0
6392 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6396 onMouseout : function(e, el)
6398 var cell = Roo.get(el);
6404 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405 cell = cell.findParent('td', false, true);
6408 var row = cell.findParent('tr', false, true);
6409 var cellIndex = cell.dom.cellIndex;
6410 var rowIndex = row.dom.rowIndex - 1; // start from 0
6412 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6416 onClick : function(e, el)
6418 var cell = Roo.get(el);
6420 if(!cell || (!this.cellSelection && !this.rowSelection)){
6424 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425 cell = cell.findParent('td', false, true);
6428 if(!cell || typeof(cell) == 'undefined'){
6432 var row = cell.findParent('tr', false, true);
6434 if(!row || typeof(row) == 'undefined'){
6438 var cellIndex = cell.dom.cellIndex;
6439 var rowIndex = this.getRowIndex(row);
6441 // why??? - should these not be based on SelectionModel?
6442 if(this.cellSelection){
6443 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6446 if(this.rowSelection){
6447 this.fireEvent('rowclick', this, row, rowIndex, e);
6453 onDblClick : function(e,el)
6455 var cell = Roo.get(el);
6457 if(!cell || (!this.cellSelection && !this.rowSelection)){
6461 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462 cell = cell.findParent('td', false, true);
6465 if(!cell || typeof(cell) == 'undefined'){
6469 var row = cell.findParent('tr', false, true);
6471 if(!row || typeof(row) == 'undefined'){
6475 var cellIndex = cell.dom.cellIndex;
6476 var rowIndex = this.getRowIndex(row);
6478 if(this.cellSelection){
6479 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6482 if(this.rowSelection){
6483 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6487 sort : function(e,el)
6489 var col = Roo.get(el);
6491 if(!col.hasClass('sortable')){
6495 var sort = col.attr('sort');
6498 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6502 this.store.sortInfo = {field : sort, direction : dir};
6505 Roo.log("calling footer first");
6506 this.footer.onClick('first');
6509 this.store.load({ params : { start : 0 } });
6513 renderHeader : function()
6521 this.totalWidth = 0;
6523 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525 var config = cm.config[i];
6529 cls : 'x-hcol-' + i,
6531 html: cm.getColumnHeader(i)
6536 if(typeof(config.sortable) != 'undefined' && config.sortable){
6538 c.html = '<i class="glyphicon"></i>' + c.html;
6541 if(typeof(config.lgHeader) != 'undefined'){
6542 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6545 if(typeof(config.mdHeader) != 'undefined'){
6546 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6549 if(typeof(config.smHeader) != 'undefined'){
6550 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6553 if(typeof(config.xsHeader) != 'undefined'){
6554 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6561 if(typeof(config.tooltip) != 'undefined'){
6562 c.tooltip = config.tooltip;
6565 if(typeof(config.colspan) != 'undefined'){
6566 c.colspan = config.colspan;
6569 if(typeof(config.hidden) != 'undefined' && config.hidden){
6570 c.style += ' display:none;';
6573 if(typeof(config.dataIndex) != 'undefined'){
6574 c.sort = config.dataIndex;
6579 if(typeof(config.align) != 'undefined' && config.align.length){
6580 c.style += ' text-align:' + config.align + ';';
6583 if(typeof(config.width) != 'undefined'){
6584 c.style += ' width:' + config.width + 'px;';
6585 this.totalWidth += config.width;
6587 this.totalWidth += 100; // assume minimum of 100 per column?
6590 if(typeof(config.cls) != 'undefined'){
6591 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 c.cls += ' hidden-' + size;
6605 c.cls += ' col-' + size + '-' + config[size];
6615 renderBody : function()
6625 colspan : this.cm.getColumnCount()
6635 renderFooter : function()
6645 colspan : this.cm.getColumnCount()
6659 // Roo.log('ds onload');
6664 var ds = this.store;
6666 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668 if (_this.store.sortInfo) {
6670 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671 e.select('i', true).addClass(['glyphicon-arrow-up']);
6674 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675 e.select('i', true).addClass(['glyphicon-arrow-down']);
6680 var tbody = this.mainBody;
6682 if(ds.getCount() > 0){
6683 ds.data.each(function(d,rowIndex){
6684 var row = this.renderRow(cm, ds, rowIndex);
6686 tbody.createChild(row);
6690 if(row.cellObjects.length){
6691 Roo.each(row.cellObjects, function(r){
6692 _this.renderCellObject(r);
6699 var tfoot = this.el.select('tfoot', true).first();
6701 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705 var total = this.ds.getTotalCount();
6707 if(this.footer.pageSize < total){
6708 this.mainFoot.show();
6712 Roo.each(this.el.select('tbody td', true).elements, function(e){
6713 e.on('mouseover', _this.onMouseover, _this);
6716 Roo.each(this.el.select('tbody td', true).elements, function(e){
6717 e.on('mouseout', _this.onMouseout, _this);
6719 this.fireEvent('rowsrendered', this);
6725 onUpdate : function(ds,record)
6727 this.refreshRow(record);
6731 onRemove : function(ds, record, index, isUpdate){
6732 if(isUpdate !== true){
6733 this.fireEvent("beforerowremoved", this, index, record);
6735 var bt = this.mainBody.dom;
6737 var rows = this.el.select('tbody > tr', true).elements;
6739 if(typeof(rows[index]) != 'undefined'){
6740 bt.removeChild(rows[index].dom);
6743 // if(bt.rows[index]){
6744 // bt.removeChild(bt.rows[index]);
6747 if(isUpdate !== true){
6748 //this.stripeRows(index);
6749 //this.syncRowHeights(index, index);
6751 this.fireEvent("rowremoved", this, index, record);
6755 onAdd : function(ds, records, rowIndex)
6757 //Roo.log('on Add called');
6758 // - note this does not handle multiple adding very well..
6759 var bt = this.mainBody.dom;
6760 for (var i =0 ; i < records.length;i++) {
6761 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762 //Roo.log(records[i]);
6763 //Roo.log(this.store.getAt(rowIndex+i));
6764 this.insertRow(this.store, rowIndex + i, false);
6771 refreshRow : function(record){
6772 var ds = this.store, index;
6773 if(typeof record == 'number'){
6775 record = ds.getAt(index);
6777 index = ds.indexOf(record);
6779 this.insertRow(ds, index, true);
6781 this.onRemove(ds, record, index+1, true);
6783 //this.syncRowHeights(index, index);
6785 this.fireEvent("rowupdated", this, index, record);
6788 insertRow : function(dm, rowIndex, isUpdate){
6791 this.fireEvent("beforerowsinserted", this, rowIndex);
6793 //var s = this.getScrollState();
6794 var row = this.renderRow(this.cm, this.store, rowIndex);
6795 // insert before rowIndex..
6796 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6800 if(row.cellObjects.length){
6801 Roo.each(row.cellObjects, function(r){
6802 _this.renderCellObject(r);
6807 this.fireEvent("rowsinserted", this, rowIndex);
6808 //this.syncRowHeights(firstRow, lastRow);
6809 //this.stripeRows(firstRow);
6816 getRowDom : function(rowIndex)
6818 var rows = this.el.select('tbody > tr', true).elements;
6820 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6823 // returns the object tree for a tr..
6826 renderRow : function(cm, ds, rowIndex)
6828 var d = ds.getAt(rowIndex);
6832 cls : 'x-row-' + rowIndex,
6836 var cellObjects = [];
6838 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839 var config = cm.config[i];
6841 var renderer = cm.getRenderer(i);
6845 if(typeof(renderer) !== 'undefined'){
6846 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849 // and are rendered into the cells after the row is rendered - using the id for the element.
6851 if(typeof(value) === 'object'){
6861 rowIndex : rowIndex,
6866 this.fireEvent('rowclass', this, rowcfg);
6870 cls : rowcfg.rowClass + ' x-col-' + i,
6872 html: (typeof(value) === 'object') ? '' : value
6879 if(typeof(config.colspan) != 'undefined'){
6880 td.colspan = config.colspan;
6883 if(typeof(config.hidden) != 'undefined' && config.hidden){
6884 td.style += ' display:none;';
6887 if(typeof(config.align) != 'undefined' && config.align.length){
6888 td.style += ' text-align:' + config.align + ';';
6890 if(typeof(config.valign) != 'undefined' && config.valign.length){
6891 td.style += ' vertical-align:' + config.valign + ';';
6894 if(typeof(config.width) != 'undefined'){
6895 td.style += ' width:' + config.width + 'px;';
6898 if(typeof(config.cursor) != 'undefined'){
6899 td.style += ' cursor:' + config.cursor + ';';
6902 if(typeof(config.cls) != 'undefined'){
6903 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6906 ['xs','sm','md','lg'].map(function(size){
6908 if(typeof(config[size]) == 'undefined'){
6912 if (!config[size]) { // 0 = hidden
6913 td.cls += ' hidden-' + size;
6917 td.cls += ' col-' + size + '-' + config[size];
6925 row.cellObjects = cellObjects;
6933 onBeforeLoad : function()
6942 this.el.select('tbody', true).first().dom.innerHTML = '';
6945 * Show or hide a row.
6946 * @param {Number} rowIndex to show or hide
6947 * @param {Boolean} state hide
6949 setRowVisibility : function(rowIndex, state)
6951 var bt = this.mainBody.dom;
6953 var rows = this.el.select('tbody > tr', true).elements;
6955 if(typeof(rows[rowIndex]) == 'undefined'){
6958 rows[rowIndex].dom.style.display = state ? '' : 'none';
6962 getSelectionModel : function(){
6964 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966 return this.selModel;
6969 * Render the Roo.bootstrap object from renderder
6971 renderCellObject : function(r)
6975 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977 var t = r.cfg.render(r.container);
6980 Roo.each(r.cfg.cn, function(c){
6982 container: t.getChildContainer(),
6985 _this.renderCellObject(child);
6990 getRowIndex : function(row)
6994 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7005 * Returns the grid's underlying element = used by panel.Grid
7006 * @return {Element} The element
7008 getGridEl : function(){
7012 * Forces a resize - used by panel.Grid
7013 * @return {Element} The element
7015 autoSize : function()
7017 //var ctr = Roo.get(this.container.dom.parentElement);
7018 var ctr = Roo.get(this.el.dom);
7020 var thd = this.getGridEl().select('thead',true).first();
7021 var tbd = this.getGridEl().select('tbody', true).first();
7022 var tfd = this.getGridEl().select('tfoot', true).first();
7024 var cw = ctr.getWidth();
7028 tbd.setSize(ctr.getWidth(),
7029 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7034 cw = Math.max(cw, this.totalWidth);
7035 this.getGridEl().select('tr',true).setWidth(cw);
7036 // resize 'expandable coloumn?
7038 return; // we doe not have a view in this design..
7041 onBodyScroll: function()
7043 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045 this.mainHead.setStyle({
7046 'position' : 'relative',
7047 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7053 var scrollHeight = this.mainBody.dom.scrollHeight;
7055 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057 var height = this.mainBody.getHeight();
7059 if(scrollHeight - height == scrollTop) {
7061 var total = this.ds.getTotalCount();
7063 if(this.footer.cursor + this.footer.pageSize < total){
7065 this.footer.ds.load({
7067 start : this.footer.cursor + this.footer.pageSize,
7068 limit : this.footer.pageSize
7078 onHeaderChange : function()
7080 var header = this.renderHeader();
7081 var table = this.el.select('table', true).first();
7083 this.mainHead.remove();
7084 this.mainHead = table.createChild(header, this.mainBody, false);
7087 onHiddenChange : function(colModel, colIndex, hidden)
7089 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092 this.CSS.updateRule(thSelector, "display", "");
7093 this.CSS.updateRule(tdSelector, "display", "");
7096 this.CSS.updateRule(thSelector, "display", "none");
7097 this.CSS.updateRule(tdSelector, "display", "none");
7100 this.onHeaderChange();
7104 setColumnWidth: function(col_index, width)
7106 // width = "md-2 xs-2..."
7107 if(!this.colModel.config[col_index]) {
7111 var w = width.split(" ");
7127 * @class Roo.bootstrap.TableCell
7128 * @extends Roo.bootstrap.Component
7129 * Bootstrap TableCell class
7130 * @cfg {String} html cell contain text
7131 * @cfg {String} cls cell class
7132 * @cfg {String} tag cell tag (td|th) default td
7133 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7134 * @cfg {String} align Aligns the content in a cell
7135 * @cfg {String} axis Categorizes cells
7136 * @cfg {String} bgcolor Specifies the background color of a cell
7137 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7138 * @cfg {Number} colspan Specifies the number of columns a cell should span
7139 * @cfg {String} headers Specifies one or more header cells a cell is related to
7140 * @cfg {Number} height Sets the height of a cell
7141 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7142 * @cfg {Number} rowspan Sets the number of rows a cell should span
7143 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7144 * @cfg {String} valign Vertical aligns the content in a cell
7145 * @cfg {Number} width Specifies the width of a cell
7148 * Create a new TableCell
7149 * @param {Object} config The config object
7152 Roo.bootstrap.TableCell = function(config){
7153 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7156 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7176 getAutoCreate : function(){
7177 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7197 cfg.align=this.align
7203 cfg.bgcolor=this.bgcolor
7206 cfg.charoff=this.charoff
7209 cfg.colspan=this.colspan
7212 cfg.headers=this.headers
7215 cfg.height=this.height
7218 cfg.nowrap=this.nowrap
7221 cfg.rowspan=this.rowspan
7224 cfg.scope=this.scope
7227 cfg.valign=this.valign
7230 cfg.width=this.width
7249 * @class Roo.bootstrap.TableRow
7250 * @extends Roo.bootstrap.Component
7251 * Bootstrap TableRow class
7252 * @cfg {String} cls row class
7253 * @cfg {String} align Aligns the content in a table row
7254 * @cfg {String} bgcolor Specifies a background color for a table row
7255 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7256 * @cfg {String} valign Vertical aligns the content in a table row
7259 * Create a new TableRow
7260 * @param {Object} config The config object
7263 Roo.bootstrap.TableRow = function(config){
7264 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7267 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7275 getAutoCreate : function(){
7276 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7286 cfg.align = this.align;
7289 cfg.bgcolor = this.bgcolor;
7292 cfg.charoff = this.charoff;
7295 cfg.valign = this.valign;
7313 * @class Roo.bootstrap.TableBody
7314 * @extends Roo.bootstrap.Component
7315 * Bootstrap TableBody class
7316 * @cfg {String} cls element class
7317 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7318 * @cfg {String} align Aligns the content inside the element
7319 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7320 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7323 * Create a new TableBody
7324 * @param {Object} config The config object
7327 Roo.bootstrap.TableBody = function(config){
7328 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7331 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7339 getAutoCreate : function(){
7340 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7354 cfg.align = this.align;
7357 cfg.charoff = this.charoff;
7360 cfg.valign = this.valign;
7367 // initEvents : function()
7374 // this.store = Roo.factory(this.store, Roo.data);
7375 // this.store.on('load', this.onLoad, this);
7377 // this.store.load();
7381 // onLoad: function ()
7383 // this.fireEvent('load', this);
7393 * Ext JS Library 1.1.1
7394 * Copyright(c) 2006-2007, Ext JS, LLC.
7396 * Originally Released Under LGPL - original licence link has changed is not relivant.
7399 * <script type="text/javascript">
7402 // as we use this in bootstrap.
7403 Roo.namespace('Roo.form');
7405 * @class Roo.form.Action
7406 * Internal Class used to handle form actions
7408 * @param {Roo.form.BasicForm} el The form element or its id
7409 * @param {Object} config Configuration options
7414 // define the action interface
7415 Roo.form.Action = function(form, options){
7417 this.options = options || {};
7420 * Client Validation Failed
7423 Roo.form.Action.CLIENT_INVALID = 'client';
7425 * Server Validation Failed
7428 Roo.form.Action.SERVER_INVALID = 'server';
7430 * Connect to Server Failed
7433 Roo.form.Action.CONNECT_FAILURE = 'connect';
7435 * Reading Data from Server Failed
7438 Roo.form.Action.LOAD_FAILURE = 'load';
7440 Roo.form.Action.prototype = {
7442 failureType : undefined,
7443 response : undefined,
7447 run : function(options){
7452 success : function(response){
7457 handleResponse : function(response){
7461 // default connection failure
7462 failure : function(response){
7464 this.response = response;
7465 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7466 this.form.afterAction(this, false);
7469 processResponse : function(response){
7470 this.response = response;
7471 if(!response.responseText){
7474 this.result = this.handleResponse(response);
7478 // utility functions used internally
7479 getUrl : function(appendParams){
7480 var url = this.options.url || this.form.url || this.form.el.dom.action;
7482 var p = this.getParams();
7484 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7490 getMethod : function(){
7491 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7494 getParams : function(){
7495 var bp = this.form.baseParams;
7496 var p = this.options.params;
7498 if(typeof p == "object"){
7499 p = Roo.urlEncode(Roo.applyIf(p, bp));
7500 }else if(typeof p == 'string' && bp){
7501 p += '&' + Roo.urlEncode(bp);
7504 p = Roo.urlEncode(bp);
7509 createCallback : function(){
7511 success: this.success,
7512 failure: this.failure,
7514 timeout: (this.form.timeout*1000),
7515 upload: this.form.fileUpload ? this.success : undefined
7520 Roo.form.Action.Submit = function(form, options){
7521 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7524 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7527 haveProgress : false,
7528 uploadComplete : false,
7530 // uploadProgress indicator.
7531 uploadProgress : function()
7533 if (!this.form.progressUrl) {
7537 if (!this.haveProgress) {
7538 Roo.MessageBox.progress("Uploading", "Uploading");
7540 if (this.uploadComplete) {
7541 Roo.MessageBox.hide();
7545 this.haveProgress = true;
7547 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7549 var c = new Roo.data.Connection();
7551 url : this.form.progressUrl,
7556 success : function(req){
7557 //console.log(data);
7561 rdata = Roo.decode(req.responseText)
7563 Roo.log("Invalid data from server..");
7567 if (!rdata || !rdata.success) {
7569 Roo.MessageBox.alert(Roo.encode(rdata));
7572 var data = rdata.data;
7574 if (this.uploadComplete) {
7575 Roo.MessageBox.hide();
7580 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7581 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7584 this.uploadProgress.defer(2000,this);
7587 failure: function(data) {
7588 Roo.log('progress url failed ');
7599 // run get Values on the form, so it syncs any secondary forms.
7600 this.form.getValues();
7602 var o = this.options;
7603 var method = this.getMethod();
7604 var isPost = method == 'POST';
7605 if(o.clientValidation === false || this.form.isValid()){
7607 if (this.form.progressUrl) {
7608 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7609 (new Date() * 1) + '' + Math.random());
7614 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7615 form:this.form.el.dom,
7616 url:this.getUrl(!isPost),
7618 params:isPost ? this.getParams() : null,
7619 isUpload: this.form.fileUpload
7622 this.uploadProgress();
7624 }else if (o.clientValidation !== false){ // client validation failed
7625 this.failureType = Roo.form.Action.CLIENT_INVALID;
7626 this.form.afterAction(this, false);
7630 success : function(response)
7632 this.uploadComplete= true;
7633 if (this.haveProgress) {
7634 Roo.MessageBox.hide();
7638 var result = this.processResponse(response);
7639 if(result === true || result.success){
7640 this.form.afterAction(this, true);
7644 this.form.markInvalid(result.errors);
7645 this.failureType = Roo.form.Action.SERVER_INVALID;
7647 this.form.afterAction(this, false);
7649 failure : function(response)
7651 this.uploadComplete= true;
7652 if (this.haveProgress) {
7653 Roo.MessageBox.hide();
7656 this.response = response;
7657 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7658 this.form.afterAction(this, false);
7661 handleResponse : function(response){
7662 if(this.form.errorReader){
7663 var rs = this.form.errorReader.read(response);
7666 for(var i = 0, len = rs.records.length; i < len; i++) {
7667 var r = rs.records[i];
7671 if(errors.length < 1){
7675 success : rs.success,
7681 ret = Roo.decode(response.responseText);
7685 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7695 Roo.form.Action.Load = function(form, options){
7696 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7697 this.reader = this.form.reader;
7700 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7705 Roo.Ajax.request(Roo.apply(
7706 this.createCallback(), {
7707 method:this.getMethod(),
7708 url:this.getUrl(false),
7709 params:this.getParams()
7713 success : function(response){
7715 var result = this.processResponse(response);
7716 if(result === true || !result.success || !result.data){
7717 this.failureType = Roo.form.Action.LOAD_FAILURE;
7718 this.form.afterAction(this, false);
7721 this.form.clearInvalid();
7722 this.form.setValues(result.data);
7723 this.form.afterAction(this, true);
7726 handleResponse : function(response){
7727 if(this.form.reader){
7728 var rs = this.form.reader.read(response);
7729 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7731 success : rs.success,
7735 return Roo.decode(response.responseText);
7739 Roo.form.Action.ACTION_TYPES = {
7740 'load' : Roo.form.Action.Load,
7741 'submit' : Roo.form.Action.Submit
7750 * @class Roo.bootstrap.Form
7751 * @extends Roo.bootstrap.Component
7752 * Bootstrap Form class
7753 * @cfg {String} method GET | POST (default POST)
7754 * @cfg {String} labelAlign top | left (default top)
7755 * @cfg {String} align left | right - for navbars
7756 * @cfg {Boolean} loadMask load mask when submit (default true)
7761 * @param {Object} config The config object
7765 Roo.bootstrap.Form = function(config){
7767 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7769 Roo.bootstrap.Form.popover.apply();
7773 * @event clientvalidation
7774 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7775 * @param {Form} this
7776 * @param {Boolean} valid true if the form has passed client-side validation
7778 clientvalidation: true,
7780 * @event beforeaction
7781 * Fires before any action is performed. Return false to cancel the action.
7782 * @param {Form} this
7783 * @param {Action} action The action to be performed
7787 * @event actionfailed
7788 * Fires when an action fails.
7789 * @param {Form} this
7790 * @param {Action} action The action that failed
7792 actionfailed : true,
7794 * @event actioncomplete
7795 * Fires when an action is completed.
7796 * @param {Form} this
7797 * @param {Action} action The action that completed
7799 actioncomplete : true
7803 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7806 * @cfg {String} method
7807 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7812 * The URL to use for form actions if one isn't supplied in the action options.
7815 * @cfg {Boolean} fileUpload
7816 * Set to true if this form is a file upload.
7820 * @cfg {Object} baseParams
7821 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7825 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7829 * @cfg {Sting} align (left|right) for navbar forms
7834 activeAction : null,
7837 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7838 * element by passing it or its id or mask the form itself by passing in true.
7841 waitMsgTarget : false,
7846 * @cfg {Boolean} errorMask (true|false) default false
7851 * @cfg {Number} maskOffset Default 100
7856 * @cfg {Boolean} maskBody
7860 getAutoCreate : function(){
7864 method : this.method || 'POST',
7865 id : this.id || Roo.id(),
7868 if (this.parent().xtype.match(/^Nav/)) {
7869 cfg.cls = 'navbar-form navbar-' + this.align;
7873 if (this.labelAlign == 'left' ) {
7874 cfg.cls += ' form-horizontal';
7880 initEvents : function()
7882 this.el.on('submit', this.onSubmit, this);
7883 // this was added as random key presses on the form where triggering form submit.
7884 this.el.on('keypress', function(e) {
7885 if (e.getCharCode() != 13) {
7888 // we might need to allow it for textareas.. and some other items.
7889 // check e.getTarget().
7891 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7895 Roo.log("keypress blocked");
7903 onSubmit : function(e){
7908 * Returns true if client-side validation on the form is successful.
7911 isValid : function(){
7912 var items = this.getItems();
7916 items.each(function(f){
7922 Roo.log('invalid field: ' + f.name);
7926 if(!target && f.el.isVisible(true)){
7932 if(this.errorMask && !valid){
7933 Roo.bootstrap.Form.popover.mask(this, target);
7940 * Returns true if any fields in this form have changed since their original load.
7943 isDirty : function(){
7945 var items = this.getItems();
7946 items.each(function(f){
7956 * Performs a predefined action (submit or load) or custom actions you define on this form.
7957 * @param {String} actionName The name of the action type
7958 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7959 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7960 * accept other config options):
7962 Property Type Description
7963 ---------------- --------------- ----------------------------------------------------------------------------------
7964 url String The url for the action (defaults to the form's url)
7965 method String The form method to use (defaults to the form's method, or POST if not defined)
7966 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7967 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7968 validate the form on the client (defaults to false)
7970 * @return {BasicForm} this
7972 doAction : function(action, options){
7973 if(typeof action == 'string'){
7974 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7976 if(this.fireEvent('beforeaction', this, action) !== false){
7977 this.beforeAction(action);
7978 action.run.defer(100, action);
7984 beforeAction : function(action){
7985 var o = action.options;
7990 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7992 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7995 // not really supported yet.. ??
7997 //if(this.waitMsgTarget === true){
7998 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7999 //}else if(this.waitMsgTarget){
8000 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8001 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8003 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8009 afterAction : function(action, success){
8010 this.activeAction = null;
8011 var o = action.options;
8016 Roo.get(document.body).unmask();
8022 //if(this.waitMsgTarget === true){
8023 // this.el.unmask();
8024 //}else if(this.waitMsgTarget){
8025 // this.waitMsgTarget.unmask();
8027 // Roo.MessageBox.updateProgress(1);
8028 // Roo.MessageBox.hide();
8035 Roo.callback(o.success, o.scope, [this, action]);
8036 this.fireEvent('actioncomplete', this, action);
8040 // failure condition..
8041 // we have a scenario where updates need confirming.
8042 // eg. if a locking scenario exists..
8043 // we look for { errors : { needs_confirm : true }} in the response.
8045 (typeof(action.result) != 'undefined') &&
8046 (typeof(action.result.errors) != 'undefined') &&
8047 (typeof(action.result.errors.needs_confirm) != 'undefined')
8050 Roo.log("not supported yet");
8053 Roo.MessageBox.confirm(
8054 "Change requires confirmation",
8055 action.result.errorMsg,
8060 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8070 Roo.callback(o.failure, o.scope, [this, action]);
8071 // show an error message if no failed handler is set..
8072 if (!this.hasListener('actionfailed')) {
8073 Roo.log("need to add dialog support");
8075 Roo.MessageBox.alert("Error",
8076 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8077 action.result.errorMsg :
8078 "Saving Failed, please check your entries or try again"
8083 this.fireEvent('actionfailed', this, action);
8088 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8089 * @param {String} id The value to search for
8092 findField : function(id){
8093 var items = this.getItems();
8094 var field = items.get(id);
8096 items.each(function(f){
8097 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8104 return field || null;
8107 * Mark fields in this form invalid in bulk.
8108 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8109 * @return {BasicForm} this
8111 markInvalid : function(errors){
8112 if(errors instanceof Array){
8113 for(var i = 0, len = errors.length; i < len; i++){
8114 var fieldError = errors[i];
8115 var f = this.findField(fieldError.id);
8117 f.markInvalid(fieldError.msg);
8123 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8124 field.markInvalid(errors[id]);
8128 //Roo.each(this.childForms || [], function (f) {
8129 // f.markInvalid(errors);
8136 * Set values for fields in this form in bulk.
8137 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8138 * @return {BasicForm} this
8140 setValues : function(values){
8141 if(values instanceof Array){ // array of objects
8142 for(var i = 0, len = values.length; i < len; i++){
8144 var f = this.findField(v.id);
8146 f.setValue(v.value);
8147 if(this.trackResetOnLoad){
8148 f.originalValue = f.getValue();
8152 }else{ // object hash
8155 if(typeof values[id] != 'function' && (field = this.findField(id))){
8157 if (field.setFromData &&
8159 field.displayField &&
8160 // combos' with local stores can
8161 // be queried via setValue()
8162 // to set their value..
8163 (field.store && !field.store.isLocal)
8167 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8168 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8169 field.setFromData(sd);
8171 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8173 field.setFromData(values);
8176 field.setValue(values[id]);
8180 if(this.trackResetOnLoad){
8181 field.originalValue = field.getValue();
8187 //Roo.each(this.childForms || [], function (f) {
8188 // f.setValues(values);
8195 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8196 * they are returned as an array.
8197 * @param {Boolean} asString
8200 getValues : function(asString){
8201 //if (this.childForms) {
8202 // copy values from the child forms
8203 // Roo.each(this.childForms, function (f) {
8204 // this.setValues(f.getValues());
8210 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8211 if(asString === true){
8214 return Roo.urlDecode(fs);
8218 * Returns the fields in this form as an object with key/value pairs.
8219 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8222 getFieldValues : function(with_hidden)
8224 var items = this.getItems();
8226 items.each(function(f){
8232 var v = f.getValue();
8234 if (f.inputType =='radio') {
8235 if (typeof(ret[f.getName()]) == 'undefined') {
8236 ret[f.getName()] = ''; // empty..
8239 if (!f.el.dom.checked) {
8247 if(f.xtype == 'MoneyField'){
8248 ret[f.currencyName] = f.getCurrency();
8251 // not sure if this supported any more..
8252 if ((typeof(v) == 'object') && f.getRawValue) {
8253 v = f.getRawValue() ; // dates..
8255 // combo boxes where name != hiddenName...
8256 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8257 ret[f.name] = f.getRawValue();
8259 ret[f.getName()] = v;
8266 * Clears all invalid messages in this form.
8267 * @return {BasicForm} this
8269 clearInvalid : function(){
8270 var items = this.getItems();
8272 items.each(function(f){
8281 * @return {BasicForm} this
8284 var items = this.getItems();
8285 items.each(function(f){
8289 Roo.each(this.childForms || [], function (f) {
8297 getItems : function()
8299 var r=new Roo.util.MixedCollection(false, function(o){
8300 return o.id || (o.id = Roo.id());
8302 var iter = function(el) {
8309 Roo.each(el.items,function(e) {
8318 hideFields : function(items)
8320 Roo.each(items, function(i){
8322 var f = this.findField(i);
8333 showFields : function(items)
8335 Roo.each(items, function(i){
8337 var f = this.findField(i);
8350 Roo.apply(Roo.bootstrap.Form, {
8377 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8378 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8379 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8380 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8383 this.maskEl.top.enableDisplayMode("block");
8384 this.maskEl.left.enableDisplayMode("block");
8385 this.maskEl.bottom.enableDisplayMode("block");
8386 this.maskEl.right.enableDisplayMode("block");
8388 this.toolTip = new Roo.bootstrap.Tooltip({
8389 cls : 'roo-form-error-popover',
8391 'left' : ['r-l', [-2,0], 'right'],
8392 'right' : ['l-r', [2,0], 'left'],
8393 'bottom' : ['tl-bl', [0,2], 'top'],
8394 'top' : [ 'bl-tl', [0,-2], 'bottom']
8398 this.toolTip.render(Roo.get(document.body));
8400 this.toolTip.el.enableDisplayMode("block");
8402 Roo.get(document.body).on('click', function(){
8406 Roo.get(document.body).on('touchstart', function(){
8410 this.isApplied = true
8413 mask : function(form, target)
8417 this.target = target;
8419 if(!this.form.errorMask || !target.el){
8423 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8425 Roo.log(scrollable);
8427 var ot = this.target.el.calcOffsetsTo(scrollable);
8429 var scrollTo = ot[1] - this.form.maskOffset;
8431 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8433 scrollable.scrollTo('top', scrollTo);
8435 var box = this.target.el.getBox();
8437 var zIndex = Roo.bootstrap.Modal.zIndex++;
8440 this.maskEl.top.setStyle('position', 'absolute');
8441 this.maskEl.top.setStyle('z-index', zIndex);
8442 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8443 this.maskEl.top.setLeft(0);
8444 this.maskEl.top.setTop(0);
8445 this.maskEl.top.show();
8447 this.maskEl.left.setStyle('position', 'absolute');
8448 this.maskEl.left.setStyle('z-index', zIndex);
8449 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8450 this.maskEl.left.setLeft(0);
8451 this.maskEl.left.setTop(box.y - this.padding);
8452 this.maskEl.left.show();
8454 this.maskEl.bottom.setStyle('position', 'absolute');
8455 this.maskEl.bottom.setStyle('z-index', zIndex);
8456 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8457 this.maskEl.bottom.setLeft(0);
8458 this.maskEl.bottom.setTop(box.bottom + this.padding);
8459 this.maskEl.bottom.show();
8461 this.maskEl.right.setStyle('position', 'absolute');
8462 this.maskEl.right.setStyle('z-index', zIndex);
8463 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8464 this.maskEl.right.setLeft(box.right + this.padding);
8465 this.maskEl.right.setTop(box.y - this.padding);
8466 this.maskEl.right.show();
8468 this.toolTip.bindEl = this.target.el;
8470 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8472 var tip = this.target.blankText;
8474 if(this.target.getValue() !== '' ) {
8476 if (this.target.invalidText.length) {
8477 tip = this.target.invalidText;
8478 } else if (this.target.regexText.length){
8479 tip = this.target.regexText;
8483 this.toolTip.show(tip);
8485 this.intervalID = window.setInterval(function() {
8486 Roo.bootstrap.Form.popover.unmask();
8489 window.onwheel = function(){ return false;};
8491 (function(){ this.isMasked = true; }).defer(500, this);
8497 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8501 this.maskEl.top.setStyle('position', 'absolute');
8502 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8503 this.maskEl.top.hide();
8505 this.maskEl.left.setStyle('position', 'absolute');
8506 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8507 this.maskEl.left.hide();
8509 this.maskEl.bottom.setStyle('position', 'absolute');
8510 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8511 this.maskEl.bottom.hide();
8513 this.maskEl.right.setStyle('position', 'absolute');
8514 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8515 this.maskEl.right.hide();
8517 this.toolTip.hide();
8519 this.toolTip.el.hide();
8521 window.onwheel = function(){ return true;};
8523 if(this.intervalID){
8524 window.clearInterval(this.intervalID);
8525 this.intervalID = false;
8528 this.isMasked = false;
8538 * Ext JS Library 1.1.1
8539 * Copyright(c) 2006-2007, Ext JS, LLC.
8541 * Originally Released Under LGPL - original licence link has changed is not relivant.
8544 * <script type="text/javascript">
8547 * @class Roo.form.VTypes
8548 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8551 Roo.form.VTypes = function(){
8552 // closure these in so they are only created once.
8553 var alpha = /^[a-zA-Z_]+$/;
8554 var alphanum = /^[a-zA-Z0-9_]+$/;
8555 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8556 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8558 // All these messages and functions are configurable
8561 * The function used to validate email addresses
8562 * @param {String} value The email address
8564 'email' : function(v){
8565 return email.test(v);
8568 * The error text to display when the email validation function returns false
8571 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8573 * The keystroke filter mask to be applied on email input
8576 'emailMask' : /[a-z0-9_\.\-@]/i,
8579 * The function used to validate URLs
8580 * @param {String} value The URL
8582 'url' : function(v){
8586 * The error text to display when the url validation function returns false
8589 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8592 * The function used to validate alpha values
8593 * @param {String} value The value
8595 'alpha' : function(v){
8596 return alpha.test(v);
8599 * The error text to display when the alpha validation function returns false
8602 'alphaText' : 'This field should only contain letters and _',
8604 * The keystroke filter mask to be applied on alpha input
8607 'alphaMask' : /[a-z_]/i,
8610 * The function used to validate alphanumeric values
8611 * @param {String} value The value
8613 'alphanum' : function(v){
8614 return alphanum.test(v);
8617 * The error text to display when the alphanumeric validation function returns false
8620 'alphanumText' : 'This field should only contain letters, numbers and _',
8622 * The keystroke filter mask to be applied on alphanumeric input
8625 'alphanumMask' : /[a-z0-9_]/i
8635 * @class Roo.bootstrap.Input
8636 * @extends Roo.bootstrap.Component
8637 * Bootstrap Input class
8638 * @cfg {Boolean} disabled is it disabled
8639 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8640 * @cfg {String} name name of the input
8641 * @cfg {string} fieldLabel - the label associated
8642 * @cfg {string} placeholder - placeholder to put in text.
8643 * @cfg {string} before - input group add on before
8644 * @cfg {string} after - input group add on after
8645 * @cfg {string} size - (lg|sm) or leave empty..
8646 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8647 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8648 * @cfg {Number} md colspan out of 12 for computer-sized screens
8649 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8650 * @cfg {string} value default value of the input
8651 * @cfg {Number} labelWidth set the width of label
8652 * @cfg {Number} labellg set the width of label (1-12)
8653 * @cfg {Number} labelmd set the width of label (1-12)
8654 * @cfg {Number} labelsm set the width of label (1-12)
8655 * @cfg {Number} labelxs set the width of label (1-12)
8656 * @cfg {String} labelAlign (top|left)
8657 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8658 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8659 * @cfg {String} indicatorpos (left|right) default left
8660 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8661 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8663 * @cfg {String} align (left|center|right) Default left
8664 * @cfg {Boolean} forceFeedback (true|false) Default false
8667 * Create a new Input
8668 * @param {Object} config The config object
8671 Roo.bootstrap.Input = function(config){
8673 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8678 * Fires when this field receives input focus.
8679 * @param {Roo.form.Field} this
8684 * Fires when this field loses input focus.
8685 * @param {Roo.form.Field} this
8690 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8691 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8692 * @param {Roo.form.Field} this
8693 * @param {Roo.EventObject} e The event object
8698 * Fires just before the field blurs if the field value has changed.
8699 * @param {Roo.form.Field} this
8700 * @param {Mixed} newValue The new value
8701 * @param {Mixed} oldValue The original value
8706 * Fires after the field has been marked as invalid.
8707 * @param {Roo.form.Field} this
8708 * @param {String} msg The validation message
8713 * Fires after the field has been validated with no errors.
8714 * @param {Roo.form.Field} this
8719 * Fires after the key up
8720 * @param {Roo.form.Field} this
8721 * @param {Roo.EventObject} e The event Object
8727 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8729 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8730 automatic validation (defaults to "keyup").
8732 validationEvent : "keyup",
8734 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8736 validateOnBlur : true,
8738 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8740 validationDelay : 250,
8742 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8744 focusClass : "x-form-focus", // not needed???
8748 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8750 invalidClass : "has-warning",
8753 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8755 validClass : "has-success",
8758 * @cfg {Boolean} hasFeedback (true|false) default true
8763 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8765 invalidFeedbackClass : "glyphicon-warning-sign",
8768 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8770 validFeedbackClass : "glyphicon-ok",
8773 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8775 selectOnFocus : false,
8778 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8782 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8787 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8789 disableKeyFilter : false,
8792 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8796 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8800 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8802 blankText : "Please complete this mandatory field",
8805 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8809 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8811 maxLength : Number.MAX_VALUE,
8813 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8815 minLengthText : "The minimum length for this field is {0}",
8817 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8819 maxLengthText : "The maximum length for this field is {0}",
8823 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8824 * If available, this function will be called only after the basic validators all return true, and will be passed the
8825 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8829 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8830 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8831 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8835 * @cfg {String} regexText -- Depricated - use Invalid Text
8840 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8846 autocomplete: false,
8865 formatedValue : false,
8866 forceFeedback : false,
8868 indicatorpos : 'left',
8878 parentLabelAlign : function()
8881 while (parent.parent()) {
8882 parent = parent.parent();
8883 if (typeof(parent.labelAlign) !='undefined') {
8884 return parent.labelAlign;
8891 getAutoCreate : function()
8893 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8899 if(this.inputType != 'hidden'){
8900 cfg.cls = 'form-group' //input-group
8906 type : this.inputType,
8908 cls : 'form-control',
8909 placeholder : this.placeholder || '',
8910 autocomplete : this.autocomplete || 'new-password'
8913 if(this.capture.length){
8914 input.capture = this.capture;
8917 if(this.accept.length){
8918 input.accept = this.accept + "/*";
8922 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8925 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8926 input.maxLength = this.maxLength;
8929 if (this.disabled) {
8930 input.disabled=true;
8933 if (this.readOnly) {
8934 input.readonly=true;
8938 input.name = this.name;
8942 input.cls += ' input-' + this.size;
8946 ['xs','sm','md','lg'].map(function(size){
8947 if (settings[size]) {
8948 cfg.cls += ' col-' + size + '-' + settings[size];
8952 var inputblock = input;
8956 cls: 'glyphicon form-control-feedback'
8959 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8962 cls : 'has-feedback',
8970 if (this.before || this.after) {
8973 cls : 'input-group',
8977 if (this.before && typeof(this.before) == 'string') {
8979 inputblock.cn.push({
8981 cls : 'roo-input-before input-group-addon',
8985 if (this.before && typeof(this.before) == 'object') {
8986 this.before = Roo.factory(this.before);
8988 inputblock.cn.push({
8990 cls : 'roo-input-before input-group-' +
8991 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8995 inputblock.cn.push(input);
8997 if (this.after && typeof(this.after) == 'string') {
8998 inputblock.cn.push({
9000 cls : 'roo-input-after input-group-addon',
9004 if (this.after && typeof(this.after) == 'object') {
9005 this.after = Roo.factory(this.after);
9007 inputblock.cn.push({
9009 cls : 'roo-input-after input-group-' +
9010 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9014 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9015 inputblock.cls += ' has-feedback';
9016 inputblock.cn.push(feedback);
9020 if (align ==='left' && this.fieldLabel.length) {
9022 cfg.cls += ' roo-form-group-label-left';
9027 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9028 tooltip : 'This field is required'
9033 cls : 'control-label',
9034 html : this.fieldLabel
9045 var labelCfg = cfg.cn[1];
9046 var contentCfg = cfg.cn[2];
9048 if(this.indicatorpos == 'right'){
9053 cls : 'control-label',
9057 html : this.fieldLabel
9061 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9062 tooltip : 'This field is required'
9075 labelCfg = cfg.cn[0];
9076 contentCfg = cfg.cn[1];
9080 if(this.labelWidth > 12){
9081 labelCfg.style = "width: " + this.labelWidth + 'px';
9084 if(this.labelWidth < 13 && this.labelmd == 0){
9085 this.labelmd = this.labelWidth;
9088 if(this.labellg > 0){
9089 labelCfg.cls += ' col-lg-' + this.labellg;
9090 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9093 if(this.labelmd > 0){
9094 labelCfg.cls += ' col-md-' + this.labelmd;
9095 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9098 if(this.labelsm > 0){
9099 labelCfg.cls += ' col-sm-' + this.labelsm;
9100 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9103 if(this.labelxs > 0){
9104 labelCfg.cls += ' col-xs-' + this.labelxs;
9105 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9109 } else if ( this.fieldLabel.length) {
9114 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9115 tooltip : 'This field is required'
9119 //cls : 'input-group-addon',
9120 html : this.fieldLabel
9128 if(this.indicatorpos == 'right'){
9133 //cls : 'input-group-addon',
9134 html : this.fieldLabel
9139 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9140 tooltip : 'This field is required'
9160 if (this.parentType === 'Navbar' && this.parent().bar) {
9161 cfg.cls += ' navbar-form';
9164 if (this.parentType === 'NavGroup') {
9165 cfg.cls += ' navbar-form';
9173 * return the real input element.
9175 inputEl: function ()
9177 return this.el.select('input.form-control',true).first();
9180 tooltipEl : function()
9182 return this.inputEl();
9185 indicatorEl : function()
9187 var indicator = this.el.select('i.roo-required-indicator',true).first();
9197 setDisabled : function(v)
9199 var i = this.inputEl().dom;
9201 i.removeAttribute('disabled');
9205 i.setAttribute('disabled','true');
9207 initEvents : function()
9210 this.inputEl().on("keydown" , this.fireKey, this);
9211 this.inputEl().on("focus", this.onFocus, this);
9212 this.inputEl().on("blur", this.onBlur, this);
9214 this.inputEl().relayEvent('keyup', this);
9216 this.indicator = this.indicatorEl();
9219 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9222 // reference to original value for reset
9223 this.originalValue = this.getValue();
9224 //Roo.form.TextField.superclass.initEvents.call(this);
9225 if(this.validationEvent == 'keyup'){
9226 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9227 this.inputEl().on('keyup', this.filterValidation, this);
9229 else if(this.validationEvent !== false){
9230 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9233 if(this.selectOnFocus){
9234 this.on("focus", this.preFocus, this);
9237 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9238 this.inputEl().on("keypress", this.filterKeys, this);
9240 this.inputEl().relayEvent('keypress', this);
9243 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9244 this.el.on("click", this.autoSize, this);
9247 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9248 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9251 if (typeof(this.before) == 'object') {
9252 this.before.render(this.el.select('.roo-input-before',true).first());
9254 if (typeof(this.after) == 'object') {
9255 this.after.render(this.el.select('.roo-input-after',true).first());
9258 this.inputEl().on('change', this.onChange, this);
9261 filterValidation : function(e){
9262 if(!e.isNavKeyPress()){
9263 this.validationTask.delay(this.validationDelay);
9267 * Validates the field value
9268 * @return {Boolean} True if the value is valid, else false
9270 validate : function(){
9271 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9272 if(this.disabled || this.validateValue(this.getRawValue())){
9283 * Validates a value according to the field's validation rules and marks the field as invalid
9284 * if the validation fails
9285 * @param {Mixed} value The value to validate
9286 * @return {Boolean} True if the value is valid, else false
9288 validateValue : function(value)
9290 if(this.getVisibilityEl().hasClass('hidden')){
9294 if(value.length < 1) { // if it's blank
9295 if(this.allowBlank){
9301 if(value.length < this.minLength){
9304 if(value.length > this.maxLength){
9308 var vt = Roo.form.VTypes;
9309 if(!vt[this.vtype](value, this)){
9313 if(typeof this.validator == "function"){
9314 var msg = this.validator(value);
9318 if (typeof(msg) == 'string') {
9319 this.invalidText = msg;
9323 if(this.regex && !this.regex.test(value)){
9331 fireKey : function(e){
9332 //Roo.log('field ' + e.getKey());
9333 if(e.isNavKeyPress()){
9334 this.fireEvent("specialkey", this, e);
9337 focus : function (selectText){
9339 this.inputEl().focus();
9340 if(selectText === true){
9341 this.inputEl().dom.select();
9347 onFocus : function(){
9348 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9349 // this.el.addClass(this.focusClass);
9352 this.hasFocus = true;
9353 this.startValue = this.getValue();
9354 this.fireEvent("focus", this);
9358 beforeBlur : Roo.emptyFn,
9362 onBlur : function(){
9364 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9365 //this.el.removeClass(this.focusClass);
9367 this.hasFocus = false;
9368 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9371 var v = this.getValue();
9372 if(String(v) !== String(this.startValue)){
9373 this.fireEvent('change', this, v, this.startValue);
9375 this.fireEvent("blur", this);
9378 onChange : function(e)
9380 var v = this.getValue();
9381 if(String(v) !== String(this.startValue)){
9382 this.fireEvent('change', this, v, this.startValue);
9388 * Resets the current field value to the originally loaded value and clears any validation messages
9391 this.setValue(this.originalValue);
9395 * Returns the name of the field
9396 * @return {Mixed} name The name field
9398 getName: function(){
9402 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9403 * @return {Mixed} value The field value
9405 getValue : function(){
9407 var v = this.inputEl().getValue();
9412 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9413 * @return {Mixed} value The field value
9415 getRawValue : function(){
9416 var v = this.inputEl().getValue();
9422 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9423 * @param {Mixed} value The value to set
9425 setRawValue : function(v){
9426 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9429 selectText : function(start, end){
9430 var v = this.getRawValue();
9432 start = start === undefined ? 0 : start;
9433 end = end === undefined ? v.length : end;
9434 var d = this.inputEl().dom;
9435 if(d.setSelectionRange){
9436 d.setSelectionRange(start, end);
9437 }else if(d.createTextRange){
9438 var range = d.createTextRange();
9439 range.moveStart("character", start);
9440 range.moveEnd("character", v.length-end);
9447 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9448 * @param {Mixed} value The value to set
9450 setValue : function(v){
9453 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9459 processValue : function(value){
9460 if(this.stripCharsRe){
9461 var newValue = value.replace(this.stripCharsRe, '');
9462 if(newValue !== value){
9463 this.setRawValue(newValue);
9470 preFocus : function(){
9472 if(this.selectOnFocus){
9473 this.inputEl().dom.select();
9476 filterKeys : function(e){
9478 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9481 var c = e.getCharCode(), cc = String.fromCharCode(c);
9482 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9485 if(!this.maskRe.test(cc)){
9490 * Clear any invalid styles/messages for this field
9492 clearInvalid : function(){
9494 if(!this.el || this.preventMark){ // not rendered
9499 this.el.removeClass(this.invalidClass);
9501 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9503 var feedback = this.el.select('.form-control-feedback', true).first();
9506 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9512 this.indicator.removeClass('visible');
9513 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9516 this.fireEvent('valid', this);
9520 * Mark this field as valid
9522 markValid : function()
9524 if(!this.el || this.preventMark){ // not rendered...
9528 this.el.removeClass([this.invalidClass, this.validClass]);
9530 var feedback = this.el.select('.form-control-feedback', true).first();
9533 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9537 this.indicator.removeClass('visible');
9538 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9545 if(this.allowBlank && !this.getRawValue().length){
9549 this.el.addClass(this.validClass);
9551 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9553 var feedback = this.el.select('.form-control-feedback', true).first();
9556 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9557 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9562 this.fireEvent('valid', this);
9566 * Mark this field as invalid
9567 * @param {String} msg The validation message
9569 markInvalid : function(msg)
9571 if(!this.el || this.preventMark){ // not rendered
9575 this.el.removeClass([this.invalidClass, this.validClass]);
9577 var feedback = this.el.select('.form-control-feedback', true).first();
9580 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9587 if(this.allowBlank && !this.getRawValue().length){
9592 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9593 this.indicator.addClass('visible');
9596 this.el.addClass(this.invalidClass);
9598 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9600 var feedback = this.el.select('.form-control-feedback', true).first();
9603 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9605 if(this.getValue().length || this.forceFeedback){
9606 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9613 this.fireEvent('invalid', this, msg);
9616 SafariOnKeyDown : function(event)
9618 // this is a workaround for a password hang bug on chrome/ webkit.
9619 if (this.inputEl().dom.type != 'password') {
9623 var isSelectAll = false;
9625 if(this.inputEl().dom.selectionEnd > 0){
9626 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9628 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9629 event.preventDefault();
9634 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9636 event.preventDefault();
9637 // this is very hacky as keydown always get's upper case.
9639 var cc = String.fromCharCode(event.getCharCode());
9640 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9644 adjustWidth : function(tag, w){
9645 tag = tag.toLowerCase();
9646 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9647 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9651 if(tag == 'textarea'){
9654 }else if(Roo.isOpera){
9658 if(tag == 'textarea'){
9666 setFieldLabel : function(v)
9673 var ar = this.el.select('label > span',true);
9675 if (ar.elements.length) {
9676 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9677 this.fieldLabel = v;
9681 var br = this.el.select('label',true);
9683 if(br.elements.length) {
9684 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9685 this.fieldLabel = v;
9689 Roo.log('Cannot Found any of label > span || label in input');
9693 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9694 this.fieldLabel = v;
9709 * @class Roo.bootstrap.TextArea
9710 * @extends Roo.bootstrap.Input
9711 * Bootstrap TextArea class
9712 * @cfg {Number} cols Specifies the visible width of a text area
9713 * @cfg {Number} rows Specifies the visible number of lines in a text area
9714 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9715 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9716 * @cfg {string} html text
9719 * Create a new TextArea
9720 * @param {Object} config The config object
9723 Roo.bootstrap.TextArea = function(config){
9724 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9728 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9738 getAutoCreate : function(){
9740 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9746 if(this.inputType != 'hidden'){
9747 cfg.cls = 'form-group' //input-group
9755 value : this.value || '',
9756 html: this.html || '',
9757 cls : 'form-control',
9758 placeholder : this.placeholder || ''
9762 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9763 input.maxLength = this.maxLength;
9767 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9771 input.cols = this.cols;
9774 if (this.readOnly) {
9775 input.readonly = true;
9779 input.name = this.name;
9783 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9787 ['xs','sm','md','lg'].map(function(size){
9788 if (settings[size]) {
9789 cfg.cls += ' col-' + size + '-' + settings[size];
9793 var inputblock = input;
9795 if(this.hasFeedback && !this.allowBlank){
9799 cls: 'glyphicon form-control-feedback'
9803 cls : 'has-feedback',
9812 if (this.before || this.after) {
9815 cls : 'input-group',
9819 inputblock.cn.push({
9821 cls : 'input-group-addon',
9826 inputblock.cn.push(input);
9828 if(this.hasFeedback && !this.allowBlank){
9829 inputblock.cls += ' has-feedback';
9830 inputblock.cn.push(feedback);
9834 inputblock.cn.push({
9836 cls : 'input-group-addon',
9843 if (align ==='left' && this.fieldLabel.length) {
9848 cls : 'control-label',
9849 html : this.fieldLabel
9860 if(this.labelWidth > 12){
9861 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9864 if(this.labelWidth < 13 && this.labelmd == 0){
9865 this.labelmd = this.labelWidth;
9868 if(this.labellg > 0){
9869 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9870 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9873 if(this.labelmd > 0){
9874 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9875 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9878 if(this.labelsm > 0){
9879 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9880 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9883 if(this.labelxs > 0){
9884 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9885 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9888 } else if ( this.fieldLabel.length) {
9893 //cls : 'input-group-addon',
9894 html : this.fieldLabel
9912 if (this.disabled) {
9913 input.disabled=true;
9920 * return the real textarea element.
9922 inputEl: function ()
9924 return this.el.select('textarea.form-control',true).first();
9928 * Clear any invalid styles/messages for this field
9930 clearInvalid : function()
9933 if(!this.el || this.preventMark){ // not rendered
9937 var label = this.el.select('label', true).first();
9938 var icon = this.el.select('i.fa-star', true).first();
9944 this.el.removeClass(this.invalidClass);
9946 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9948 var feedback = this.el.select('.form-control-feedback', true).first();
9951 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9956 this.fireEvent('valid', this);
9960 * Mark this field as valid
9962 markValid : function()
9964 if(!this.el || this.preventMark){ // not rendered
9968 this.el.removeClass([this.invalidClass, this.validClass]);
9970 var feedback = this.el.select('.form-control-feedback', true).first();
9973 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9976 if(this.disabled || this.allowBlank){
9980 var label = this.el.select('label', true).first();
9981 var icon = this.el.select('i.fa-star', true).first();
9987 this.el.addClass(this.validClass);
9989 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9991 var feedback = this.el.select('.form-control-feedback', true).first();
9994 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9995 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10000 this.fireEvent('valid', this);
10004 * Mark this field as invalid
10005 * @param {String} msg The validation message
10007 markInvalid : function(msg)
10009 if(!this.el || this.preventMark){ // not rendered
10013 this.el.removeClass([this.invalidClass, this.validClass]);
10015 var feedback = this.el.select('.form-control-feedback', true).first();
10018 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10021 if(this.disabled || this.allowBlank){
10025 var label = this.el.select('label', true).first();
10026 var icon = this.el.select('i.fa-star', true).first();
10028 if(!this.getValue().length && label && !icon){
10029 this.el.createChild({
10031 cls : 'text-danger fa fa-lg fa-star',
10032 tooltip : 'This field is required',
10033 style : 'margin-right:5px;'
10037 this.el.addClass(this.invalidClass);
10039 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10041 var feedback = this.el.select('.form-control-feedback', true).first();
10044 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10046 if(this.getValue().length || this.forceFeedback){
10047 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10054 this.fireEvent('invalid', this, msg);
10062 * trigger field - base class for combo..
10067 * @class Roo.bootstrap.TriggerField
10068 * @extends Roo.bootstrap.Input
10069 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10070 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10071 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10072 * for which you can provide a custom implementation. For example:
10074 var trigger = new Roo.bootstrap.TriggerField();
10075 trigger.onTriggerClick = myTriggerFn;
10076 trigger.applyTo('my-field');
10079 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10080 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10081 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10082 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10083 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10086 * Create a new TriggerField.
10087 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10088 * to the base TextField)
10090 Roo.bootstrap.TriggerField = function(config){
10091 this.mimicing = false;
10092 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10095 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10097 * @cfg {String} triggerClass A CSS class to apply to the trigger
10100 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10105 * @cfg {Boolean} removable (true|false) special filter default false
10109 /** @cfg {Boolean} grow @hide */
10110 /** @cfg {Number} growMin @hide */
10111 /** @cfg {Number} growMax @hide */
10117 autoSize: Roo.emptyFn,
10121 deferHeight : true,
10124 actionMode : 'wrap',
10129 getAutoCreate : function(){
10131 var align = this.labelAlign || this.parentLabelAlign();
10136 cls: 'form-group' //input-group
10143 type : this.inputType,
10144 cls : 'form-control',
10145 autocomplete: 'new-password',
10146 placeholder : this.placeholder || ''
10150 input.name = this.name;
10153 input.cls += ' input-' + this.size;
10156 if (this.disabled) {
10157 input.disabled=true;
10160 var inputblock = input;
10162 if(this.hasFeedback && !this.allowBlank){
10166 cls: 'glyphicon form-control-feedback'
10169 if(this.removable && !this.editable && !this.tickable){
10171 cls : 'has-feedback',
10177 cls : 'roo-combo-removable-btn close'
10184 cls : 'has-feedback',
10193 if(this.removable && !this.editable && !this.tickable){
10195 cls : 'roo-removable',
10201 cls : 'roo-combo-removable-btn close'
10208 if (this.before || this.after) {
10211 cls : 'input-group',
10215 inputblock.cn.push({
10217 cls : 'input-group-addon',
10222 inputblock.cn.push(input);
10224 if(this.hasFeedback && !this.allowBlank){
10225 inputblock.cls += ' has-feedback';
10226 inputblock.cn.push(feedback);
10230 inputblock.cn.push({
10232 cls : 'input-group-addon',
10245 cls: 'form-hidden-field'
10259 cls: 'form-hidden-field'
10263 cls: 'roo-select2-choices',
10267 cls: 'roo-select2-search-field',
10280 cls: 'roo-select2-container input-group',
10285 // cls: 'typeahead typeahead-long dropdown-menu',
10286 // style: 'display:none'
10291 if(!this.multiple && this.showToggleBtn){
10297 if (this.caret != false) {
10300 cls: 'fa fa-' + this.caret
10307 cls : 'input-group-addon btn dropdown-toggle',
10312 cls: 'combobox-clear',
10326 combobox.cls += ' roo-select2-container-multi';
10329 if (align ==='left' && this.fieldLabel.length) {
10331 cfg.cls += ' roo-form-group-label-left';
10336 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10337 tooltip : 'This field is required'
10342 cls : 'control-label',
10343 html : this.fieldLabel
10355 var labelCfg = cfg.cn[1];
10356 var contentCfg = cfg.cn[2];
10358 if(this.indicatorpos == 'right'){
10363 cls : 'control-label',
10367 html : this.fieldLabel
10371 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10372 tooltip : 'This field is required'
10385 labelCfg = cfg.cn[0];
10386 contentCfg = cfg.cn[1];
10389 if(this.labelWidth > 12){
10390 labelCfg.style = "width: " + this.labelWidth + 'px';
10393 if(this.labelWidth < 13 && this.labelmd == 0){
10394 this.labelmd = this.labelWidth;
10397 if(this.labellg > 0){
10398 labelCfg.cls += ' col-lg-' + this.labellg;
10399 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10402 if(this.labelmd > 0){
10403 labelCfg.cls += ' col-md-' + this.labelmd;
10404 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10407 if(this.labelsm > 0){
10408 labelCfg.cls += ' col-sm-' + this.labelsm;
10409 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10412 if(this.labelxs > 0){
10413 labelCfg.cls += ' col-xs-' + this.labelxs;
10414 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10417 } else if ( this.fieldLabel.length) {
10418 // Roo.log(" label");
10422 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10423 tooltip : 'This field is required'
10427 //cls : 'input-group-addon',
10428 html : this.fieldLabel
10436 if(this.indicatorpos == 'right'){
10444 html : this.fieldLabel
10448 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10449 tooltip : 'This field is required'
10462 // Roo.log(" no label && no align");
10469 ['xs','sm','md','lg'].map(function(size){
10470 if (settings[size]) {
10471 cfg.cls += ' col-' + size + '-' + settings[size];
10482 onResize : function(w, h){
10483 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10484 // if(typeof w == 'number'){
10485 // var x = w - this.trigger.getWidth();
10486 // this.inputEl().setWidth(this.adjustWidth('input', x));
10487 // this.trigger.setStyle('left', x+'px');
10492 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10495 getResizeEl : function(){
10496 return this.inputEl();
10500 getPositionEl : function(){
10501 return this.inputEl();
10505 alignErrorIcon : function(){
10506 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10510 initEvents : function(){
10514 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10515 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10516 if(!this.multiple && this.showToggleBtn){
10517 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10518 if(this.hideTrigger){
10519 this.trigger.setDisplayed(false);
10521 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10525 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10528 if(this.removable && !this.editable && !this.tickable){
10529 var close = this.closeTriggerEl();
10532 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10533 close.on('click', this.removeBtnClick, this, close);
10537 //this.trigger.addClassOnOver('x-form-trigger-over');
10538 //this.trigger.addClassOnClick('x-form-trigger-click');
10541 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10545 closeTriggerEl : function()
10547 var close = this.el.select('.roo-combo-removable-btn', true).first();
10548 return close ? close : false;
10551 removeBtnClick : function(e, h, el)
10553 e.preventDefault();
10555 if(this.fireEvent("remove", this) !== false){
10557 this.fireEvent("afterremove", this)
10561 createList : function()
10563 this.list = Roo.get(document.body).createChild({
10565 cls: 'typeahead typeahead-long dropdown-menu',
10566 style: 'display:none'
10569 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10574 initTrigger : function(){
10579 onDestroy : function(){
10581 this.trigger.removeAllListeners();
10582 // this.trigger.remove();
10585 // this.wrap.remove();
10587 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10591 onFocus : function(){
10592 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10594 if(!this.mimicing){
10595 this.wrap.addClass('x-trigger-wrap-focus');
10596 this.mimicing = true;
10597 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10598 if(this.monitorTab){
10599 this.el.on("keydown", this.checkTab, this);
10606 checkTab : function(e){
10607 if(e.getKey() == e.TAB){
10608 this.triggerBlur();
10613 onBlur : function(){
10618 mimicBlur : function(e, t){
10620 if(!this.wrap.contains(t) && this.validateBlur()){
10621 this.triggerBlur();
10627 triggerBlur : function(){
10628 this.mimicing = false;
10629 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10630 if(this.monitorTab){
10631 this.el.un("keydown", this.checkTab, this);
10633 //this.wrap.removeClass('x-trigger-wrap-focus');
10634 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10638 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10639 validateBlur : function(e, t){
10644 onDisable : function(){
10645 this.inputEl().dom.disabled = true;
10646 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10648 // this.wrap.addClass('x-item-disabled');
10653 onEnable : function(){
10654 this.inputEl().dom.disabled = false;
10655 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10657 // this.el.removeClass('x-item-disabled');
10662 onShow : function(){
10663 var ae = this.getActionEl();
10666 ae.dom.style.display = '';
10667 ae.dom.style.visibility = 'visible';
10673 onHide : function(){
10674 var ae = this.getActionEl();
10675 ae.dom.style.display = 'none';
10679 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10680 * by an implementing function.
10682 * @param {EventObject} e
10684 onTriggerClick : Roo.emptyFn
10688 * Ext JS Library 1.1.1
10689 * Copyright(c) 2006-2007, Ext JS, LLC.
10691 * Originally Released Under LGPL - original licence link has changed is not relivant.
10694 * <script type="text/javascript">
10699 * @class Roo.data.SortTypes
10701 * Defines the default sorting (casting?) comparison functions used when sorting data.
10703 Roo.data.SortTypes = {
10705 * Default sort that does nothing
10706 * @param {Mixed} s The value being converted
10707 * @return {Mixed} The comparison value
10709 none : function(s){
10714 * The regular expression used to strip tags
10718 stripTagsRE : /<\/?[^>]+>/gi,
10721 * Strips all HTML tags to sort on text only
10722 * @param {Mixed} s The value being converted
10723 * @return {String} The comparison value
10725 asText : function(s){
10726 return String(s).replace(this.stripTagsRE, "");
10730 * Strips all HTML tags to sort on text only - Case insensitive
10731 * @param {Mixed} s The value being converted
10732 * @return {String} The comparison value
10734 asUCText : function(s){
10735 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10739 * Case insensitive string
10740 * @param {Mixed} s The value being converted
10741 * @return {String} The comparison value
10743 asUCString : function(s) {
10744 return String(s).toUpperCase();
10749 * @param {Mixed} s The value being converted
10750 * @return {Number} The comparison value
10752 asDate : function(s) {
10756 if(s instanceof Date){
10757 return s.getTime();
10759 return Date.parse(String(s));
10764 * @param {Mixed} s The value being converted
10765 * @return {Float} The comparison value
10767 asFloat : function(s) {
10768 var val = parseFloat(String(s).replace(/,/g, ""));
10777 * @param {Mixed} s The value being converted
10778 * @return {Number} The comparison value
10780 asInt : function(s) {
10781 var val = parseInt(String(s).replace(/,/g, ""));
10789 * Ext JS Library 1.1.1
10790 * Copyright(c) 2006-2007, Ext JS, LLC.
10792 * Originally Released Under LGPL - original licence link has changed is not relivant.
10795 * <script type="text/javascript">
10799 * @class Roo.data.Record
10800 * Instances of this class encapsulate both record <em>definition</em> information, and record
10801 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10802 * to access Records cached in an {@link Roo.data.Store} object.<br>
10804 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10805 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10808 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10810 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10811 * {@link #create}. The parameters are the same.
10812 * @param {Array} data An associative Array of data values keyed by the field name.
10813 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10814 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10815 * not specified an integer id is generated.
10817 Roo.data.Record = function(data, id){
10818 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10823 * Generate a constructor for a specific record layout.
10824 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10825 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10826 * Each field definition object may contain the following properties: <ul>
10827 * <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,
10828 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10829 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10830 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10831 * is being used, then this is a string containing the javascript expression to reference the data relative to
10832 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10833 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10834 * this may be omitted.</p></li>
10835 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10836 * <ul><li>auto (Default, implies no conversion)</li>
10841 * <li>date</li></ul></p></li>
10842 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10843 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10844 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10845 * by the Reader into an object that will be stored in the Record. It is passed the
10846 * following parameters:<ul>
10847 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10849 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10851 * <br>usage:<br><pre><code>
10852 var TopicRecord = Roo.data.Record.create(
10853 {name: 'title', mapping: 'topic_title'},
10854 {name: 'author', mapping: 'username'},
10855 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10856 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10857 {name: 'lastPoster', mapping: 'user2'},
10858 {name: 'excerpt', mapping: 'post_text'}
10861 var myNewRecord = new TopicRecord({
10862 title: 'Do my job please',
10865 lastPost: new Date(),
10866 lastPoster: 'Animal',
10867 excerpt: 'No way dude!'
10869 myStore.add(myNewRecord);
10874 Roo.data.Record.create = function(o){
10875 var f = function(){
10876 f.superclass.constructor.apply(this, arguments);
10878 Roo.extend(f, Roo.data.Record);
10879 var p = f.prototype;
10880 p.fields = new Roo.util.MixedCollection(false, function(field){
10883 for(var i = 0, len = o.length; i < len; i++){
10884 p.fields.add(new Roo.data.Field(o[i]));
10886 f.getField = function(name){
10887 return p.fields.get(name);
10892 Roo.data.Record.AUTO_ID = 1000;
10893 Roo.data.Record.EDIT = 'edit';
10894 Roo.data.Record.REJECT = 'reject';
10895 Roo.data.Record.COMMIT = 'commit';
10897 Roo.data.Record.prototype = {
10899 * Readonly flag - true if this record has been modified.
10908 join : function(store){
10909 this.store = store;
10913 * Set the named field to the specified value.
10914 * @param {String} name The name of the field to set.
10915 * @param {Object} value The value to set the field to.
10917 set : function(name, value){
10918 if(this.data[name] == value){
10922 if(!this.modified){
10923 this.modified = {};
10925 if(typeof this.modified[name] == 'undefined'){
10926 this.modified[name] = this.data[name];
10928 this.data[name] = value;
10929 if(!this.editing && this.store){
10930 this.store.afterEdit(this);
10935 * Get the value of the named field.
10936 * @param {String} name The name of the field to get the value of.
10937 * @return {Object} The value of the field.
10939 get : function(name){
10940 return this.data[name];
10944 beginEdit : function(){
10945 this.editing = true;
10946 this.modified = {};
10950 cancelEdit : function(){
10951 this.editing = false;
10952 delete this.modified;
10956 endEdit : function(){
10957 this.editing = false;
10958 if(this.dirty && this.store){
10959 this.store.afterEdit(this);
10964 * Usually called by the {@link Roo.data.Store} which owns the Record.
10965 * Rejects all changes made to the Record since either creation, or the last commit operation.
10966 * Modified fields are reverted to their original values.
10968 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10969 * of reject operations.
10971 reject : function(){
10972 var m = this.modified;
10974 if(typeof m[n] != "function"){
10975 this.data[n] = m[n];
10978 this.dirty = false;
10979 delete this.modified;
10980 this.editing = false;
10982 this.store.afterReject(this);
10987 * Usually called by the {@link Roo.data.Store} which owns the Record.
10988 * Commits all changes made to the Record since either creation, or the last commit operation.
10990 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10991 * of commit operations.
10993 commit : function(){
10994 this.dirty = false;
10995 delete this.modified;
10996 this.editing = false;
10998 this.store.afterCommit(this);
11003 hasError : function(){
11004 return this.error != null;
11008 clearError : function(){
11013 * Creates a copy of this record.
11014 * @param {String} id (optional) A new record id if you don't want to use this record's id
11017 copy : function(newId) {
11018 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11022 * Ext JS Library 1.1.1
11023 * Copyright(c) 2006-2007, Ext JS, LLC.
11025 * Originally Released Under LGPL - original licence link has changed is not relivant.
11028 * <script type="text/javascript">
11034 * @class Roo.data.Store
11035 * @extends Roo.util.Observable
11036 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11037 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11039 * 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
11040 * has no knowledge of the format of the data returned by the Proxy.<br>
11042 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11043 * instances from the data object. These records are cached and made available through accessor functions.
11045 * Creates a new Store.
11046 * @param {Object} config A config object containing the objects needed for the Store to access data,
11047 * and read the data into Records.
11049 Roo.data.Store = function(config){
11050 this.data = new Roo.util.MixedCollection(false);
11051 this.data.getKey = function(o){
11054 this.baseParams = {};
11056 this.paramNames = {
11061 "multisort" : "_multisort"
11064 if(config && config.data){
11065 this.inlineData = config.data;
11066 delete config.data;
11069 Roo.apply(this, config);
11071 if(this.reader){ // reader passed
11072 this.reader = Roo.factory(this.reader, Roo.data);
11073 this.reader.xmodule = this.xmodule || false;
11074 if(!this.recordType){
11075 this.recordType = this.reader.recordType;
11077 if(this.reader.onMetaChange){
11078 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11082 if(this.recordType){
11083 this.fields = this.recordType.prototype.fields;
11085 this.modified = [];
11089 * @event datachanged
11090 * Fires when the data cache has changed, and a widget which is using this Store
11091 * as a Record cache should refresh its view.
11092 * @param {Store} this
11094 datachanged : true,
11096 * @event metachange
11097 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11098 * @param {Store} this
11099 * @param {Object} meta The JSON metadata
11104 * Fires when Records have been added to the Store
11105 * @param {Store} this
11106 * @param {Roo.data.Record[]} records The array of Records added
11107 * @param {Number} index The index at which the record(s) were added
11112 * Fires when a Record has been removed from the Store
11113 * @param {Store} this
11114 * @param {Roo.data.Record} record The Record that was removed
11115 * @param {Number} index The index at which the record was removed
11120 * Fires when a Record has been updated
11121 * @param {Store} this
11122 * @param {Roo.data.Record} record The Record that was updated
11123 * @param {String} operation The update operation being performed. Value may be one of:
11125 Roo.data.Record.EDIT
11126 Roo.data.Record.REJECT
11127 Roo.data.Record.COMMIT
11133 * Fires when the data cache has been cleared.
11134 * @param {Store} this
11138 * @event beforeload
11139 * Fires before a request is made for a new data object. If the beforeload handler returns false
11140 * the load action will be canceled.
11141 * @param {Store} this
11142 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11146 * @event beforeloadadd
11147 * Fires after a new set of Records has been loaded.
11148 * @param {Store} this
11149 * @param {Roo.data.Record[]} records The Records that were loaded
11150 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11152 beforeloadadd : true,
11155 * Fires after a new set of Records has been loaded, before they are added to the store.
11156 * @param {Store} this
11157 * @param {Roo.data.Record[]} records The Records that were loaded
11158 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11159 * @params {Object} return from reader
11163 * @event loadexception
11164 * Fires if an exception occurs in the Proxy during loading.
11165 * Called with the signature of the Proxy's "loadexception" event.
11166 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11169 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11170 * @param {Object} load options
11171 * @param {Object} jsonData from your request (normally this contains the Exception)
11173 loadexception : true
11177 this.proxy = Roo.factory(this.proxy, Roo.data);
11178 this.proxy.xmodule = this.xmodule || false;
11179 this.relayEvents(this.proxy, ["loadexception"]);
11181 this.sortToggle = {};
11182 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11184 Roo.data.Store.superclass.constructor.call(this);
11186 if(this.inlineData){
11187 this.loadData(this.inlineData);
11188 delete this.inlineData;
11192 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11194 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11195 * without a remote query - used by combo/forms at present.
11199 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11202 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11205 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11206 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11209 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11210 * on any HTTP request
11213 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11216 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11220 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11221 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11223 remoteSort : false,
11226 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11227 * loaded or when a record is removed. (defaults to false).
11229 pruneModifiedRecords : false,
11232 lastOptions : null,
11235 * Add Records to the Store and fires the add event.
11236 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11238 add : function(records){
11239 records = [].concat(records);
11240 for(var i = 0, len = records.length; i < len; i++){
11241 records[i].join(this);
11243 var index = this.data.length;
11244 this.data.addAll(records);
11245 this.fireEvent("add", this, records, index);
11249 * Remove a Record from the Store and fires the remove event.
11250 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11252 remove : function(record){
11253 var index = this.data.indexOf(record);
11254 this.data.removeAt(index);
11256 if(this.pruneModifiedRecords){
11257 this.modified.remove(record);
11259 this.fireEvent("remove", this, record, index);
11263 * Remove all Records from the Store and fires the clear event.
11265 removeAll : function(){
11267 if(this.pruneModifiedRecords){
11268 this.modified = [];
11270 this.fireEvent("clear", this);
11274 * Inserts Records to the Store at the given index and fires the add event.
11275 * @param {Number} index The start index at which to insert the passed Records.
11276 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11278 insert : function(index, records){
11279 records = [].concat(records);
11280 for(var i = 0, len = records.length; i < len; i++){
11281 this.data.insert(index, records[i]);
11282 records[i].join(this);
11284 this.fireEvent("add", this, records, index);
11288 * Get the index within the cache of the passed Record.
11289 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11290 * @return {Number} The index of the passed Record. Returns -1 if not found.
11292 indexOf : function(record){
11293 return this.data.indexOf(record);
11297 * Get the index within the cache of the Record with the passed id.
11298 * @param {String} id The id of the Record to find.
11299 * @return {Number} The index of the Record. Returns -1 if not found.
11301 indexOfId : function(id){
11302 return this.data.indexOfKey(id);
11306 * Get the Record with the specified id.
11307 * @param {String} id The id of the Record to find.
11308 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11310 getById : function(id){
11311 return this.data.key(id);
11315 * Get the Record at the specified index.
11316 * @param {Number} index The index of the Record to find.
11317 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11319 getAt : function(index){
11320 return this.data.itemAt(index);
11324 * Returns a range of Records between specified indices.
11325 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11326 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11327 * @return {Roo.data.Record[]} An array of Records
11329 getRange : function(start, end){
11330 return this.data.getRange(start, end);
11334 storeOptions : function(o){
11335 o = Roo.apply({}, o);
11338 this.lastOptions = o;
11342 * Loads the Record cache from the configured Proxy using the configured Reader.
11344 * If using remote paging, then the first load call must specify the <em>start</em>
11345 * and <em>limit</em> properties in the options.params property to establish the initial
11346 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11348 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11349 * and this call will return before the new data has been loaded. Perform any post-processing
11350 * in a callback function, or in a "load" event handler.</strong>
11352 * @param {Object} options An object containing properties which control loading options:<ul>
11353 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11354 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11355 * passed the following arguments:<ul>
11356 * <li>r : Roo.data.Record[]</li>
11357 * <li>options: Options object from the load call</li>
11358 * <li>success: Boolean success indicator</li></ul></li>
11359 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11360 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11363 load : function(options){
11364 options = options || {};
11365 if(this.fireEvent("beforeload", this, options) !== false){
11366 this.storeOptions(options);
11367 var p = Roo.apply(options.params || {}, this.baseParams);
11368 // if meta was not loaded from remote source.. try requesting it.
11369 if (!this.reader.metaFromRemote) {
11370 p._requestMeta = 1;
11372 if(this.sortInfo && this.remoteSort){
11373 var pn = this.paramNames;
11374 p[pn["sort"]] = this.sortInfo.field;
11375 p[pn["dir"]] = this.sortInfo.direction;
11377 if (this.multiSort) {
11378 var pn = this.paramNames;
11379 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11382 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11387 * Reloads the Record cache from the configured Proxy using the configured Reader and
11388 * the options from the last load operation performed.
11389 * @param {Object} options (optional) An object containing properties which may override the options
11390 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11391 * the most recently used options are reused).
11393 reload : function(options){
11394 this.load(Roo.applyIf(options||{}, this.lastOptions));
11398 // Called as a callback by the Reader during a load operation.
11399 loadRecords : function(o, options, success){
11400 if(!o || success === false){
11401 if(success !== false){
11402 this.fireEvent("load", this, [], options, o);
11404 if(options.callback){
11405 options.callback.call(options.scope || this, [], options, false);
11409 // if data returned failure - throw an exception.
11410 if (o.success === false) {
11411 // show a message if no listener is registered.
11412 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11413 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11415 // loadmask wil be hooked into this..
11416 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11419 var r = o.records, t = o.totalRecords || r.length;
11421 this.fireEvent("beforeloadadd", this, r, options, o);
11423 if(!options || options.add !== true){
11424 if(this.pruneModifiedRecords){
11425 this.modified = [];
11427 for(var i = 0, len = r.length; i < len; i++){
11431 this.data = this.snapshot;
11432 delete this.snapshot;
11435 this.data.addAll(r);
11436 this.totalLength = t;
11438 this.fireEvent("datachanged", this);
11440 this.totalLength = Math.max(t, this.data.length+r.length);
11444 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11446 var e = new Roo.data.Record({});
11448 e.set(this.parent.displayField, this.parent.emptyTitle);
11449 e.set(this.parent.valueField, '');
11454 this.fireEvent("load", this, r, options, o);
11455 if(options.callback){
11456 options.callback.call(options.scope || this, r, options, true);
11462 * Loads data from a passed data block. A Reader which understands the format of the data
11463 * must have been configured in the constructor.
11464 * @param {Object} data The data block from which to read the Records. The format of the data expected
11465 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11466 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11468 loadData : function(o, append){
11469 var r = this.reader.readRecords(o);
11470 this.loadRecords(r, {add: append}, true);
11474 * Gets the number of cached records.
11476 * <em>If using paging, this may not be the total size of the dataset. If the data object
11477 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11478 * the data set size</em>
11480 getCount : function(){
11481 return this.data.length || 0;
11485 * Gets the total number of records in the dataset as returned by the server.
11487 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11488 * the dataset size</em>
11490 getTotalCount : function(){
11491 return this.totalLength || 0;
11495 * Returns the sort state of the Store as an object with two properties:
11497 field {String} The name of the field by which the Records are sorted
11498 direction {String} The sort order, "ASC" or "DESC"
11501 getSortState : function(){
11502 return this.sortInfo;
11506 applySort : function(){
11507 if(this.sortInfo && !this.remoteSort){
11508 var s = this.sortInfo, f = s.field;
11509 var st = this.fields.get(f).sortType;
11510 var fn = function(r1, r2){
11511 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11512 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11514 this.data.sort(s.direction, fn);
11515 if(this.snapshot && this.snapshot != this.data){
11516 this.snapshot.sort(s.direction, fn);
11522 * Sets the default sort column and order to be used by the next load operation.
11523 * @param {String} fieldName The name of the field to sort by.
11524 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11526 setDefaultSort : function(field, dir){
11527 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11531 * Sort the Records.
11532 * If remote sorting is used, the sort is performed on the server, and the cache is
11533 * reloaded. If local sorting is used, the cache is sorted internally.
11534 * @param {String} fieldName The name of the field to sort by.
11535 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11537 sort : function(fieldName, dir){
11538 var f = this.fields.get(fieldName);
11540 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11542 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11543 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11548 this.sortToggle[f.name] = dir;
11549 this.sortInfo = {field: f.name, direction: dir};
11550 if(!this.remoteSort){
11552 this.fireEvent("datachanged", this);
11554 this.load(this.lastOptions);
11559 * Calls the specified function for each of the Records in the cache.
11560 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11561 * Returning <em>false</em> aborts and exits the iteration.
11562 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11564 each : function(fn, scope){
11565 this.data.each(fn, scope);
11569 * Gets all records modified since the last commit. Modified records are persisted across load operations
11570 * (e.g., during paging).
11571 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11573 getModifiedRecords : function(){
11574 return this.modified;
11578 createFilterFn : function(property, value, anyMatch){
11579 if(!value.exec){ // not a regex
11580 value = String(value);
11581 if(value.length == 0){
11584 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11586 return function(r){
11587 return value.test(r.data[property]);
11592 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11593 * @param {String} property A field on your records
11594 * @param {Number} start The record index to start at (defaults to 0)
11595 * @param {Number} end The last record index to include (defaults to length - 1)
11596 * @return {Number} The sum
11598 sum : function(property, start, end){
11599 var rs = this.data.items, v = 0;
11600 start = start || 0;
11601 end = (end || end === 0) ? end : rs.length-1;
11603 for(var i = start; i <= end; i++){
11604 v += (rs[i].data[property] || 0);
11610 * Filter the records by a specified property.
11611 * @param {String} field A field on your records
11612 * @param {String/RegExp} value Either a string that the field
11613 * should start with or a RegExp to test against the field
11614 * @param {Boolean} anyMatch True to match any part not just the beginning
11616 filter : function(property, value, anyMatch){
11617 var fn = this.createFilterFn(property, value, anyMatch);
11618 return fn ? this.filterBy(fn) : this.clearFilter();
11622 * Filter by a function. The specified function will be called with each
11623 * record in this data source. If the function returns true the record is included,
11624 * otherwise it is filtered.
11625 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11626 * @param {Object} scope (optional) The scope of the function (defaults to this)
11628 filterBy : function(fn, scope){
11629 this.snapshot = this.snapshot || this.data;
11630 this.data = this.queryBy(fn, scope||this);
11631 this.fireEvent("datachanged", this);
11635 * Query the records by a specified property.
11636 * @param {String} field A field on your records
11637 * @param {String/RegExp} value Either a string that the field
11638 * should start with or a RegExp to test against the field
11639 * @param {Boolean} anyMatch True to match any part not just the beginning
11640 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11642 query : function(property, value, anyMatch){
11643 var fn = this.createFilterFn(property, value, anyMatch);
11644 return fn ? this.queryBy(fn) : this.data.clone();
11648 * Query by a function. The specified function will be called with each
11649 * record in this data source. If the function returns true the record is included
11651 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11652 * @param {Object} scope (optional) The scope of the function (defaults to this)
11653 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11655 queryBy : function(fn, scope){
11656 var data = this.snapshot || this.data;
11657 return data.filterBy(fn, scope||this);
11661 * Collects unique values for a particular dataIndex from this store.
11662 * @param {String} dataIndex The property to collect
11663 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11664 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11665 * @return {Array} An array of the unique values
11667 collect : function(dataIndex, allowNull, bypassFilter){
11668 var d = (bypassFilter === true && this.snapshot) ?
11669 this.snapshot.items : this.data.items;
11670 var v, sv, r = [], l = {};
11671 for(var i = 0, len = d.length; i < len; i++){
11672 v = d[i].data[dataIndex];
11674 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11683 * Revert to a view of the Record cache with no filtering applied.
11684 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11686 clearFilter : function(suppressEvent){
11687 if(this.snapshot && this.snapshot != this.data){
11688 this.data = this.snapshot;
11689 delete this.snapshot;
11690 if(suppressEvent !== true){
11691 this.fireEvent("datachanged", this);
11697 afterEdit : function(record){
11698 if(this.modified.indexOf(record) == -1){
11699 this.modified.push(record);
11701 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11705 afterReject : function(record){
11706 this.modified.remove(record);
11707 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11711 afterCommit : function(record){
11712 this.modified.remove(record);
11713 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11717 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11718 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11720 commitChanges : function(){
11721 var m = this.modified.slice(0);
11722 this.modified = [];
11723 for(var i = 0, len = m.length; i < len; i++){
11729 * Cancel outstanding changes on all changed records.
11731 rejectChanges : function(){
11732 var m = this.modified.slice(0);
11733 this.modified = [];
11734 for(var i = 0, len = m.length; i < len; i++){
11739 onMetaChange : function(meta, rtype, o){
11740 this.recordType = rtype;
11741 this.fields = rtype.prototype.fields;
11742 delete this.snapshot;
11743 this.sortInfo = meta.sortInfo || this.sortInfo;
11744 this.modified = [];
11745 this.fireEvent('metachange', this, this.reader.meta);
11748 moveIndex : function(data, type)
11750 var index = this.indexOf(data);
11752 var newIndex = index + type;
11756 this.insert(newIndex, data);
11761 * Ext JS Library 1.1.1
11762 * Copyright(c) 2006-2007, Ext JS, LLC.
11764 * Originally Released Under LGPL - original licence link has changed is not relivant.
11767 * <script type="text/javascript">
11771 * @class Roo.data.SimpleStore
11772 * @extends Roo.data.Store
11773 * Small helper class to make creating Stores from Array data easier.
11774 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11775 * @cfg {Array} fields An array of field definition objects, or field name strings.
11776 * @cfg {Array} data The multi-dimensional array of data
11778 * @param {Object} config
11780 Roo.data.SimpleStore = function(config){
11781 Roo.data.SimpleStore.superclass.constructor.call(this, {
11783 reader: new Roo.data.ArrayReader({
11786 Roo.data.Record.create(config.fields)
11788 proxy : new Roo.data.MemoryProxy(config.data)
11792 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11794 * Ext JS Library 1.1.1
11795 * Copyright(c) 2006-2007, Ext JS, LLC.
11797 * Originally Released Under LGPL - original licence link has changed is not relivant.
11800 * <script type="text/javascript">
11805 * @extends Roo.data.Store
11806 * @class Roo.data.JsonStore
11807 * Small helper class to make creating Stores for JSON data easier. <br/>
11809 var store = new Roo.data.JsonStore({
11810 url: 'get-images.php',
11812 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11815 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11816 * JsonReader and HttpProxy (unless inline data is provided).</b>
11817 * @cfg {Array} fields An array of field definition objects, or field name strings.
11819 * @param {Object} config
11821 Roo.data.JsonStore = function(c){
11822 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11823 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11824 reader: new Roo.data.JsonReader(c, c.fields)
11827 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11829 * Ext JS Library 1.1.1
11830 * Copyright(c) 2006-2007, Ext JS, LLC.
11832 * Originally Released Under LGPL - original licence link has changed is not relivant.
11835 * <script type="text/javascript">
11839 Roo.data.Field = function(config){
11840 if(typeof config == "string"){
11841 config = {name: config};
11843 Roo.apply(this, config);
11846 this.type = "auto";
11849 var st = Roo.data.SortTypes;
11850 // named sortTypes are supported, here we look them up
11851 if(typeof this.sortType == "string"){
11852 this.sortType = st[this.sortType];
11855 // set default sortType for strings and dates
11856 if(!this.sortType){
11859 this.sortType = st.asUCString;
11862 this.sortType = st.asDate;
11865 this.sortType = st.none;
11870 var stripRe = /[\$,%]/g;
11872 // prebuilt conversion function for this field, instead of
11873 // switching every time we're reading a value
11875 var cv, dateFormat = this.dateFormat;
11880 cv = function(v){ return v; };
11883 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11887 return v !== undefined && v !== null && v !== '' ?
11888 parseInt(String(v).replace(stripRe, ""), 10) : '';
11893 return v !== undefined && v !== null && v !== '' ?
11894 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11899 cv = function(v){ return v === true || v === "true" || v == 1; };
11906 if(v instanceof Date){
11910 if(dateFormat == "timestamp"){
11911 return new Date(v*1000);
11913 return Date.parseDate(v, dateFormat);
11915 var parsed = Date.parse(v);
11916 return parsed ? new Date(parsed) : null;
11925 Roo.data.Field.prototype = {
11933 * Ext JS Library 1.1.1
11934 * Copyright(c) 2006-2007, Ext JS, LLC.
11936 * Originally Released Under LGPL - original licence link has changed is not relivant.
11939 * <script type="text/javascript">
11942 // Base class for reading structured data from a data source. This class is intended to be
11943 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11946 * @class Roo.data.DataReader
11947 * Base class for reading structured data from a data source. This class is intended to be
11948 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11951 Roo.data.DataReader = function(meta, recordType){
11955 this.recordType = recordType instanceof Array ?
11956 Roo.data.Record.create(recordType) : recordType;
11959 Roo.data.DataReader.prototype = {
11961 * Create an empty record
11962 * @param {Object} data (optional) - overlay some values
11963 * @return {Roo.data.Record} record created.
11965 newRow : function(d) {
11967 this.recordType.prototype.fields.each(function(c) {
11969 case 'int' : da[c.name] = 0; break;
11970 case 'date' : da[c.name] = new Date(); break;
11971 case 'float' : da[c.name] = 0.0; break;
11972 case 'boolean' : da[c.name] = false; break;
11973 default : da[c.name] = ""; break;
11977 return new this.recordType(Roo.apply(da, d));
11982 * Ext JS Library 1.1.1
11983 * Copyright(c) 2006-2007, Ext JS, LLC.
11985 * Originally Released Under LGPL - original licence link has changed is not relivant.
11988 * <script type="text/javascript">
11992 * @class Roo.data.DataProxy
11993 * @extends Roo.data.Observable
11994 * This class is an abstract base class for implementations which provide retrieval of
11995 * unformatted data objects.<br>
11997 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11998 * (of the appropriate type which knows how to parse the data object) to provide a block of
11999 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12001 * Custom implementations must implement the load method as described in
12002 * {@link Roo.data.HttpProxy#load}.
12004 Roo.data.DataProxy = function(){
12007 * @event beforeload
12008 * Fires before a network request is made to retrieve a data object.
12009 * @param {Object} This DataProxy object.
12010 * @param {Object} params The params parameter to the load function.
12015 * Fires before the load method's callback is called.
12016 * @param {Object} This DataProxy object.
12017 * @param {Object} o The data object.
12018 * @param {Object} arg The callback argument object passed to the load function.
12022 * @event loadexception
12023 * Fires if an Exception occurs during data retrieval.
12024 * @param {Object} This DataProxy object.
12025 * @param {Object} o The data object.
12026 * @param {Object} arg The callback argument object passed to the load function.
12027 * @param {Object} e The Exception.
12029 loadexception : true
12031 Roo.data.DataProxy.superclass.constructor.call(this);
12034 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12037 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12041 * Ext JS Library 1.1.1
12042 * Copyright(c) 2006-2007, Ext JS, LLC.
12044 * Originally Released Under LGPL - original licence link has changed is not relivant.
12047 * <script type="text/javascript">
12050 * @class Roo.data.MemoryProxy
12051 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12052 * to the Reader when its load method is called.
12054 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12056 Roo.data.MemoryProxy = function(data){
12060 Roo.data.MemoryProxy.superclass.constructor.call(this);
12064 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12067 * Load data from the requested source (in this case an in-memory
12068 * data object passed to the constructor), read the data object into
12069 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12070 * process that block using the passed callback.
12071 * @param {Object} params This parameter is not used by the MemoryProxy class.
12072 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12073 * object into a block of Roo.data.Records.
12074 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12075 * The function must be passed <ul>
12076 * <li>The Record block object</li>
12077 * <li>The "arg" argument from the load function</li>
12078 * <li>A boolean success indicator</li>
12080 * @param {Object} scope The scope in which to call the callback
12081 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12083 load : function(params, reader, callback, scope, arg){
12084 params = params || {};
12087 result = reader.readRecords(this.data);
12089 this.fireEvent("loadexception", this, arg, null, e);
12090 callback.call(scope, null, arg, false);
12093 callback.call(scope, result, arg, true);
12097 update : function(params, records){
12102 * Ext JS Library 1.1.1
12103 * Copyright(c) 2006-2007, Ext JS, LLC.
12105 * Originally Released Under LGPL - original licence link has changed is not relivant.
12108 * <script type="text/javascript">
12111 * @class Roo.data.HttpProxy
12112 * @extends Roo.data.DataProxy
12113 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12114 * configured to reference a certain URL.<br><br>
12116 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12117 * from which the running page was served.<br><br>
12119 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12121 * Be aware that to enable the browser to parse an XML document, the server must set
12122 * the Content-Type header in the HTTP response to "text/xml".
12124 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12125 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12126 * will be used to make the request.
12128 Roo.data.HttpProxy = function(conn){
12129 Roo.data.HttpProxy.superclass.constructor.call(this);
12130 // is conn a conn config or a real conn?
12132 this.useAjax = !conn || !conn.events;
12136 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12137 // thse are take from connection...
12140 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12143 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12144 * extra parameters to each request made by this object. (defaults to undefined)
12147 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12148 * to each request made by this object. (defaults to undefined)
12151 * @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)
12154 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12157 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12163 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12167 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12168 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12169 * a finer-grained basis than the DataProxy events.
12171 getConnection : function(){
12172 return this.useAjax ? Roo.Ajax : this.conn;
12176 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12177 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12178 * process that block using the passed callback.
12179 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12180 * for the request to the remote server.
12181 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12182 * object into a block of Roo.data.Records.
12183 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12184 * The function must be passed <ul>
12185 * <li>The Record block object</li>
12186 * <li>The "arg" argument from the load function</li>
12187 * <li>A boolean success indicator</li>
12189 * @param {Object} scope The scope in which to call the callback
12190 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12192 load : function(params, reader, callback, scope, arg){
12193 if(this.fireEvent("beforeload", this, params) !== false){
12195 params : params || {},
12197 callback : callback,
12202 callback : this.loadResponse,
12206 Roo.applyIf(o, this.conn);
12207 if(this.activeRequest){
12208 Roo.Ajax.abort(this.activeRequest);
12210 this.activeRequest = Roo.Ajax.request(o);
12212 this.conn.request(o);
12215 callback.call(scope||this, null, arg, false);
12220 loadResponse : function(o, success, response){
12221 delete this.activeRequest;
12223 this.fireEvent("loadexception", this, o, response);
12224 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12229 result = o.reader.read(response);
12231 this.fireEvent("loadexception", this, o, response, e);
12232 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12236 this.fireEvent("load", this, o, o.request.arg);
12237 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12241 update : function(dataSet){
12246 updateResponse : function(dataSet){
12251 * Ext JS Library 1.1.1
12252 * Copyright(c) 2006-2007, Ext JS, LLC.
12254 * Originally Released Under LGPL - original licence link has changed is not relivant.
12257 * <script type="text/javascript">
12261 * @class Roo.data.ScriptTagProxy
12262 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12263 * other than the originating domain of the running page.<br><br>
12265 * <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
12266 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12268 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12269 * source code that is used as the source inside a <script> tag.<br><br>
12271 * In order for the browser to process the returned data, the server must wrap the data object
12272 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12273 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12274 * depending on whether the callback name was passed:
12277 boolean scriptTag = false;
12278 String cb = request.getParameter("callback");
12281 response.setContentType("text/javascript");
12283 response.setContentType("application/x-json");
12285 Writer out = response.getWriter();
12287 out.write(cb + "(");
12289 out.print(dataBlock.toJsonString());
12296 * @param {Object} config A configuration object.
12298 Roo.data.ScriptTagProxy = function(config){
12299 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12300 Roo.apply(this, config);
12301 this.head = document.getElementsByTagName("head")[0];
12304 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12306 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12308 * @cfg {String} url The URL from which to request the data object.
12311 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12315 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12316 * the server the name of the callback function set up by the load call to process the returned data object.
12317 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12318 * javascript output which calls this named function passing the data object as its only parameter.
12320 callbackParam : "callback",
12322 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12323 * name to the request.
12328 * Load data from the configured URL, read the data object into
12329 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12330 * process that block using the passed callback.
12331 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12332 * for the request to the remote server.
12333 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12334 * object into a block of Roo.data.Records.
12335 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12336 * The function must be passed <ul>
12337 * <li>The Record block object</li>
12338 * <li>The "arg" argument from the load function</li>
12339 * <li>A boolean success indicator</li>
12341 * @param {Object} scope The scope in which to call the callback
12342 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12344 load : function(params, reader, callback, scope, arg){
12345 if(this.fireEvent("beforeload", this, params) !== false){
12347 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12349 var url = this.url;
12350 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12352 url += "&_dc=" + (new Date().getTime());
12354 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12357 cb : "stcCallback"+transId,
12358 scriptId : "stcScript"+transId,
12362 callback : callback,
12368 window[trans.cb] = function(o){
12369 conn.handleResponse(o, trans);
12372 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12374 if(this.autoAbort !== false){
12378 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12380 var script = document.createElement("script");
12381 script.setAttribute("src", url);
12382 script.setAttribute("type", "text/javascript");
12383 script.setAttribute("id", trans.scriptId);
12384 this.head.appendChild(script);
12386 this.trans = trans;
12388 callback.call(scope||this, null, arg, false);
12393 isLoading : function(){
12394 return this.trans ? true : false;
12398 * Abort the current server request.
12400 abort : function(){
12401 if(this.isLoading()){
12402 this.destroyTrans(this.trans);
12407 destroyTrans : function(trans, isLoaded){
12408 this.head.removeChild(document.getElementById(trans.scriptId));
12409 clearTimeout(trans.timeoutId);
12411 window[trans.cb] = undefined;
12413 delete window[trans.cb];
12416 // if hasn't been loaded, wait for load to remove it to prevent script error
12417 window[trans.cb] = function(){
12418 window[trans.cb] = undefined;
12420 delete window[trans.cb];
12427 handleResponse : function(o, trans){
12428 this.trans = false;
12429 this.destroyTrans(trans, true);
12432 result = trans.reader.readRecords(o);
12434 this.fireEvent("loadexception", this, o, trans.arg, e);
12435 trans.callback.call(trans.scope||window, null, trans.arg, false);
12438 this.fireEvent("load", this, o, trans.arg);
12439 trans.callback.call(trans.scope||window, result, trans.arg, true);
12443 handleFailure : function(trans){
12444 this.trans = false;
12445 this.destroyTrans(trans, false);
12446 this.fireEvent("loadexception", this, null, trans.arg);
12447 trans.callback.call(trans.scope||window, null, trans.arg, false);
12451 * Ext JS Library 1.1.1
12452 * Copyright(c) 2006-2007, Ext JS, LLC.
12454 * Originally Released Under LGPL - original licence link has changed is not relivant.
12457 * <script type="text/javascript">
12461 * @class Roo.data.JsonReader
12462 * @extends Roo.data.DataReader
12463 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12464 * based on mappings in a provided Roo.data.Record constructor.
12466 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12467 * in the reply previously.
12472 var RecordDef = Roo.data.Record.create([
12473 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12474 {name: 'occupation'} // This field will use "occupation" as the mapping.
12476 var myReader = new Roo.data.JsonReader({
12477 totalProperty: "results", // The property which contains the total dataset size (optional)
12478 root: "rows", // The property which contains an Array of row objects
12479 id: "id" // The property within each row object that provides an ID for the record (optional)
12483 * This would consume a JSON file like this:
12485 { 'results': 2, 'rows': [
12486 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12487 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12490 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12491 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12492 * paged from the remote server.
12493 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12494 * @cfg {String} root name of the property which contains the Array of row objects.
12495 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12496 * @cfg {Array} fields Array of field definition objects
12498 * Create a new JsonReader
12499 * @param {Object} meta Metadata configuration options
12500 * @param {Object} recordType Either an Array of field definition objects,
12501 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12503 Roo.data.JsonReader = function(meta, recordType){
12506 // set some defaults:
12507 Roo.applyIf(meta, {
12508 totalProperty: 'total',
12509 successProperty : 'success',
12514 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12516 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12519 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12520 * Used by Store query builder to append _requestMeta to params.
12523 metaFromRemote : false,
12525 * This method is only used by a DataProxy which has retrieved data from a remote server.
12526 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12527 * @return {Object} data A data block which is used by an Roo.data.Store object as
12528 * a cache of Roo.data.Records.
12530 read : function(response){
12531 var json = response.responseText;
12533 var o = /* eval:var:o */ eval("("+json+")");
12535 throw {message: "JsonReader.read: Json object not found"};
12541 this.metaFromRemote = true;
12542 this.meta = o.metaData;
12543 this.recordType = Roo.data.Record.create(o.metaData.fields);
12544 this.onMetaChange(this.meta, this.recordType, o);
12546 return this.readRecords(o);
12549 // private function a store will implement
12550 onMetaChange : function(meta, recordType, o){
12557 simpleAccess: function(obj, subsc) {
12564 getJsonAccessor: function(){
12566 return function(expr) {
12568 return(re.test(expr))
12569 ? new Function("obj", "return obj." + expr)
12574 return Roo.emptyFn;
12579 * Create a data block containing Roo.data.Records from an XML document.
12580 * @param {Object} o An object which contains an Array of row objects in the property specified
12581 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12582 * which contains the total size of the dataset.
12583 * @return {Object} data A data block which is used by an Roo.data.Store object as
12584 * a cache of Roo.data.Records.
12586 readRecords : function(o){
12588 * After any data loads, the raw JSON data is available for further custom processing.
12592 var s = this.meta, Record = this.recordType,
12593 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12595 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12597 if(s.totalProperty) {
12598 this.getTotal = this.getJsonAccessor(s.totalProperty);
12600 if(s.successProperty) {
12601 this.getSuccess = this.getJsonAccessor(s.successProperty);
12603 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12605 var g = this.getJsonAccessor(s.id);
12606 this.getId = function(rec) {
12608 return (r === undefined || r === "") ? null : r;
12611 this.getId = function(){return null;};
12614 for(var jj = 0; jj < fl; jj++){
12616 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12617 this.ef[jj] = this.getJsonAccessor(map);
12621 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12622 if(s.totalProperty){
12623 var vt = parseInt(this.getTotal(o), 10);
12628 if(s.successProperty){
12629 var vs = this.getSuccess(o);
12630 if(vs === false || vs === 'false'){
12635 for(var i = 0; i < c; i++){
12638 var id = this.getId(n);
12639 for(var j = 0; j < fl; j++){
12641 var v = this.ef[j](n);
12643 Roo.log('missing convert for ' + f.name);
12647 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12649 var record = new Record(values, id);
12651 records[i] = record;
12657 totalRecords : totalRecords
12662 * Ext JS Library 1.1.1
12663 * Copyright(c) 2006-2007, Ext JS, LLC.
12665 * Originally Released Under LGPL - original licence link has changed is not relivant.
12668 * <script type="text/javascript">
12672 * @class Roo.data.ArrayReader
12673 * @extends Roo.data.DataReader
12674 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12675 * Each element of that Array represents a row of data fields. The
12676 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12677 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12681 var RecordDef = Roo.data.Record.create([
12682 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12683 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12685 var myReader = new Roo.data.ArrayReader({
12686 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12690 * This would consume an Array like this:
12692 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12694 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12696 * Create a new JsonReader
12697 * @param {Object} meta Metadata configuration options.
12698 * @param {Object} recordType Either an Array of field definition objects
12699 * as specified to {@link Roo.data.Record#create},
12700 * or an {@link Roo.data.Record} object
12701 * created using {@link Roo.data.Record#create}.
12703 Roo.data.ArrayReader = function(meta, recordType){
12704 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12707 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12709 * Create a data block containing Roo.data.Records from an XML document.
12710 * @param {Object} o An Array of row objects which represents the dataset.
12711 * @return {Object} data A data block which is used by an Roo.data.Store object as
12712 * a cache of Roo.data.Records.
12714 readRecords : function(o){
12715 var sid = this.meta ? this.meta.id : null;
12716 var recordType = this.recordType, fields = recordType.prototype.fields;
12719 for(var i = 0; i < root.length; i++){
12722 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12723 for(var j = 0, jlen = fields.length; j < jlen; j++){
12724 var f = fields.items[j];
12725 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12726 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12728 values[f.name] = v;
12730 var record = new recordType(values, id);
12732 records[records.length] = record;
12736 totalRecords : records.length
12745 * @class Roo.bootstrap.ComboBox
12746 * @extends Roo.bootstrap.TriggerField
12747 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12748 * @cfg {Boolean} append (true|false) default false
12749 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12750 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12751 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12752 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12753 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12754 * @cfg {Boolean} animate default true
12755 * @cfg {Boolean} emptyResultText only for touch device
12756 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12757 * @cfg {String} emptyTitle default ''
12759 * Create a new ComboBox.
12760 * @param {Object} config Configuration options
12762 Roo.bootstrap.ComboBox = function(config){
12763 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12767 * Fires when the dropdown list is expanded
12768 * @param {Roo.bootstrap.ComboBox} combo This combo box
12773 * Fires when the dropdown list is collapsed
12774 * @param {Roo.bootstrap.ComboBox} combo This combo box
12778 * @event beforeselect
12779 * Fires before a list item is selected. Return false to cancel the selection.
12780 * @param {Roo.bootstrap.ComboBox} combo This combo box
12781 * @param {Roo.data.Record} record The data record returned from the underlying store
12782 * @param {Number} index The index of the selected item in the dropdown list
12784 'beforeselect' : true,
12787 * Fires when a list item is selected
12788 * @param {Roo.bootstrap.ComboBox} combo This combo box
12789 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12790 * @param {Number} index The index of the selected item in the dropdown list
12794 * @event beforequery
12795 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12796 * The event object passed has these properties:
12797 * @param {Roo.bootstrap.ComboBox} combo This combo box
12798 * @param {String} query The query
12799 * @param {Boolean} forceAll true to force "all" query
12800 * @param {Boolean} cancel true to cancel the query
12801 * @param {Object} e The query event object
12803 'beforequery': true,
12806 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12807 * @param {Roo.bootstrap.ComboBox} combo This combo box
12812 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12813 * @param {Roo.bootstrap.ComboBox} combo This combo box
12814 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12819 * Fires when the remove value from the combobox array
12820 * @param {Roo.bootstrap.ComboBox} combo This combo box
12824 * @event afterremove
12825 * Fires when the remove value from the combobox array
12826 * @param {Roo.bootstrap.ComboBox} combo This combo box
12828 'afterremove' : true,
12830 * @event specialfilter
12831 * Fires when specialfilter
12832 * @param {Roo.bootstrap.ComboBox} combo This combo box
12834 'specialfilter' : true,
12837 * Fires when tick the element
12838 * @param {Roo.bootstrap.ComboBox} combo This combo box
12842 * @event touchviewdisplay
12843 * Fires when touch view require special display (default is using displayField)
12844 * @param {Roo.bootstrap.ComboBox} combo This combo box
12845 * @param {Object} cfg set html .
12847 'touchviewdisplay' : true
12852 this.tickItems = [];
12854 this.selectedIndex = -1;
12855 if(this.mode == 'local'){
12856 if(config.queryDelay === undefined){
12857 this.queryDelay = 10;
12859 if(config.minChars === undefined){
12865 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12868 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12869 * rendering into an Roo.Editor, defaults to false)
12872 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12873 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12876 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12879 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12880 * the dropdown list (defaults to undefined, with no header element)
12884 * @cfg {String/Roo.Template} tpl The template to use to render the output
12888 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12890 listWidth: undefined,
12892 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12893 * mode = 'remote' or 'text' if mode = 'local')
12895 displayField: undefined,
12898 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12899 * mode = 'remote' or 'value' if mode = 'local').
12900 * Note: use of a valueField requires the user make a selection
12901 * in order for a value to be mapped.
12903 valueField: undefined,
12905 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12910 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12911 * field's data value (defaults to the underlying DOM element's name)
12913 hiddenName: undefined,
12915 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12919 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12921 selectedClass: 'active',
12924 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12928 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12929 * anchor positions (defaults to 'tl-bl')
12931 listAlign: 'tl-bl?',
12933 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12937 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12938 * query specified by the allQuery config option (defaults to 'query')
12940 triggerAction: 'query',
12942 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12943 * (defaults to 4, does not apply if editable = false)
12947 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12948 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12952 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12953 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12957 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12958 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12962 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12963 * when editable = true (defaults to false)
12965 selectOnFocus:false,
12967 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12969 queryParam: 'query',
12971 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12972 * when mode = 'remote' (defaults to 'Loading...')
12974 loadingText: 'Loading...',
12976 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12980 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12984 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12985 * traditional select (defaults to true)
12989 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12993 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12997 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12998 * listWidth has a higher value)
13002 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13003 * allow the user to set arbitrary text into the field (defaults to false)
13005 forceSelection:false,
13007 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13008 * if typeAhead = true (defaults to 250)
13010 typeAheadDelay : 250,
13012 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13013 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13015 valueNotFoundText : undefined,
13017 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13019 blockFocus : false,
13022 * @cfg {Boolean} disableClear Disable showing of clear button.
13024 disableClear : false,
13026 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13028 alwaysQuery : false,
13031 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13036 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13038 invalidClass : "has-warning",
13041 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13043 validClass : "has-success",
13046 * @cfg {Boolean} specialFilter (true|false) special filter default false
13048 specialFilter : false,
13051 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13053 mobileTouchView : true,
13056 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13058 useNativeIOS : false,
13061 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13063 mobile_restrict_height : false,
13065 ios_options : false,
13077 btnPosition : 'right',
13078 triggerList : true,
13079 showToggleBtn : true,
13081 emptyResultText: 'Empty',
13082 triggerText : 'Select',
13085 // element that contains real text value.. (when hidden is used..)
13087 getAutoCreate : function()
13092 * Render classic select for iso
13095 if(Roo.isIOS && this.useNativeIOS){
13096 cfg = this.getAutoCreateNativeIOS();
13104 if(Roo.isTouch && this.mobileTouchView){
13105 cfg = this.getAutoCreateTouchView();
13112 if(!this.tickable){
13113 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13118 * ComboBox with tickable selections
13121 var align = this.labelAlign || this.parentLabelAlign();
13124 cls : 'form-group roo-combobox-tickable' //input-group
13127 var btn_text_select = '';
13128 var btn_text_done = '';
13129 var btn_text_cancel = '';
13131 if (this.btn_text_show) {
13132 btn_text_select = 'Select';
13133 btn_text_done = 'Done';
13134 btn_text_cancel = 'Cancel';
13139 cls : 'tickable-buttons',
13144 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13145 //html : this.triggerText
13146 html: btn_text_select
13152 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13154 html: btn_text_done
13160 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13162 html: btn_text_cancel
13168 buttons.cn.unshift({
13170 cls: 'roo-select2-search-field-input'
13176 Roo.each(buttons.cn, function(c){
13178 c.cls += ' btn-' + _this.size;
13181 if (_this.disabled) {
13192 cls: 'form-hidden-field'
13196 cls: 'roo-select2-choices',
13200 cls: 'roo-select2-search-field',
13211 cls: 'roo-select2-container input-group roo-select2-container-multi',
13216 // cls: 'typeahead typeahead-long dropdown-menu',
13217 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13222 if(this.hasFeedback && !this.allowBlank){
13226 cls: 'glyphicon form-control-feedback'
13229 combobox.cn.push(feedback);
13233 if (align ==='left' && this.fieldLabel.length) {
13235 cfg.cls += ' roo-form-group-label-left';
13240 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13241 tooltip : 'This field is required'
13246 cls : 'control-label',
13247 html : this.fieldLabel
13259 var labelCfg = cfg.cn[1];
13260 var contentCfg = cfg.cn[2];
13263 if(this.indicatorpos == 'right'){
13269 cls : 'control-label',
13273 html : this.fieldLabel
13277 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13278 tooltip : 'This field is required'
13293 labelCfg = cfg.cn[0];
13294 contentCfg = cfg.cn[1];
13298 if(this.labelWidth > 12){
13299 labelCfg.style = "width: " + this.labelWidth + 'px';
13302 if(this.labelWidth < 13 && this.labelmd == 0){
13303 this.labelmd = this.labelWidth;
13306 if(this.labellg > 0){
13307 labelCfg.cls += ' col-lg-' + this.labellg;
13308 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13311 if(this.labelmd > 0){
13312 labelCfg.cls += ' col-md-' + this.labelmd;
13313 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13316 if(this.labelsm > 0){
13317 labelCfg.cls += ' col-sm-' + this.labelsm;
13318 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13321 if(this.labelxs > 0){
13322 labelCfg.cls += ' col-xs-' + this.labelxs;
13323 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13327 } else if ( this.fieldLabel.length) {
13328 // Roo.log(" label");
13332 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13333 tooltip : 'This field is required'
13337 //cls : 'input-group-addon',
13338 html : this.fieldLabel
13343 if(this.indicatorpos == 'right'){
13347 //cls : 'input-group-addon',
13348 html : this.fieldLabel
13352 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13353 tooltip : 'This field is required'
13362 // Roo.log(" no label && no align");
13369 ['xs','sm','md','lg'].map(function(size){
13370 if (settings[size]) {
13371 cfg.cls += ' col-' + size + '-' + settings[size];
13379 _initEventsCalled : false,
13382 initEvents: function()
13384 if (this._initEventsCalled) { // as we call render... prevent looping...
13387 this._initEventsCalled = true;
13390 throw "can not find store for combo";
13393 this.indicator = this.indicatorEl();
13395 this.store = Roo.factory(this.store, Roo.data);
13396 this.store.parent = this;
13398 // if we are building from html. then this element is so complex, that we can not really
13399 // use the rendered HTML.
13400 // so we have to trash and replace the previous code.
13401 if (Roo.XComponent.build_from_html) {
13402 // remove this element....
13403 var e = this.el.dom, k=0;
13404 while (e ) { e = e.previousSibling; ++k;}
13409 this.rendered = false;
13411 this.render(this.parent().getChildContainer(true), k);
13414 if(Roo.isIOS && this.useNativeIOS){
13415 this.initIOSView();
13423 if(Roo.isTouch && this.mobileTouchView){
13424 this.initTouchView();
13429 this.initTickableEvents();
13433 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13435 if(this.hiddenName){
13437 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13439 this.hiddenField.dom.value =
13440 this.hiddenValue !== undefined ? this.hiddenValue :
13441 this.value !== undefined ? this.value : '';
13443 // prevent input submission
13444 this.el.dom.removeAttribute('name');
13445 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13450 // this.el.dom.setAttribute('autocomplete', 'off');
13453 var cls = 'x-combo-list';
13455 //this.list = new Roo.Layer({
13456 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13462 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13463 _this.list.setWidth(lw);
13466 this.list.on('mouseover', this.onViewOver, this);
13467 this.list.on('mousemove', this.onViewMove, this);
13468 this.list.on('scroll', this.onViewScroll, this);
13471 this.list.swallowEvent('mousewheel');
13472 this.assetHeight = 0;
13475 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13476 this.assetHeight += this.header.getHeight();
13479 this.innerList = this.list.createChild({cls:cls+'-inner'});
13480 this.innerList.on('mouseover', this.onViewOver, this);
13481 this.innerList.on('mousemove', this.onViewMove, this);
13482 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13484 if(this.allowBlank && !this.pageSize && !this.disableClear){
13485 this.footer = this.list.createChild({cls:cls+'-ft'});
13486 this.pageTb = new Roo.Toolbar(this.footer);
13490 this.footer = this.list.createChild({cls:cls+'-ft'});
13491 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13492 {pageSize: this.pageSize});
13496 if (this.pageTb && this.allowBlank && !this.disableClear) {
13498 this.pageTb.add(new Roo.Toolbar.Fill(), {
13499 cls: 'x-btn-icon x-btn-clear',
13501 handler: function()
13504 _this.clearValue();
13505 _this.onSelect(false, -1);
13510 this.assetHeight += this.footer.getHeight();
13515 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13518 this.view = new Roo.View(this.list, this.tpl, {
13519 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13521 //this.view.wrapEl.setDisplayed(false);
13522 this.view.on('click', this.onViewClick, this);
13525 this.store.on('beforeload', this.onBeforeLoad, this);
13526 this.store.on('load', this.onLoad, this);
13527 this.store.on('loadexception', this.onLoadException, this);
13529 if(this.resizable){
13530 this.resizer = new Roo.Resizable(this.list, {
13531 pinned:true, handles:'se'
13533 this.resizer.on('resize', function(r, w, h){
13534 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13535 this.listWidth = w;
13536 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13537 this.restrictHeight();
13539 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13542 if(!this.editable){
13543 this.editable = true;
13544 this.setEditable(false);
13549 if (typeof(this.events.add.listeners) != 'undefined') {
13551 this.addicon = this.wrap.createChild(
13552 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13554 this.addicon.on('click', function(e) {
13555 this.fireEvent('add', this);
13558 if (typeof(this.events.edit.listeners) != 'undefined') {
13560 this.editicon = this.wrap.createChild(
13561 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13562 if (this.addicon) {
13563 this.editicon.setStyle('margin-left', '40px');
13565 this.editicon.on('click', function(e) {
13567 // we fire even if inothing is selected..
13568 this.fireEvent('edit', this, this.lastData );
13574 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13575 "up" : function(e){
13576 this.inKeyMode = true;
13580 "down" : function(e){
13581 if(!this.isExpanded()){
13582 this.onTriggerClick();
13584 this.inKeyMode = true;
13589 "enter" : function(e){
13590 // this.onViewClick();
13594 if(this.fireEvent("specialkey", this, e)){
13595 this.onViewClick(false);
13601 "esc" : function(e){
13605 "tab" : function(e){
13608 if(this.fireEvent("specialkey", this, e)){
13609 this.onViewClick(false);
13617 doRelay : function(foo, bar, hname){
13618 if(hname == 'down' || this.scope.isExpanded()){
13619 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13628 this.queryDelay = Math.max(this.queryDelay || 10,
13629 this.mode == 'local' ? 10 : 250);
13632 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13634 if(this.typeAhead){
13635 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13637 if(this.editable !== false){
13638 this.inputEl().on("keyup", this.onKeyUp, this);
13640 if(this.forceSelection){
13641 this.inputEl().on('blur', this.doForce, this);
13645 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13646 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13650 initTickableEvents: function()
13654 if(this.hiddenName){
13656 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13658 this.hiddenField.dom.value =
13659 this.hiddenValue !== undefined ? this.hiddenValue :
13660 this.value !== undefined ? this.value : '';
13662 // prevent input submission
13663 this.el.dom.removeAttribute('name');
13664 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13669 // this.list = this.el.select('ul.dropdown-menu',true).first();
13671 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13672 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13673 if(this.triggerList){
13674 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13677 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13678 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13680 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13681 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13683 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13684 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13686 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13687 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13688 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13691 this.cancelBtn.hide();
13696 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13697 _this.list.setWidth(lw);
13700 this.list.on('mouseover', this.onViewOver, this);
13701 this.list.on('mousemove', this.onViewMove, this);
13703 this.list.on('scroll', this.onViewScroll, this);
13706 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13707 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13710 this.view = new Roo.View(this.list, this.tpl, {
13715 selectedClass: this.selectedClass
13718 //this.view.wrapEl.setDisplayed(false);
13719 this.view.on('click', this.onViewClick, this);
13723 this.store.on('beforeload', this.onBeforeLoad, this);
13724 this.store.on('load', this.onLoad, this);
13725 this.store.on('loadexception', this.onLoadException, this);
13728 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13729 "up" : function(e){
13730 this.inKeyMode = true;
13734 "down" : function(e){
13735 this.inKeyMode = true;
13739 "enter" : function(e){
13740 if(this.fireEvent("specialkey", this, e)){
13741 this.onViewClick(false);
13747 "esc" : function(e){
13748 this.onTickableFooterButtonClick(e, false, false);
13751 "tab" : function(e){
13752 this.fireEvent("specialkey", this, e);
13754 this.onTickableFooterButtonClick(e, false, false);
13761 doRelay : function(e, fn, key){
13762 if(this.scope.isExpanded()){
13763 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13772 this.queryDelay = Math.max(this.queryDelay || 10,
13773 this.mode == 'local' ? 10 : 250);
13776 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13778 if(this.typeAhead){
13779 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13782 if(this.editable !== false){
13783 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13786 this.indicator = this.indicatorEl();
13788 if(this.indicator){
13789 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13790 this.indicator.hide();
13795 onDestroy : function(){
13797 this.view.setStore(null);
13798 this.view.el.removeAllListeners();
13799 this.view.el.remove();
13800 this.view.purgeListeners();
13803 this.list.dom.innerHTML = '';
13807 this.store.un('beforeload', this.onBeforeLoad, this);
13808 this.store.un('load', this.onLoad, this);
13809 this.store.un('loadexception', this.onLoadException, this);
13811 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13815 fireKey : function(e){
13816 if(e.isNavKeyPress() && !this.list.isVisible()){
13817 this.fireEvent("specialkey", this, e);
13822 onResize: function(w, h){
13823 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13825 // if(typeof w != 'number'){
13826 // // we do not handle it!?!?
13829 // var tw = this.trigger.getWidth();
13830 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13831 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13833 // this.inputEl().setWidth( this.adjustWidth('input', x));
13835 // //this.trigger.setStyle('left', x+'px');
13837 // if(this.list && this.listWidth === undefined){
13838 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13839 // this.list.setWidth(lw);
13840 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13848 * Allow or prevent the user from directly editing the field text. If false is passed,
13849 * the user will only be able to select from the items defined in the dropdown list. This method
13850 * is the runtime equivalent of setting the 'editable' config option at config time.
13851 * @param {Boolean} value True to allow the user to directly edit the field text
13853 setEditable : function(value){
13854 if(value == this.editable){
13857 this.editable = value;
13859 this.inputEl().dom.setAttribute('readOnly', true);
13860 this.inputEl().on('mousedown', this.onTriggerClick, this);
13861 this.inputEl().addClass('x-combo-noedit');
13863 this.inputEl().dom.setAttribute('readOnly', false);
13864 this.inputEl().un('mousedown', this.onTriggerClick, this);
13865 this.inputEl().removeClass('x-combo-noedit');
13871 onBeforeLoad : function(combo,opts){
13872 if(!this.hasFocus){
13876 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13878 this.restrictHeight();
13879 this.selectedIndex = -1;
13883 onLoad : function(){
13885 this.hasQuery = false;
13887 if(!this.hasFocus){
13891 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13892 this.loading.hide();
13895 if(this.store.getCount() > 0){
13898 this.restrictHeight();
13899 if(this.lastQuery == this.allQuery){
13900 if(this.editable && !this.tickable){
13901 this.inputEl().dom.select();
13905 !this.selectByValue(this.value, true) &&
13908 !this.store.lastOptions ||
13909 typeof(this.store.lastOptions.add) == 'undefined' ||
13910 this.store.lastOptions.add != true
13913 this.select(0, true);
13916 if(this.autoFocus){
13919 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13920 this.taTask.delay(this.typeAheadDelay);
13924 this.onEmptyResults();
13930 onLoadException : function()
13932 this.hasQuery = false;
13934 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13935 this.loading.hide();
13938 if(this.tickable && this.editable){
13943 // only causes errors at present
13944 //Roo.log(this.store.reader.jsonData);
13945 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13947 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13953 onTypeAhead : function(){
13954 if(this.store.getCount() > 0){
13955 var r = this.store.getAt(0);
13956 var newValue = r.data[this.displayField];
13957 var len = newValue.length;
13958 var selStart = this.getRawValue().length;
13960 if(selStart != len){
13961 this.setRawValue(newValue);
13962 this.selectText(selStart, newValue.length);
13968 onSelect : function(record, index){
13970 if(this.fireEvent('beforeselect', this, record, index) !== false){
13972 this.setFromData(index > -1 ? record.data : false);
13975 this.fireEvent('select', this, record, index);
13980 * Returns the currently selected field value or empty string if no value is set.
13981 * @return {String} value The selected value
13983 getValue : function()
13985 if(Roo.isIOS && this.useNativeIOS){
13986 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13990 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13993 if(this.valueField){
13994 return typeof this.value != 'undefined' ? this.value : '';
13996 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14000 getRawValue : function()
14002 if(Roo.isIOS && this.useNativeIOS){
14003 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14006 var v = this.inputEl().getValue();
14012 * Clears any text/value currently set in the field
14014 clearValue : function(){
14016 if(this.hiddenField){
14017 this.hiddenField.dom.value = '';
14020 this.setRawValue('');
14021 this.lastSelectionText = '';
14022 this.lastData = false;
14024 var close = this.closeTriggerEl();
14035 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14036 * will be displayed in the field. If the value does not match the data value of an existing item,
14037 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14038 * Otherwise the field will be blank (although the value will still be set).
14039 * @param {String} value The value to match
14041 setValue : function(v)
14043 if(Roo.isIOS && this.useNativeIOS){
14044 this.setIOSValue(v);
14054 if(this.valueField){
14055 var r = this.findRecord(this.valueField, v);
14057 text = r.data[this.displayField];
14058 }else if(this.valueNotFoundText !== undefined){
14059 text = this.valueNotFoundText;
14062 this.lastSelectionText = text;
14063 if(this.hiddenField){
14064 this.hiddenField.dom.value = v;
14066 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14069 var close = this.closeTriggerEl();
14072 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14078 * @property {Object} the last set data for the element
14083 * Sets the value of the field based on a object which is related to the record format for the store.
14084 * @param {Object} value the value to set as. or false on reset?
14086 setFromData : function(o){
14093 var dv = ''; // display value
14094 var vv = ''; // value value..
14096 if (this.displayField) {
14097 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14099 // this is an error condition!!!
14100 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14103 if(this.valueField){
14104 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14107 var close = this.closeTriggerEl();
14110 if(dv.length || vv * 1 > 0){
14112 this.blockFocus=true;
14118 if(this.hiddenField){
14119 this.hiddenField.dom.value = vv;
14121 this.lastSelectionText = dv;
14122 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14126 // no hidden field.. - we store the value in 'value', but still display
14127 // display field!!!!
14128 this.lastSelectionText = dv;
14129 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14136 reset : function(){
14137 // overridden so that last data is reset..
14144 this.setValue(this.originalValue);
14145 //this.clearInvalid();
14146 this.lastData = false;
14148 this.view.clearSelections();
14154 findRecord : function(prop, value){
14156 if(this.store.getCount() > 0){
14157 this.store.each(function(r){
14158 if(r.data[prop] == value){
14168 getName: function()
14170 // returns hidden if it's set..
14171 if (!this.rendered) {return ''};
14172 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14176 onViewMove : function(e, t){
14177 this.inKeyMode = false;
14181 onViewOver : function(e, t){
14182 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14185 var item = this.view.findItemFromChild(t);
14188 var index = this.view.indexOf(item);
14189 this.select(index, false);
14194 onViewClick : function(view, doFocus, el, e)
14196 var index = this.view.getSelectedIndexes()[0];
14198 var r = this.store.getAt(index);
14202 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14209 Roo.each(this.tickItems, function(v,k){
14211 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14213 _this.tickItems.splice(k, 1);
14215 if(typeof(e) == 'undefined' && view == false){
14216 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14228 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14229 this.tickItems.push(r.data);
14232 if(typeof(e) == 'undefined' && view == false){
14233 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14240 this.onSelect(r, index);
14242 if(doFocus !== false && !this.blockFocus){
14243 this.inputEl().focus();
14248 restrictHeight : function(){
14249 //this.innerList.dom.style.height = '';
14250 //var inner = this.innerList.dom;
14251 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14252 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14253 //this.list.beginUpdate();
14254 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14255 this.list.alignTo(this.inputEl(), this.listAlign);
14256 this.list.alignTo(this.inputEl(), this.listAlign);
14257 //this.list.endUpdate();
14261 onEmptyResults : function(){
14263 if(this.tickable && this.editable){
14264 this.hasFocus = false;
14265 this.restrictHeight();
14273 * Returns true if the dropdown list is expanded, else false.
14275 isExpanded : function(){
14276 return this.list.isVisible();
14280 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14281 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14282 * @param {String} value The data value of the item to select
14283 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14284 * selected item if it is not currently in view (defaults to true)
14285 * @return {Boolean} True if the value matched an item in the list, else false
14287 selectByValue : function(v, scrollIntoView){
14288 if(v !== undefined && v !== null){
14289 var r = this.findRecord(this.valueField || this.displayField, v);
14291 this.select(this.store.indexOf(r), scrollIntoView);
14299 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14300 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14301 * @param {Number} index The zero-based index of the list item to select
14302 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14303 * selected item if it is not currently in view (defaults to true)
14305 select : function(index, scrollIntoView){
14306 this.selectedIndex = index;
14307 this.view.select(index);
14308 if(scrollIntoView !== false){
14309 var el = this.view.getNode(index);
14311 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14314 this.list.scrollChildIntoView(el, false);
14320 selectNext : function(){
14321 var ct = this.store.getCount();
14323 if(this.selectedIndex == -1){
14325 }else if(this.selectedIndex < ct-1){
14326 this.select(this.selectedIndex+1);
14332 selectPrev : function(){
14333 var ct = this.store.getCount();
14335 if(this.selectedIndex == -1){
14337 }else if(this.selectedIndex != 0){
14338 this.select(this.selectedIndex-1);
14344 onKeyUp : function(e){
14345 if(this.editable !== false && !e.isSpecialKey()){
14346 this.lastKey = e.getKey();
14347 this.dqTask.delay(this.queryDelay);
14352 validateBlur : function(){
14353 return !this.list || !this.list.isVisible();
14357 initQuery : function(){
14359 var v = this.getRawValue();
14361 if(this.tickable && this.editable){
14362 v = this.tickableInputEl().getValue();
14369 doForce : function(){
14370 if(this.inputEl().dom.value.length > 0){
14371 this.inputEl().dom.value =
14372 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14378 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14379 * query allowing the query action to be canceled if needed.
14380 * @param {String} query The SQL query to execute
14381 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14382 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14383 * saved in the current store (defaults to false)
14385 doQuery : function(q, forceAll){
14387 if(q === undefined || q === null){
14392 forceAll: forceAll,
14396 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14401 forceAll = qe.forceAll;
14402 if(forceAll === true || (q.length >= this.minChars)){
14404 this.hasQuery = true;
14406 if(this.lastQuery != q || this.alwaysQuery){
14407 this.lastQuery = q;
14408 if(this.mode == 'local'){
14409 this.selectedIndex = -1;
14411 this.store.clearFilter();
14414 if(this.specialFilter){
14415 this.fireEvent('specialfilter', this);
14420 this.store.filter(this.displayField, q);
14423 this.store.fireEvent("datachanged", this.store);
14430 this.store.baseParams[this.queryParam] = q;
14432 var options = {params : this.getParams(q)};
14435 options.add = true;
14436 options.params.start = this.page * this.pageSize;
14439 this.store.load(options);
14442 * this code will make the page width larger, at the beginning, the list not align correctly,
14443 * we should expand the list on onLoad
14444 * so command out it
14449 this.selectedIndex = -1;
14454 this.loadNext = false;
14458 getParams : function(q){
14460 //p[this.queryParam] = q;
14464 p.limit = this.pageSize;
14470 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14472 collapse : function(){
14473 if(!this.isExpanded()){
14479 this.hasFocus = false;
14483 this.cancelBtn.hide();
14484 this.trigger.show();
14487 this.tickableInputEl().dom.value = '';
14488 this.tickableInputEl().blur();
14493 Roo.get(document).un('mousedown', this.collapseIf, this);
14494 Roo.get(document).un('mousewheel', this.collapseIf, this);
14495 if (!this.editable) {
14496 Roo.get(document).un('keydown', this.listKeyPress, this);
14498 this.fireEvent('collapse', this);
14504 collapseIf : function(e){
14505 var in_combo = e.within(this.el);
14506 var in_list = e.within(this.list);
14507 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14509 if (in_combo || in_list || is_list) {
14510 //e.stopPropagation();
14515 this.onTickableFooterButtonClick(e, false, false);
14523 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14525 expand : function(){
14527 if(this.isExpanded() || !this.hasFocus){
14531 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14532 this.list.setWidth(lw);
14538 this.restrictHeight();
14542 this.tickItems = Roo.apply([], this.item);
14545 this.cancelBtn.show();
14546 this.trigger.hide();
14549 this.tickableInputEl().focus();
14554 Roo.get(document).on('mousedown', this.collapseIf, this);
14555 Roo.get(document).on('mousewheel', this.collapseIf, this);
14556 if (!this.editable) {
14557 Roo.get(document).on('keydown', this.listKeyPress, this);
14560 this.fireEvent('expand', this);
14564 // Implements the default empty TriggerField.onTriggerClick function
14565 onTriggerClick : function(e)
14567 Roo.log('trigger click');
14569 if(this.disabled || !this.triggerList){
14574 this.loadNext = false;
14576 if(this.isExpanded()){
14578 if (!this.blockFocus) {
14579 this.inputEl().focus();
14583 this.hasFocus = true;
14584 if(this.triggerAction == 'all') {
14585 this.doQuery(this.allQuery, true);
14587 this.doQuery(this.getRawValue());
14589 if (!this.blockFocus) {
14590 this.inputEl().focus();
14595 onTickableTriggerClick : function(e)
14602 this.loadNext = false;
14603 this.hasFocus = true;
14605 if(this.triggerAction == 'all') {
14606 this.doQuery(this.allQuery, true);
14608 this.doQuery(this.getRawValue());
14612 onSearchFieldClick : function(e)
14614 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14615 this.onTickableFooterButtonClick(e, false, false);
14619 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14624 this.loadNext = false;
14625 this.hasFocus = true;
14627 if(this.triggerAction == 'all') {
14628 this.doQuery(this.allQuery, true);
14630 this.doQuery(this.getRawValue());
14634 listKeyPress : function(e)
14636 //Roo.log('listkeypress');
14637 // scroll to first matching element based on key pres..
14638 if (e.isSpecialKey()) {
14641 var k = String.fromCharCode(e.getKey()).toUpperCase();
14644 var csel = this.view.getSelectedNodes();
14645 var cselitem = false;
14647 var ix = this.view.indexOf(csel[0]);
14648 cselitem = this.store.getAt(ix);
14649 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14655 this.store.each(function(v) {
14657 // start at existing selection.
14658 if (cselitem.id == v.id) {
14664 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14665 match = this.store.indexOf(v);
14671 if (match === false) {
14672 return true; // no more action?
14675 this.view.select(match);
14676 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14677 sn.scrollIntoView(sn.dom.parentNode, false);
14680 onViewScroll : function(e, t){
14682 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){
14686 this.hasQuery = true;
14688 this.loading = this.list.select('.loading', true).first();
14690 if(this.loading === null){
14691 this.list.createChild({
14693 cls: 'loading roo-select2-more-results roo-select2-active',
14694 html: 'Loading more results...'
14697 this.loading = this.list.select('.loading', true).first();
14699 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14701 this.loading.hide();
14704 this.loading.show();
14709 this.loadNext = true;
14711 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14716 addItem : function(o)
14718 var dv = ''; // display value
14720 if (this.displayField) {
14721 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14723 // this is an error condition!!!
14724 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14731 var choice = this.choices.createChild({
14733 cls: 'roo-select2-search-choice',
14742 cls: 'roo-select2-search-choice-close fa fa-times',
14747 }, this.searchField);
14749 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14751 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14759 this.inputEl().dom.value = '';
14764 onRemoveItem : function(e, _self, o)
14766 e.preventDefault();
14768 this.lastItem = Roo.apply([], this.item);
14770 var index = this.item.indexOf(o.data) * 1;
14773 Roo.log('not this item?!');
14777 this.item.splice(index, 1);
14782 this.fireEvent('remove', this, e);
14788 syncValue : function()
14790 if(!this.item.length){
14797 Roo.each(this.item, function(i){
14798 if(_this.valueField){
14799 value.push(i[_this.valueField]);
14806 this.value = value.join(',');
14808 if(this.hiddenField){
14809 this.hiddenField.dom.value = this.value;
14812 this.store.fireEvent("datachanged", this.store);
14817 clearItem : function()
14819 if(!this.multiple){
14825 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14833 if(this.tickable && !Roo.isTouch){
14834 this.view.refresh();
14838 inputEl: function ()
14840 if(Roo.isIOS && this.useNativeIOS){
14841 return this.el.select('select.roo-ios-select', true).first();
14844 if(Roo.isTouch && this.mobileTouchView){
14845 return this.el.select('input.form-control',true).first();
14849 return this.searchField;
14852 return this.el.select('input.form-control',true).first();
14855 onTickableFooterButtonClick : function(e, btn, el)
14857 e.preventDefault();
14859 this.lastItem = Roo.apply([], this.item);
14861 if(btn && btn.name == 'cancel'){
14862 this.tickItems = Roo.apply([], this.item);
14871 Roo.each(this.tickItems, function(o){
14879 validate : function()
14881 if(this.getVisibilityEl().hasClass('hidden')){
14885 var v = this.getRawValue();
14888 v = this.getValue();
14891 if(this.disabled || this.allowBlank || v.length){
14896 this.markInvalid();
14900 tickableInputEl : function()
14902 if(!this.tickable || !this.editable){
14903 return this.inputEl();
14906 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14910 getAutoCreateTouchView : function()
14915 cls: 'form-group' //input-group
14921 type : this.inputType,
14922 cls : 'form-control x-combo-noedit',
14923 autocomplete: 'new-password',
14924 placeholder : this.placeholder || '',
14929 input.name = this.name;
14933 input.cls += ' input-' + this.size;
14936 if (this.disabled) {
14937 input.disabled = true;
14948 inputblock.cls += ' input-group';
14950 inputblock.cn.unshift({
14952 cls : 'input-group-addon',
14957 if(this.removable && !this.multiple){
14958 inputblock.cls += ' roo-removable';
14960 inputblock.cn.push({
14963 cls : 'roo-combo-removable-btn close'
14967 if(this.hasFeedback && !this.allowBlank){
14969 inputblock.cls += ' has-feedback';
14971 inputblock.cn.push({
14973 cls: 'glyphicon form-control-feedback'
14980 inputblock.cls += (this.before) ? '' : ' input-group';
14982 inputblock.cn.push({
14984 cls : 'input-group-addon',
14995 cls: 'form-hidden-field'
15009 cls: 'form-hidden-field'
15013 cls: 'roo-select2-choices',
15017 cls: 'roo-select2-search-field',
15030 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15036 if(!this.multiple && this.showToggleBtn){
15043 if (this.caret != false) {
15046 cls: 'fa fa-' + this.caret
15053 cls : 'input-group-addon btn dropdown-toggle',
15058 cls: 'combobox-clear',
15072 combobox.cls += ' roo-select2-container-multi';
15075 var align = this.labelAlign || this.parentLabelAlign();
15077 if (align ==='left' && this.fieldLabel.length) {
15082 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15083 tooltip : 'This field is required'
15087 cls : 'control-label',
15088 html : this.fieldLabel
15099 var labelCfg = cfg.cn[1];
15100 var contentCfg = cfg.cn[2];
15103 if(this.indicatorpos == 'right'){
15108 cls : 'control-label',
15112 html : this.fieldLabel
15116 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15117 tooltip : 'This field is required'
15130 labelCfg = cfg.cn[0];
15131 contentCfg = cfg.cn[1];
15136 if(this.labelWidth > 12){
15137 labelCfg.style = "width: " + this.labelWidth + 'px';
15140 if(this.labelWidth < 13 && this.labelmd == 0){
15141 this.labelmd = this.labelWidth;
15144 if(this.labellg > 0){
15145 labelCfg.cls += ' col-lg-' + this.labellg;
15146 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15149 if(this.labelmd > 0){
15150 labelCfg.cls += ' col-md-' + this.labelmd;
15151 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15154 if(this.labelsm > 0){
15155 labelCfg.cls += ' col-sm-' + this.labelsm;
15156 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15159 if(this.labelxs > 0){
15160 labelCfg.cls += ' col-xs-' + this.labelxs;
15161 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15165 } else if ( this.fieldLabel.length) {
15169 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15170 tooltip : 'This field is required'
15174 cls : 'control-label',
15175 html : this.fieldLabel
15186 if(this.indicatorpos == 'right'){
15190 cls : 'control-label',
15191 html : this.fieldLabel,
15195 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15196 tooltip : 'This field is required'
15213 var settings = this;
15215 ['xs','sm','md','lg'].map(function(size){
15216 if (settings[size]) {
15217 cfg.cls += ' col-' + size + '-' + settings[size];
15224 initTouchView : function()
15226 this.renderTouchView();
15228 this.touchViewEl.on('scroll', function(){
15229 this.el.dom.scrollTop = 0;
15232 this.originalValue = this.getValue();
15234 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15236 this.inputEl().on("click", this.showTouchView, this);
15237 if (this.triggerEl) {
15238 this.triggerEl.on("click", this.showTouchView, this);
15242 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15243 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15245 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15247 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15248 this.store.on('load', this.onTouchViewLoad, this);
15249 this.store.on('loadexception', this.onTouchViewLoadException, this);
15251 if(this.hiddenName){
15253 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15255 this.hiddenField.dom.value =
15256 this.hiddenValue !== undefined ? this.hiddenValue :
15257 this.value !== undefined ? this.value : '';
15259 this.el.dom.removeAttribute('name');
15260 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15264 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15265 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15268 if(this.removable && !this.multiple){
15269 var close = this.closeTriggerEl();
15271 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15272 close.on('click', this.removeBtnClick, this, close);
15276 * fix the bug in Safari iOS8
15278 this.inputEl().on("focus", function(e){
15279 document.activeElement.blur();
15282 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15289 renderTouchView : function()
15291 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15292 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15294 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15295 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15297 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15298 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15299 this.touchViewBodyEl.setStyle('overflow', 'auto');
15301 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15302 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15304 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15305 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15309 showTouchView : function()
15315 this.touchViewHeaderEl.hide();
15317 if(this.modalTitle.length){
15318 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15319 this.touchViewHeaderEl.show();
15322 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15323 this.touchViewEl.show();
15325 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15327 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15328 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15330 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15332 if(this.modalTitle.length){
15333 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15336 this.touchViewBodyEl.setHeight(bodyHeight);
15340 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15342 this.touchViewEl.addClass('in');
15345 if(this._touchViewMask){
15346 Roo.get(document.body).addClass("x-body-masked");
15347 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15348 this._touchViewMask.setStyle('z-index', 10000);
15349 this._touchViewMask.addClass('show');
15352 this.doTouchViewQuery();
15356 hideTouchView : function()
15358 this.touchViewEl.removeClass('in');
15362 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15364 this.touchViewEl.setStyle('display', 'none');
15367 if(this._touchViewMask){
15368 this._touchViewMask.removeClass('show');
15369 Roo.get(document.body).removeClass("x-body-masked");
15373 setTouchViewValue : function()
15380 Roo.each(this.tickItems, function(o){
15385 this.hideTouchView();
15388 doTouchViewQuery : function()
15397 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15401 if(!this.alwaysQuery || this.mode == 'local'){
15402 this.onTouchViewLoad();
15409 onTouchViewBeforeLoad : function(combo,opts)
15415 onTouchViewLoad : function()
15417 if(this.store.getCount() < 1){
15418 this.onTouchViewEmptyResults();
15422 this.clearTouchView();
15424 var rawValue = this.getRawValue();
15426 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15428 this.tickItems = [];
15430 this.store.data.each(function(d, rowIndex){
15431 var row = this.touchViewListGroup.createChild(template);
15433 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15434 row.addClass(d.data.cls);
15437 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15440 html : d.data[this.displayField]
15443 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15444 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15447 row.removeClass('selected');
15448 if(!this.multiple && this.valueField &&
15449 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15452 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15453 row.addClass('selected');
15456 if(this.multiple && this.valueField &&
15457 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15461 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15462 this.tickItems.push(d.data);
15465 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15469 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15471 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15473 if(this.modalTitle.length){
15474 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15477 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15479 if(this.mobile_restrict_height && listHeight < bodyHeight){
15480 this.touchViewBodyEl.setHeight(listHeight);
15485 if(firstChecked && listHeight > bodyHeight){
15486 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15491 onTouchViewLoadException : function()
15493 this.hideTouchView();
15496 onTouchViewEmptyResults : function()
15498 this.clearTouchView();
15500 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15502 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15506 clearTouchView : function()
15508 this.touchViewListGroup.dom.innerHTML = '';
15511 onTouchViewClick : function(e, el, o)
15513 e.preventDefault();
15516 var rowIndex = o.rowIndex;
15518 var r = this.store.getAt(rowIndex);
15520 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15522 if(!this.multiple){
15523 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15524 c.dom.removeAttribute('checked');
15527 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15529 this.setFromData(r.data);
15531 var close = this.closeTriggerEl();
15537 this.hideTouchView();
15539 this.fireEvent('select', this, r, rowIndex);
15544 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15545 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15546 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15550 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15551 this.addItem(r.data);
15552 this.tickItems.push(r.data);
15556 getAutoCreateNativeIOS : function()
15559 cls: 'form-group' //input-group,
15564 cls : 'roo-ios-select'
15568 combobox.name = this.name;
15571 if (this.disabled) {
15572 combobox.disabled = true;
15575 var settings = this;
15577 ['xs','sm','md','lg'].map(function(size){
15578 if (settings[size]) {
15579 cfg.cls += ' col-' + size + '-' + settings[size];
15589 initIOSView : function()
15591 this.store.on('load', this.onIOSViewLoad, this);
15596 onIOSViewLoad : function()
15598 if(this.store.getCount() < 1){
15602 this.clearIOSView();
15604 if(this.allowBlank) {
15606 var default_text = '-- SELECT --';
15608 if(this.placeholder.length){
15609 default_text = this.placeholder;
15612 if(this.emptyTitle.length){
15613 default_text += ' - ' + this.emptyTitle + ' -';
15616 var opt = this.inputEl().createChild({
15619 html : default_text
15623 o[this.valueField] = 0;
15624 o[this.displayField] = default_text;
15626 this.ios_options.push({
15633 this.store.data.each(function(d, rowIndex){
15637 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15638 html = d.data[this.displayField];
15643 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15644 value = d.data[this.valueField];
15653 if(this.value == d.data[this.valueField]){
15654 option['selected'] = true;
15657 var opt = this.inputEl().createChild(option);
15659 this.ios_options.push({
15666 this.inputEl().on('change', function(){
15667 this.fireEvent('select', this);
15672 clearIOSView: function()
15674 this.inputEl().dom.innerHTML = '';
15676 this.ios_options = [];
15679 setIOSValue: function(v)
15683 if(!this.ios_options){
15687 Roo.each(this.ios_options, function(opts){
15689 opts.el.dom.removeAttribute('selected');
15691 if(opts.data[this.valueField] != v){
15695 opts.el.dom.setAttribute('selected', true);
15701 * @cfg {Boolean} grow
15705 * @cfg {Number} growMin
15709 * @cfg {Number} growMax
15718 Roo.apply(Roo.bootstrap.ComboBox, {
15722 cls: 'modal-header',
15744 cls: 'list-group-item',
15748 cls: 'roo-combobox-list-group-item-value'
15752 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15766 listItemCheckbox : {
15768 cls: 'list-group-item',
15772 cls: 'roo-combobox-list-group-item-value'
15776 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15792 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15797 cls: 'modal-footer',
15805 cls: 'col-xs-6 text-left',
15808 cls: 'btn btn-danger roo-touch-view-cancel',
15814 cls: 'col-xs-6 text-right',
15817 cls: 'btn btn-success roo-touch-view-ok',
15828 Roo.apply(Roo.bootstrap.ComboBox, {
15830 touchViewTemplate : {
15832 cls: 'modal fade roo-combobox-touch-view',
15836 cls: 'modal-dialog',
15837 style : 'position:fixed', // we have to fix position....
15841 cls: 'modal-content',
15843 Roo.bootstrap.ComboBox.header,
15844 Roo.bootstrap.ComboBox.body,
15845 Roo.bootstrap.ComboBox.footer
15854 * Ext JS Library 1.1.1
15855 * Copyright(c) 2006-2007, Ext JS, LLC.
15857 * Originally Released Under LGPL - original licence link has changed is not relivant.
15860 * <script type="text/javascript">
15865 * @extends Roo.util.Observable
15866 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15867 * This class also supports single and multi selection modes. <br>
15868 * Create a data model bound view:
15870 var store = new Roo.data.Store(...);
15872 var view = new Roo.View({
15874 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15876 singleSelect: true,
15877 selectedClass: "ydataview-selected",
15881 // listen for node click?
15882 view.on("click", function(vw, index, node, e){
15883 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15887 dataModel.load("foobar.xml");
15889 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15891 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15892 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15894 * Note: old style constructor is still suported (container, template, config)
15897 * Create a new View
15898 * @param {Object} config The config object
15901 Roo.View = function(config, depreciated_tpl, depreciated_config){
15903 this.parent = false;
15905 if (typeof(depreciated_tpl) == 'undefined') {
15906 // new way.. - universal constructor.
15907 Roo.apply(this, config);
15908 this.el = Roo.get(this.el);
15911 this.el = Roo.get(config);
15912 this.tpl = depreciated_tpl;
15913 Roo.apply(this, depreciated_config);
15915 this.wrapEl = this.el.wrap().wrap();
15916 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15919 if(typeof(this.tpl) == "string"){
15920 this.tpl = new Roo.Template(this.tpl);
15922 // support xtype ctors..
15923 this.tpl = new Roo.factory(this.tpl, Roo);
15927 this.tpl.compile();
15932 * @event beforeclick
15933 * Fires before a click is processed. Returns false to cancel the default action.
15934 * @param {Roo.View} this
15935 * @param {Number} index The index of the target node
15936 * @param {HTMLElement} node The target node
15937 * @param {Roo.EventObject} e The raw event object
15939 "beforeclick" : true,
15942 * Fires when a template node is clicked.
15943 * @param {Roo.View} this
15944 * @param {Number} index The index of the target node
15945 * @param {HTMLElement} node The target node
15946 * @param {Roo.EventObject} e The raw event object
15951 * Fires when a template node is double clicked.
15952 * @param {Roo.View} this
15953 * @param {Number} index The index of the target node
15954 * @param {HTMLElement} node The target node
15955 * @param {Roo.EventObject} e The raw event object
15959 * @event contextmenu
15960 * Fires when a template node is right clicked.
15961 * @param {Roo.View} this
15962 * @param {Number} index The index of the target node
15963 * @param {HTMLElement} node The target node
15964 * @param {Roo.EventObject} e The raw event object
15966 "contextmenu" : true,
15968 * @event selectionchange
15969 * Fires when the selected nodes change.
15970 * @param {Roo.View} this
15971 * @param {Array} selections Array of the selected nodes
15973 "selectionchange" : true,
15976 * @event beforeselect
15977 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15978 * @param {Roo.View} this
15979 * @param {HTMLElement} node The node to be selected
15980 * @param {Array} selections Array of currently selected nodes
15982 "beforeselect" : true,
15984 * @event preparedata
15985 * Fires on every row to render, to allow you to change the data.
15986 * @param {Roo.View} this
15987 * @param {Object} data to be rendered (change this)
15989 "preparedata" : true
15997 "click": this.onClick,
15998 "dblclick": this.onDblClick,
15999 "contextmenu": this.onContextMenu,
16003 this.selections = [];
16005 this.cmp = new Roo.CompositeElementLite([]);
16007 this.store = Roo.factory(this.store, Roo.data);
16008 this.setStore(this.store, true);
16011 if ( this.footer && this.footer.xtype) {
16013 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16015 this.footer.dataSource = this.store;
16016 this.footer.container = fctr;
16017 this.footer = Roo.factory(this.footer, Roo);
16018 fctr.insertFirst(this.el);
16020 // this is a bit insane - as the paging toolbar seems to detach the el..
16021 // dom.parentNode.parentNode.parentNode
16022 // they get detached?
16026 Roo.View.superclass.constructor.call(this);
16031 Roo.extend(Roo.View, Roo.util.Observable, {
16034 * @cfg {Roo.data.Store} store Data store to load data from.
16039 * @cfg {String|Roo.Element} el The container element.
16044 * @cfg {String|Roo.Template} tpl The template used by this View
16048 * @cfg {String} dataName the named area of the template to use as the data area
16049 * Works with domtemplates roo-name="name"
16053 * @cfg {String} selectedClass The css class to add to selected nodes
16055 selectedClass : "x-view-selected",
16057 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16062 * @cfg {String} text to display on mask (default Loading)
16066 * @cfg {Boolean} multiSelect Allow multiple selection
16068 multiSelect : false,
16070 * @cfg {Boolean} singleSelect Allow single selection
16072 singleSelect: false,
16075 * @cfg {Boolean} toggleSelect - selecting
16077 toggleSelect : false,
16080 * @cfg {Boolean} tickable - selecting
16085 * Returns the element this view is bound to.
16086 * @return {Roo.Element}
16088 getEl : function(){
16089 return this.wrapEl;
16095 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16097 refresh : function(){
16098 //Roo.log('refresh');
16101 // if we are using something like 'domtemplate', then
16102 // the what gets used is:
16103 // t.applySubtemplate(NAME, data, wrapping data..)
16104 // the outer template then get' applied with
16105 // the store 'extra data'
16106 // and the body get's added to the
16107 // roo-name="data" node?
16108 // <span class='roo-tpl-{name}'></span> ?????
16112 this.clearSelections();
16113 this.el.update("");
16115 var records = this.store.getRange();
16116 if(records.length < 1) {
16118 // is this valid?? = should it render a template??
16120 this.el.update(this.emptyText);
16124 if (this.dataName) {
16125 this.el.update(t.apply(this.store.meta)); //????
16126 el = this.el.child('.roo-tpl-' + this.dataName);
16129 for(var i = 0, len = records.length; i < len; i++){
16130 var data = this.prepareData(records[i].data, i, records[i]);
16131 this.fireEvent("preparedata", this, data, i, records[i]);
16133 var d = Roo.apply({}, data);
16136 Roo.apply(d, {'roo-id' : Roo.id()});
16140 Roo.each(this.parent.item, function(item){
16141 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16144 Roo.apply(d, {'roo-data-checked' : 'checked'});
16148 html[html.length] = Roo.util.Format.trim(
16150 t.applySubtemplate(this.dataName, d, this.store.meta) :
16157 el.update(html.join(""));
16158 this.nodes = el.dom.childNodes;
16159 this.updateIndexes(0);
16164 * Function to override to reformat the data that is sent to
16165 * the template for each node.
16166 * DEPRICATED - use the preparedata event handler.
16167 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16168 * a JSON object for an UpdateManager bound view).
16170 prepareData : function(data, index, record)
16172 this.fireEvent("preparedata", this, data, index, record);
16176 onUpdate : function(ds, record){
16177 // Roo.log('on update');
16178 this.clearSelections();
16179 var index = this.store.indexOf(record);
16180 var n = this.nodes[index];
16181 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16182 n.parentNode.removeChild(n);
16183 this.updateIndexes(index, index);
16189 onAdd : function(ds, records, index)
16191 //Roo.log(['on Add', ds, records, index] );
16192 this.clearSelections();
16193 if(this.nodes.length == 0){
16197 var n = this.nodes[index];
16198 for(var i = 0, len = records.length; i < len; i++){
16199 var d = this.prepareData(records[i].data, i, records[i]);
16201 this.tpl.insertBefore(n, d);
16204 this.tpl.append(this.el, d);
16207 this.updateIndexes(index);
16210 onRemove : function(ds, record, index){
16211 // Roo.log('onRemove');
16212 this.clearSelections();
16213 var el = this.dataName ?
16214 this.el.child('.roo-tpl-' + this.dataName) :
16217 el.dom.removeChild(this.nodes[index]);
16218 this.updateIndexes(index);
16222 * Refresh an individual node.
16223 * @param {Number} index
16225 refreshNode : function(index){
16226 this.onUpdate(this.store, this.store.getAt(index));
16229 updateIndexes : function(startIndex, endIndex){
16230 var ns = this.nodes;
16231 startIndex = startIndex || 0;
16232 endIndex = endIndex || ns.length - 1;
16233 for(var i = startIndex; i <= endIndex; i++){
16234 ns[i].nodeIndex = i;
16239 * Changes the data store this view uses and refresh the view.
16240 * @param {Store} store
16242 setStore : function(store, initial){
16243 if(!initial && this.store){
16244 this.store.un("datachanged", this.refresh);
16245 this.store.un("add", this.onAdd);
16246 this.store.un("remove", this.onRemove);
16247 this.store.un("update", this.onUpdate);
16248 this.store.un("clear", this.refresh);
16249 this.store.un("beforeload", this.onBeforeLoad);
16250 this.store.un("load", this.onLoad);
16251 this.store.un("loadexception", this.onLoad);
16255 store.on("datachanged", this.refresh, this);
16256 store.on("add", this.onAdd, this);
16257 store.on("remove", this.onRemove, this);
16258 store.on("update", this.onUpdate, this);
16259 store.on("clear", this.refresh, this);
16260 store.on("beforeload", this.onBeforeLoad, this);
16261 store.on("load", this.onLoad, this);
16262 store.on("loadexception", this.onLoad, this);
16270 * onbeforeLoad - masks the loading area.
16273 onBeforeLoad : function(store,opts)
16275 //Roo.log('onBeforeLoad');
16277 this.el.update("");
16279 this.el.mask(this.mask ? this.mask : "Loading" );
16281 onLoad : function ()
16288 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16289 * @param {HTMLElement} node
16290 * @return {HTMLElement} The template node
16292 findItemFromChild : function(node){
16293 var el = this.dataName ?
16294 this.el.child('.roo-tpl-' + this.dataName,true) :
16297 if(!node || node.parentNode == el){
16300 var p = node.parentNode;
16301 while(p && p != el){
16302 if(p.parentNode == el){
16311 onClick : function(e){
16312 var item = this.findItemFromChild(e.getTarget());
16314 var index = this.indexOf(item);
16315 if(this.onItemClick(item, index, e) !== false){
16316 this.fireEvent("click", this, index, item, e);
16319 this.clearSelections();
16324 onContextMenu : function(e){
16325 var item = this.findItemFromChild(e.getTarget());
16327 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16332 onDblClick : function(e){
16333 var item = this.findItemFromChild(e.getTarget());
16335 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16339 onItemClick : function(item, index, e)
16341 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16344 if (this.toggleSelect) {
16345 var m = this.isSelected(item) ? 'unselect' : 'select';
16348 _t[m](item, true, false);
16351 if(this.multiSelect || this.singleSelect){
16352 if(this.multiSelect && e.shiftKey && this.lastSelection){
16353 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16355 this.select(item, this.multiSelect && e.ctrlKey);
16356 this.lastSelection = item;
16359 if(!this.tickable){
16360 e.preventDefault();
16368 * Get the number of selected nodes.
16371 getSelectionCount : function(){
16372 return this.selections.length;
16376 * Get the currently selected nodes.
16377 * @return {Array} An array of HTMLElements
16379 getSelectedNodes : function(){
16380 return this.selections;
16384 * Get the indexes of the selected nodes.
16387 getSelectedIndexes : function(){
16388 var indexes = [], s = this.selections;
16389 for(var i = 0, len = s.length; i < len; i++){
16390 indexes.push(s[i].nodeIndex);
16396 * Clear all selections
16397 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16399 clearSelections : function(suppressEvent){
16400 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16401 this.cmp.elements = this.selections;
16402 this.cmp.removeClass(this.selectedClass);
16403 this.selections = [];
16404 if(!suppressEvent){
16405 this.fireEvent("selectionchange", this, this.selections);
16411 * Returns true if the passed node is selected
16412 * @param {HTMLElement/Number} node The node or node index
16413 * @return {Boolean}
16415 isSelected : function(node){
16416 var s = this.selections;
16420 node = this.getNode(node);
16421 return s.indexOf(node) !== -1;
16426 * @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
16427 * @param {Boolean} keepExisting (optional) true to keep existing selections
16428 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16430 select : function(nodeInfo, keepExisting, suppressEvent){
16431 if(nodeInfo instanceof Array){
16433 this.clearSelections(true);
16435 for(var i = 0, len = nodeInfo.length; i < len; i++){
16436 this.select(nodeInfo[i], true, true);
16440 var node = this.getNode(nodeInfo);
16441 if(!node || this.isSelected(node)){
16442 return; // already selected.
16445 this.clearSelections(true);
16448 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16449 Roo.fly(node).addClass(this.selectedClass);
16450 this.selections.push(node);
16451 if(!suppressEvent){
16452 this.fireEvent("selectionchange", this, this.selections);
16460 * @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
16461 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16462 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16464 unselect : function(nodeInfo, keepExisting, suppressEvent)
16466 if(nodeInfo instanceof Array){
16467 Roo.each(this.selections, function(s) {
16468 this.unselect(s, nodeInfo);
16472 var node = this.getNode(nodeInfo);
16473 if(!node || !this.isSelected(node)){
16474 //Roo.log("not selected");
16475 return; // not selected.
16479 Roo.each(this.selections, function(s) {
16481 Roo.fly(node).removeClass(this.selectedClass);
16488 this.selections= ns;
16489 this.fireEvent("selectionchange", this, this.selections);
16493 * Gets a template node.
16494 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16495 * @return {HTMLElement} The node or null if it wasn't found
16497 getNode : function(nodeInfo){
16498 if(typeof nodeInfo == "string"){
16499 return document.getElementById(nodeInfo);
16500 }else if(typeof nodeInfo == "number"){
16501 return this.nodes[nodeInfo];
16507 * Gets a range template nodes.
16508 * @param {Number} startIndex
16509 * @param {Number} endIndex
16510 * @return {Array} An array of nodes
16512 getNodes : function(start, end){
16513 var ns = this.nodes;
16514 start = start || 0;
16515 end = typeof end == "undefined" ? ns.length - 1 : end;
16518 for(var i = start; i <= end; i++){
16522 for(var i = start; i >= end; i--){
16530 * Finds the index of the passed node
16531 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16532 * @return {Number} The index of the node or -1
16534 indexOf : function(node){
16535 node = this.getNode(node);
16536 if(typeof node.nodeIndex == "number"){
16537 return node.nodeIndex;
16539 var ns = this.nodes;
16540 for(var i = 0, len = ns.length; i < len; i++){
16551 * based on jquery fullcalendar
16555 Roo.bootstrap = Roo.bootstrap || {};
16557 * @class Roo.bootstrap.Calendar
16558 * @extends Roo.bootstrap.Component
16559 * Bootstrap Calendar class
16560 * @cfg {Boolean} loadMask (true|false) default false
16561 * @cfg {Object} header generate the user specific header of the calendar, default false
16564 * Create a new Container
16565 * @param {Object} config The config object
16570 Roo.bootstrap.Calendar = function(config){
16571 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16575 * Fires when a date is selected
16576 * @param {DatePicker} this
16577 * @param {Date} date The selected date
16581 * @event monthchange
16582 * Fires when the displayed month changes
16583 * @param {DatePicker} this
16584 * @param {Date} date The selected month
16586 'monthchange': true,
16588 * @event evententer
16589 * Fires when mouse over an event
16590 * @param {Calendar} this
16591 * @param {event} Event
16593 'evententer': true,
16595 * @event eventleave
16596 * Fires when the mouse leaves an
16597 * @param {Calendar} this
16600 'eventleave': true,
16602 * @event eventclick
16603 * Fires when the mouse click an
16604 * @param {Calendar} this
16613 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16616 * @cfg {Number} startDay
16617 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16625 getAutoCreate : function(){
16628 var fc_button = function(name, corner, style, content ) {
16629 return Roo.apply({},{
16631 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16633 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16636 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16647 style : 'width:100%',
16654 cls : 'fc-header-left',
16656 fc_button('prev', 'left', 'arrow', '‹' ),
16657 fc_button('next', 'right', 'arrow', '›' ),
16658 { tag: 'span', cls: 'fc-header-space' },
16659 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16667 cls : 'fc-header-center',
16671 cls: 'fc-header-title',
16674 html : 'month / year'
16682 cls : 'fc-header-right',
16684 /* fc_button('month', 'left', '', 'month' ),
16685 fc_button('week', '', '', 'week' ),
16686 fc_button('day', 'right', '', 'day' )
16698 header = this.header;
16701 var cal_heads = function() {
16703 // fixme - handle this.
16705 for (var i =0; i < Date.dayNames.length; i++) {
16706 var d = Date.dayNames[i];
16709 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16710 html : d.substring(0,3)
16714 ret[0].cls += ' fc-first';
16715 ret[6].cls += ' fc-last';
16718 var cal_cell = function(n) {
16721 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16726 cls: 'fc-day-number',
16730 cls: 'fc-day-content',
16734 style: 'position: relative;' // height: 17px;
16746 var cal_rows = function() {
16749 for (var r = 0; r < 6; r++) {
16756 for (var i =0; i < Date.dayNames.length; i++) {
16757 var d = Date.dayNames[i];
16758 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16761 row.cn[0].cls+=' fc-first';
16762 row.cn[0].cn[0].style = 'min-height:90px';
16763 row.cn[6].cls+=' fc-last';
16767 ret[0].cls += ' fc-first';
16768 ret[4].cls += ' fc-prev-last';
16769 ret[5].cls += ' fc-last';
16776 cls: 'fc-border-separate',
16777 style : 'width:100%',
16785 cls : 'fc-first fc-last',
16803 cls : 'fc-content',
16804 style : "position: relative;",
16807 cls : 'fc-view fc-view-month fc-grid',
16808 style : 'position: relative',
16809 unselectable : 'on',
16812 cls : 'fc-event-container',
16813 style : 'position:absolute;z-index:8;top:0;left:0;'
16831 initEvents : function()
16834 throw "can not find store for calendar";
16840 style: "text-align:center",
16844 style: "background-color:white;width:50%;margin:250 auto",
16848 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16859 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16861 var size = this.el.select('.fc-content', true).first().getSize();
16862 this.maskEl.setSize(size.width, size.height);
16863 this.maskEl.enableDisplayMode("block");
16864 if(!this.loadMask){
16865 this.maskEl.hide();
16868 this.store = Roo.factory(this.store, Roo.data);
16869 this.store.on('load', this.onLoad, this);
16870 this.store.on('beforeload', this.onBeforeLoad, this);
16874 this.cells = this.el.select('.fc-day',true);
16875 //Roo.log(this.cells);
16876 this.textNodes = this.el.query('.fc-day-number');
16877 this.cells.addClassOnOver('fc-state-hover');
16879 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16880 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16881 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16882 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16884 this.on('monthchange', this.onMonthChange, this);
16886 this.update(new Date().clearTime());
16889 resize : function() {
16890 var sz = this.el.getSize();
16892 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16893 this.el.select('.fc-day-content div',true).setHeight(34);
16898 showPrevMonth : function(e){
16899 this.update(this.activeDate.add("mo", -1));
16901 showToday : function(e){
16902 this.update(new Date().clearTime());
16905 showNextMonth : function(e){
16906 this.update(this.activeDate.add("mo", 1));
16910 showPrevYear : function(){
16911 this.update(this.activeDate.add("y", -1));
16915 showNextYear : function(){
16916 this.update(this.activeDate.add("y", 1));
16921 update : function(date)
16923 var vd = this.activeDate;
16924 this.activeDate = date;
16925 // if(vd && this.el){
16926 // var t = date.getTime();
16927 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16928 // Roo.log('using add remove');
16930 // this.fireEvent('monthchange', this, date);
16932 // this.cells.removeClass("fc-state-highlight");
16933 // this.cells.each(function(c){
16934 // if(c.dateValue == t){
16935 // c.addClass("fc-state-highlight");
16936 // setTimeout(function(){
16937 // try{c.dom.firstChild.focus();}catch(e){}
16947 var days = date.getDaysInMonth();
16949 var firstOfMonth = date.getFirstDateOfMonth();
16950 var startingPos = firstOfMonth.getDay()-this.startDay;
16952 if(startingPos < this.startDay){
16956 var pm = date.add(Date.MONTH, -1);
16957 var prevStart = pm.getDaysInMonth()-startingPos;
16959 this.cells = this.el.select('.fc-day',true);
16960 this.textNodes = this.el.query('.fc-day-number');
16961 this.cells.addClassOnOver('fc-state-hover');
16963 var cells = this.cells.elements;
16964 var textEls = this.textNodes;
16966 Roo.each(cells, function(cell){
16967 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16970 days += startingPos;
16972 // convert everything to numbers so it's fast
16973 var day = 86400000;
16974 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16977 //Roo.log(prevStart);
16979 var today = new Date().clearTime().getTime();
16980 var sel = date.clearTime().getTime();
16981 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16982 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16983 var ddMatch = this.disabledDatesRE;
16984 var ddText = this.disabledDatesText;
16985 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16986 var ddaysText = this.disabledDaysText;
16987 var format = this.format;
16989 var setCellClass = function(cal, cell){
16993 //Roo.log('set Cell Class');
16995 var t = d.getTime();
16999 cell.dateValue = t;
17001 cell.className += " fc-today";
17002 cell.className += " fc-state-highlight";
17003 cell.title = cal.todayText;
17006 // disable highlight in other month..
17007 //cell.className += " fc-state-highlight";
17012 cell.className = " fc-state-disabled";
17013 cell.title = cal.minText;
17017 cell.className = " fc-state-disabled";
17018 cell.title = cal.maxText;
17022 if(ddays.indexOf(d.getDay()) != -1){
17023 cell.title = ddaysText;
17024 cell.className = " fc-state-disabled";
17027 if(ddMatch && format){
17028 var fvalue = d.dateFormat(format);
17029 if(ddMatch.test(fvalue)){
17030 cell.title = ddText.replace("%0", fvalue);
17031 cell.className = " fc-state-disabled";
17035 if (!cell.initialClassName) {
17036 cell.initialClassName = cell.dom.className;
17039 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17044 for(; i < startingPos; i++) {
17045 textEls[i].innerHTML = (++prevStart);
17046 d.setDate(d.getDate()+1);
17048 cells[i].className = "fc-past fc-other-month";
17049 setCellClass(this, cells[i]);
17054 for(; i < days; i++){
17055 intDay = i - startingPos + 1;
17056 textEls[i].innerHTML = (intDay);
17057 d.setDate(d.getDate()+1);
17059 cells[i].className = ''; // "x-date-active";
17060 setCellClass(this, cells[i]);
17064 for(; i < 42; i++) {
17065 textEls[i].innerHTML = (++extraDays);
17066 d.setDate(d.getDate()+1);
17068 cells[i].className = "fc-future fc-other-month";
17069 setCellClass(this, cells[i]);
17072 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17074 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17076 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17077 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17079 if(totalRows != 6){
17080 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17081 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17084 this.fireEvent('monthchange', this, date);
17088 if(!this.internalRender){
17089 var main = this.el.dom.firstChild;
17090 var w = main.offsetWidth;
17091 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17092 Roo.fly(main).setWidth(w);
17093 this.internalRender = true;
17094 // opera does not respect the auto grow header center column
17095 // then, after it gets a width opera refuses to recalculate
17096 // without a second pass
17097 if(Roo.isOpera && !this.secondPass){
17098 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17099 this.secondPass = true;
17100 this.update.defer(10, this, [date]);
17107 findCell : function(dt) {
17108 dt = dt.clearTime().getTime();
17110 this.cells.each(function(c){
17111 //Roo.log("check " +c.dateValue + '?=' + dt);
17112 if(c.dateValue == dt){
17122 findCells : function(ev) {
17123 var s = ev.start.clone().clearTime().getTime();
17125 var e= ev.end.clone().clearTime().getTime();
17128 this.cells.each(function(c){
17129 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17131 if(c.dateValue > e){
17134 if(c.dateValue < s){
17143 // findBestRow: function(cells)
17147 // for (var i =0 ; i < cells.length;i++) {
17148 // ret = Math.max(cells[i].rows || 0,ret);
17155 addItem : function(ev)
17157 // look for vertical location slot in
17158 var cells = this.findCells(ev);
17160 // ev.row = this.findBestRow(cells);
17162 // work out the location.
17166 for(var i =0; i < cells.length; i++) {
17168 cells[i].row = cells[0].row;
17171 cells[i].row = cells[i].row + 1;
17181 if (crow.start.getY() == cells[i].getY()) {
17183 crow.end = cells[i];
17200 cells[0].events.push(ev);
17202 this.calevents.push(ev);
17205 clearEvents: function() {
17207 if(!this.calevents){
17211 Roo.each(this.cells.elements, function(c){
17217 Roo.each(this.calevents, function(e) {
17218 Roo.each(e.els, function(el) {
17219 el.un('mouseenter' ,this.onEventEnter, this);
17220 el.un('mouseleave' ,this.onEventLeave, this);
17225 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17231 renderEvents: function()
17235 this.cells.each(function(c) {
17244 if(c.row != c.events.length){
17245 r = 4 - (4 - (c.row - c.events.length));
17248 c.events = ev.slice(0, r);
17249 c.more = ev.slice(r);
17251 if(c.more.length && c.more.length == 1){
17252 c.events.push(c.more.pop());
17255 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17259 this.cells.each(function(c) {
17261 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17264 for (var e = 0; e < c.events.length; e++){
17265 var ev = c.events[e];
17266 var rows = ev.rows;
17268 for(var i = 0; i < rows.length; i++) {
17270 // how many rows should it span..
17273 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17274 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17276 unselectable : "on",
17279 cls: 'fc-event-inner',
17283 // cls: 'fc-event-time',
17284 // html : cells.length > 1 ? '' : ev.time
17288 cls: 'fc-event-title',
17289 html : String.format('{0}', ev.title)
17296 cls: 'ui-resizable-handle ui-resizable-e',
17297 html : '  '
17304 cfg.cls += ' fc-event-start';
17306 if ((i+1) == rows.length) {
17307 cfg.cls += ' fc-event-end';
17310 var ctr = _this.el.select('.fc-event-container',true).first();
17311 var cg = ctr.createChild(cfg);
17313 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17314 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17316 var r = (c.more.length) ? 1 : 0;
17317 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17318 cg.setWidth(ebox.right - sbox.x -2);
17320 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17321 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17322 cg.on('click', _this.onEventClick, _this, ev);
17333 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17334 style : 'position: absolute',
17335 unselectable : "on",
17338 cls: 'fc-event-inner',
17342 cls: 'fc-event-title',
17350 cls: 'ui-resizable-handle ui-resizable-e',
17351 html : '  '
17357 var ctr = _this.el.select('.fc-event-container',true).first();
17358 var cg = ctr.createChild(cfg);
17360 var sbox = c.select('.fc-day-content',true).first().getBox();
17361 var ebox = c.select('.fc-day-content',true).first().getBox();
17363 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17364 cg.setWidth(ebox.right - sbox.x -2);
17366 cg.on('click', _this.onMoreEventClick, _this, c.more);
17376 onEventEnter: function (e, el,event,d) {
17377 this.fireEvent('evententer', this, el, event);
17380 onEventLeave: function (e, el,event,d) {
17381 this.fireEvent('eventleave', this, el, event);
17384 onEventClick: function (e, el,event,d) {
17385 this.fireEvent('eventclick', this, el, event);
17388 onMonthChange: function () {
17392 onMoreEventClick: function(e, el, more)
17396 this.calpopover.placement = 'right';
17397 this.calpopover.setTitle('More');
17399 this.calpopover.setContent('');
17401 var ctr = this.calpopover.el.select('.popover-content', true).first();
17403 Roo.each(more, function(m){
17405 cls : 'fc-event-hori fc-event-draggable',
17408 var cg = ctr.createChild(cfg);
17410 cg.on('click', _this.onEventClick, _this, m);
17413 this.calpopover.show(el);
17418 onLoad: function ()
17420 this.calevents = [];
17423 if(this.store.getCount() > 0){
17424 this.store.data.each(function(d){
17427 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17428 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17429 time : d.data.start_time,
17430 title : d.data.title,
17431 description : d.data.description,
17432 venue : d.data.venue
17437 this.renderEvents();
17439 if(this.calevents.length && this.loadMask){
17440 this.maskEl.hide();
17444 onBeforeLoad: function()
17446 this.clearEvents();
17448 this.maskEl.show();
17462 * @class Roo.bootstrap.Popover
17463 * @extends Roo.bootstrap.Component
17464 * Bootstrap Popover class
17465 * @cfg {String} html contents of the popover (or false to use children..)
17466 * @cfg {String} title of popover (or false to hide)
17467 * @cfg {String} placement how it is placed
17468 * @cfg {String} trigger click || hover (or false to trigger manually)
17469 * @cfg {String} over what (parent or false to trigger manually.)
17470 * @cfg {Number} delay - delay before showing
17473 * Create a new Popover
17474 * @param {Object} config The config object
17477 Roo.bootstrap.Popover = function(config){
17478 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17484 * After the popover show
17486 * @param {Roo.bootstrap.Popover} this
17491 * After the popover hide
17493 * @param {Roo.bootstrap.Popover} this
17499 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17501 title: 'Fill in a title',
17504 placement : 'right',
17505 trigger : 'hover', // hover
17511 can_build_overlaid : false,
17513 getChildContainer : function()
17515 return this.el.select('.popover-content',true).first();
17518 getAutoCreate : function(){
17521 cls : 'popover roo-dynamic',
17522 style: 'display:block',
17528 cls : 'popover-inner',
17532 cls: 'popover-title',
17536 cls : 'popover-content',
17547 setTitle: function(str)
17550 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17552 setContent: function(str)
17555 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17557 // as it get's added to the bottom of the page.
17558 onRender : function(ct, position)
17560 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17562 var cfg = Roo.apply({}, this.getAutoCreate());
17566 cfg.cls += ' ' + this.cls;
17569 cfg.style = this.style;
17571 //Roo.log("adding to ");
17572 this.el = Roo.get(document.body).createChild(cfg, position);
17573 // Roo.log(this.el);
17578 initEvents : function()
17580 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17581 this.el.enableDisplayMode('block');
17583 if (this.over === false) {
17586 if (this.triggers === false) {
17589 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17590 var triggers = this.trigger ? this.trigger.split(' ') : [];
17591 Roo.each(triggers, function(trigger) {
17593 if (trigger == 'click') {
17594 on_el.on('click', this.toggle, this);
17595 } else if (trigger != 'manual') {
17596 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17597 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17599 on_el.on(eventIn ,this.enter, this);
17600 on_el.on(eventOut, this.leave, this);
17611 toggle : function () {
17612 this.hoverState == 'in' ? this.leave() : this.enter();
17615 enter : function () {
17617 clearTimeout(this.timeout);
17619 this.hoverState = 'in';
17621 if (!this.delay || !this.delay.show) {
17626 this.timeout = setTimeout(function () {
17627 if (_t.hoverState == 'in') {
17630 }, this.delay.show)
17633 leave : function() {
17634 clearTimeout(this.timeout);
17636 this.hoverState = 'out';
17638 if (!this.delay || !this.delay.hide) {
17643 this.timeout = setTimeout(function () {
17644 if (_t.hoverState == 'out') {
17647 }, this.delay.hide)
17650 show : function (on_el)
17653 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17657 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17658 if (this.html !== false) {
17659 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17661 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17662 if (!this.title.length) {
17663 this.el.select('.popover-title',true).hide();
17666 var placement = typeof this.placement == 'function' ?
17667 this.placement.call(this, this.el, on_el) :
17670 var autoToken = /\s?auto?\s?/i;
17671 var autoPlace = autoToken.test(placement);
17673 placement = placement.replace(autoToken, '') || 'top';
17677 //this.el.setXY([0,0]);
17679 this.el.dom.style.display='block';
17680 this.el.addClass(placement);
17682 //this.el.appendTo(on_el);
17684 var p = this.getPosition();
17685 var box = this.el.getBox();
17690 var align = Roo.bootstrap.Popover.alignment[placement];
17693 this.el.alignTo(on_el, align[0],align[1]);
17694 //var arrow = this.el.select('.arrow',true).first();
17695 //arrow.set(align[2],
17697 this.el.addClass('in');
17700 if (this.el.hasClass('fade')) {
17704 this.hoverState = 'in';
17706 this.fireEvent('show', this);
17711 this.el.setXY([0,0]);
17712 this.el.removeClass('in');
17714 this.hoverState = null;
17716 this.fireEvent('hide', this);
17721 Roo.bootstrap.Popover.alignment = {
17722 'left' : ['r-l', [-10,0], 'right'],
17723 'right' : ['l-r', [10,0], 'left'],
17724 'bottom' : ['t-b', [0,10], 'top'],
17725 'top' : [ 'b-t', [0,-10], 'bottom']
17736 * @class Roo.bootstrap.Progress
17737 * @extends Roo.bootstrap.Component
17738 * Bootstrap Progress class
17739 * @cfg {Boolean} striped striped of the progress bar
17740 * @cfg {Boolean} active animated of the progress bar
17744 * Create a new Progress
17745 * @param {Object} config The config object
17748 Roo.bootstrap.Progress = function(config){
17749 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17752 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17757 getAutoCreate : function(){
17765 cfg.cls += ' progress-striped';
17769 cfg.cls += ' active';
17788 * @class Roo.bootstrap.ProgressBar
17789 * @extends Roo.bootstrap.Component
17790 * Bootstrap ProgressBar class
17791 * @cfg {Number} aria_valuenow aria-value now
17792 * @cfg {Number} aria_valuemin aria-value min
17793 * @cfg {Number} aria_valuemax aria-value max
17794 * @cfg {String} label label for the progress bar
17795 * @cfg {String} panel (success | info | warning | danger )
17796 * @cfg {String} role role of the progress bar
17797 * @cfg {String} sr_only text
17801 * Create a new ProgressBar
17802 * @param {Object} config The config object
17805 Roo.bootstrap.ProgressBar = function(config){
17806 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17809 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17813 aria_valuemax : 100,
17819 getAutoCreate : function()
17824 cls: 'progress-bar',
17825 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17837 cfg.role = this.role;
17840 if(this.aria_valuenow){
17841 cfg['aria-valuenow'] = this.aria_valuenow;
17844 if(this.aria_valuemin){
17845 cfg['aria-valuemin'] = this.aria_valuemin;
17848 if(this.aria_valuemax){
17849 cfg['aria-valuemax'] = this.aria_valuemax;
17852 if(this.label && !this.sr_only){
17853 cfg.html = this.label;
17857 cfg.cls += ' progress-bar-' + this.panel;
17863 update : function(aria_valuenow)
17865 this.aria_valuenow = aria_valuenow;
17867 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17882 * @class Roo.bootstrap.TabGroup
17883 * @extends Roo.bootstrap.Column
17884 * Bootstrap Column class
17885 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17886 * @cfg {Boolean} carousel true to make the group behave like a carousel
17887 * @cfg {Boolean} bullets show bullets for the panels
17888 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17889 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17890 * @cfg {Boolean} showarrow (true|false) show arrow default true
17893 * Create a new TabGroup
17894 * @param {Object} config The config object
17897 Roo.bootstrap.TabGroup = function(config){
17898 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17900 this.navId = Roo.id();
17903 Roo.bootstrap.TabGroup.register(this);
17907 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17910 transition : false,
17915 slideOnTouch : false,
17918 getAutoCreate : function()
17920 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17922 cfg.cls += ' tab-content';
17924 if (this.carousel) {
17925 cfg.cls += ' carousel slide';
17928 cls : 'carousel-inner',
17932 if(this.bullets && !Roo.isTouch){
17935 cls : 'carousel-bullets',
17939 if(this.bullets_cls){
17940 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17947 cfg.cn[0].cn.push(bullets);
17950 if(this.showarrow){
17951 cfg.cn[0].cn.push({
17953 class : 'carousel-arrow',
17957 class : 'carousel-prev',
17961 class : 'fa fa-chevron-left'
17967 class : 'carousel-next',
17971 class : 'fa fa-chevron-right'
17984 initEvents: function()
17986 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17987 // this.el.on("touchstart", this.onTouchStart, this);
17990 if(this.autoslide){
17993 this.slideFn = window.setInterval(function() {
17994 _this.showPanelNext();
17998 if(this.showarrow){
17999 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18000 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18006 // onTouchStart : function(e, el, o)
18008 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18012 // this.showPanelNext();
18016 getChildContainer : function()
18018 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18022 * register a Navigation item
18023 * @param {Roo.bootstrap.NavItem} the navitem to add
18025 register : function(item)
18027 this.tabs.push( item);
18028 item.navId = this.navId; // not really needed..
18033 getActivePanel : function()
18036 Roo.each(this.tabs, function(t) {
18046 getPanelByName : function(n)
18049 Roo.each(this.tabs, function(t) {
18050 if (t.tabId == n) {
18058 indexOfPanel : function(p)
18061 Roo.each(this.tabs, function(t,i) {
18062 if (t.tabId == p.tabId) {
18071 * show a specific panel
18072 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18073 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18075 showPanel : function (pan)
18077 if(this.transition || typeof(pan) == 'undefined'){
18078 Roo.log("waiting for the transitionend");
18082 if (typeof(pan) == 'number') {
18083 pan = this.tabs[pan];
18086 if (typeof(pan) == 'string') {
18087 pan = this.getPanelByName(pan);
18090 var cur = this.getActivePanel();
18093 Roo.log('pan or acitve pan is undefined');
18097 if (pan.tabId == this.getActivePanel().tabId) {
18101 if (false === cur.fireEvent('beforedeactivate')) {
18105 if(this.bullets > 0 && !Roo.isTouch){
18106 this.setActiveBullet(this.indexOfPanel(pan));
18109 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18111 this.transition = true;
18112 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18113 var lr = dir == 'next' ? 'left' : 'right';
18114 pan.el.addClass(dir); // or prev
18115 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18116 cur.el.addClass(lr); // or right
18117 pan.el.addClass(lr);
18120 cur.el.on('transitionend', function() {
18121 Roo.log("trans end?");
18123 pan.el.removeClass([lr,dir]);
18124 pan.setActive(true);
18126 cur.el.removeClass([lr]);
18127 cur.setActive(false);
18129 _this.transition = false;
18131 }, this, { single: true } );
18136 cur.setActive(false);
18137 pan.setActive(true);
18142 showPanelNext : function()
18144 var i = this.indexOfPanel(this.getActivePanel());
18146 if (i >= this.tabs.length - 1 && !this.autoslide) {
18150 if (i >= this.tabs.length - 1 && this.autoslide) {
18154 this.showPanel(this.tabs[i+1]);
18157 showPanelPrev : function()
18159 var i = this.indexOfPanel(this.getActivePanel());
18161 if (i < 1 && !this.autoslide) {
18165 if (i < 1 && this.autoslide) {
18166 i = this.tabs.length;
18169 this.showPanel(this.tabs[i-1]);
18173 addBullet: function()
18175 if(!this.bullets || Roo.isTouch){
18178 var ctr = this.el.select('.carousel-bullets',true).first();
18179 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18180 var bullet = ctr.createChild({
18181 cls : 'bullet bullet-' + i
18182 },ctr.dom.lastChild);
18187 bullet.on('click', (function(e, el, o, ii, t){
18189 e.preventDefault();
18191 this.showPanel(ii);
18193 if(this.autoslide && this.slideFn){
18194 clearInterval(this.slideFn);
18195 this.slideFn = window.setInterval(function() {
18196 _this.showPanelNext();
18200 }).createDelegate(this, [i, bullet], true));
18205 setActiveBullet : function(i)
18211 Roo.each(this.el.select('.bullet', true).elements, function(el){
18212 el.removeClass('selected');
18215 var bullet = this.el.select('.bullet-' + i, true).first();
18221 bullet.addClass('selected');
18232 Roo.apply(Roo.bootstrap.TabGroup, {
18236 * register a Navigation Group
18237 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18239 register : function(navgrp)
18241 this.groups[navgrp.navId] = navgrp;
18245 * fetch a Navigation Group based on the navigation ID
18246 * if one does not exist , it will get created.
18247 * @param {string} the navgroup to add
18248 * @returns {Roo.bootstrap.NavGroup} the navgroup
18250 get: function(navId) {
18251 if (typeof(this.groups[navId]) == 'undefined') {
18252 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18254 return this.groups[navId] ;
18269 * @class Roo.bootstrap.TabPanel
18270 * @extends Roo.bootstrap.Component
18271 * Bootstrap TabPanel class
18272 * @cfg {Boolean} active panel active
18273 * @cfg {String} html panel content
18274 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18275 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18276 * @cfg {String} href click to link..
18280 * Create a new TabPanel
18281 * @param {Object} config The config object
18284 Roo.bootstrap.TabPanel = function(config){
18285 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18289 * Fires when the active status changes
18290 * @param {Roo.bootstrap.TabPanel} this
18291 * @param {Boolean} state the new state
18296 * @event beforedeactivate
18297 * Fires before a tab is de-activated - can be used to do validation on a form.
18298 * @param {Roo.bootstrap.TabPanel} this
18299 * @return {Boolean} false if there is an error
18302 'beforedeactivate': true
18305 this.tabId = this.tabId || Roo.id();
18309 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18317 getAutoCreate : function(){
18320 // item is needed for carousel - not sure if it has any effect otherwise
18321 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18322 html: this.html || ''
18326 cfg.cls += ' active';
18330 cfg.tabId = this.tabId;
18337 initEvents: function()
18339 var p = this.parent();
18341 this.navId = this.navId || p.navId;
18343 if (typeof(this.navId) != 'undefined') {
18344 // not really needed.. but just in case.. parent should be a NavGroup.
18345 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18349 var i = tg.tabs.length - 1;
18351 if(this.active && tg.bullets > 0 && i < tg.bullets){
18352 tg.setActiveBullet(i);
18356 this.el.on('click', this.onClick, this);
18359 this.el.on("touchstart", this.onTouchStart, this);
18360 this.el.on("touchmove", this.onTouchMove, this);
18361 this.el.on("touchend", this.onTouchEnd, this);
18366 onRender : function(ct, position)
18368 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18371 setActive : function(state)
18373 Roo.log("panel - set active " + this.tabId + "=" + state);
18375 this.active = state;
18377 this.el.removeClass('active');
18379 } else if (!this.el.hasClass('active')) {
18380 this.el.addClass('active');
18383 this.fireEvent('changed', this, state);
18386 onClick : function(e)
18388 e.preventDefault();
18390 if(!this.href.length){
18394 window.location.href = this.href;
18403 onTouchStart : function(e)
18405 this.swiping = false;
18407 this.startX = e.browserEvent.touches[0].clientX;
18408 this.startY = e.browserEvent.touches[0].clientY;
18411 onTouchMove : function(e)
18413 this.swiping = true;
18415 this.endX = e.browserEvent.touches[0].clientX;
18416 this.endY = e.browserEvent.touches[0].clientY;
18419 onTouchEnd : function(e)
18426 var tabGroup = this.parent();
18428 if(this.endX > this.startX){ // swiping right
18429 tabGroup.showPanelPrev();
18433 if(this.startX > this.endX){ // swiping left
18434 tabGroup.showPanelNext();
18453 * @class Roo.bootstrap.DateField
18454 * @extends Roo.bootstrap.Input
18455 * Bootstrap DateField class
18456 * @cfg {Number} weekStart default 0
18457 * @cfg {String} viewMode default empty, (months|years)
18458 * @cfg {String} minViewMode default empty, (months|years)
18459 * @cfg {Number} startDate default -Infinity
18460 * @cfg {Number} endDate default Infinity
18461 * @cfg {Boolean} todayHighlight default false
18462 * @cfg {Boolean} todayBtn default false
18463 * @cfg {Boolean} calendarWeeks default false
18464 * @cfg {Object} daysOfWeekDisabled default empty
18465 * @cfg {Boolean} singleMode default false (true | false)
18467 * @cfg {Boolean} keyboardNavigation default true
18468 * @cfg {String} language default en
18471 * Create a new DateField
18472 * @param {Object} config The config object
18475 Roo.bootstrap.DateField = function(config){
18476 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18480 * Fires when this field show.
18481 * @param {Roo.bootstrap.DateField} this
18482 * @param {Mixed} date The date value
18487 * Fires when this field hide.
18488 * @param {Roo.bootstrap.DateField} this
18489 * @param {Mixed} date The date value
18494 * Fires when select a date.
18495 * @param {Roo.bootstrap.DateField} this
18496 * @param {Mixed} date The date value
18500 * @event beforeselect
18501 * Fires when before select a date.
18502 * @param {Roo.bootstrap.DateField} this
18503 * @param {Mixed} date The date value
18505 beforeselect : true
18509 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18512 * @cfg {String} format
18513 * The default date format string which can be overriden for localization support. The format must be
18514 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18518 * @cfg {String} altFormats
18519 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18520 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18522 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18530 todayHighlight : false,
18536 keyboardNavigation: true,
18538 calendarWeeks: false,
18540 startDate: -Infinity,
18544 daysOfWeekDisabled: [],
18548 singleMode : false,
18550 UTCDate: function()
18552 return new Date(Date.UTC.apply(Date, arguments));
18555 UTCToday: function()
18557 var today = new Date();
18558 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18561 getDate: function() {
18562 var d = this.getUTCDate();
18563 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18566 getUTCDate: function() {
18570 setDate: function(d) {
18571 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18574 setUTCDate: function(d) {
18576 this.setValue(this.formatDate(this.date));
18579 onRender: function(ct, position)
18582 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18584 this.language = this.language || 'en';
18585 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18586 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18588 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18589 this.format = this.format || 'm/d/y';
18590 this.isInline = false;
18591 this.isInput = true;
18592 this.component = this.el.select('.add-on', true).first() || false;
18593 this.component = (this.component && this.component.length === 0) ? false : this.component;
18594 this.hasInput = this.component && this.inputEl().length;
18596 if (typeof(this.minViewMode === 'string')) {
18597 switch (this.minViewMode) {
18599 this.minViewMode = 1;
18602 this.minViewMode = 2;
18605 this.minViewMode = 0;
18610 if (typeof(this.viewMode === 'string')) {
18611 switch (this.viewMode) {
18624 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18626 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18628 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18630 this.picker().on('mousedown', this.onMousedown, this);
18631 this.picker().on('click', this.onClick, this);
18633 this.picker().addClass('datepicker-dropdown');
18635 this.startViewMode = this.viewMode;
18637 if(this.singleMode){
18638 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18639 v.setVisibilityMode(Roo.Element.DISPLAY);
18643 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18644 v.setStyle('width', '189px');
18648 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18649 if(!this.calendarWeeks){
18654 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18655 v.attr('colspan', function(i, val){
18656 return parseInt(val) + 1;
18661 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18663 this.setStartDate(this.startDate);
18664 this.setEndDate(this.endDate);
18666 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18673 if(this.isInline) {
18678 picker : function()
18680 return this.pickerEl;
18681 // return this.el.select('.datepicker', true).first();
18684 fillDow: function()
18686 var dowCnt = this.weekStart;
18695 if(this.calendarWeeks){
18703 while (dowCnt < this.weekStart + 7) {
18707 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18711 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18714 fillMonths: function()
18717 var months = this.picker().select('>.datepicker-months td', true).first();
18719 months.dom.innerHTML = '';
18725 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18728 months.createChild(month);
18735 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;
18737 if (this.date < this.startDate) {
18738 this.viewDate = new Date(this.startDate);
18739 } else if (this.date > this.endDate) {
18740 this.viewDate = new Date(this.endDate);
18742 this.viewDate = new Date(this.date);
18750 var d = new Date(this.viewDate),
18751 year = d.getUTCFullYear(),
18752 month = d.getUTCMonth(),
18753 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18754 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18755 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18756 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18757 currentDate = this.date && this.date.valueOf(),
18758 today = this.UTCToday();
18760 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18762 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18764 // this.picker.select('>tfoot th.today').
18765 // .text(dates[this.language].today)
18766 // .toggle(this.todayBtn !== false);
18768 this.updateNavArrows();
18771 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18773 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18775 prevMonth.setUTCDate(day);
18777 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18779 var nextMonth = new Date(prevMonth);
18781 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18783 nextMonth = nextMonth.valueOf();
18785 var fillMonths = false;
18787 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18789 while(prevMonth.valueOf() <= nextMonth) {
18792 if (prevMonth.getUTCDay() === this.weekStart) {
18794 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18802 if(this.calendarWeeks){
18803 // ISO 8601: First week contains first thursday.
18804 // ISO also states week starts on Monday, but we can be more abstract here.
18806 // Start of current week: based on weekstart/current date
18807 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18808 // Thursday of this week
18809 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18810 // First Thursday of year, year from thursday
18811 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18812 // Calendar week: ms between thursdays, div ms per day, div 7 days
18813 calWeek = (th - yth) / 864e5 / 7 + 1;
18815 fillMonths.cn.push({
18823 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18825 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18828 if (this.todayHighlight &&
18829 prevMonth.getUTCFullYear() == today.getFullYear() &&
18830 prevMonth.getUTCMonth() == today.getMonth() &&
18831 prevMonth.getUTCDate() == today.getDate()) {
18832 clsName += ' today';
18835 if (currentDate && prevMonth.valueOf() === currentDate) {
18836 clsName += ' active';
18839 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18840 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18841 clsName += ' disabled';
18844 fillMonths.cn.push({
18846 cls: 'day ' + clsName,
18847 html: prevMonth.getDate()
18850 prevMonth.setDate(prevMonth.getDate()+1);
18853 var currentYear = this.date && this.date.getUTCFullYear();
18854 var currentMonth = this.date && this.date.getUTCMonth();
18856 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18858 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18859 v.removeClass('active');
18861 if(currentYear === year && k === currentMonth){
18862 v.addClass('active');
18865 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18866 v.addClass('disabled');
18872 year = parseInt(year/10, 10) * 10;
18874 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18876 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18879 for (var i = -1; i < 11; i++) {
18880 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18882 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18890 showMode: function(dir)
18893 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18896 Roo.each(this.picker().select('>div',true).elements, function(v){
18897 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18900 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18905 if(this.isInline) {
18909 this.picker().removeClass(['bottom', 'top']);
18911 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18913 * place to the top of element!
18917 this.picker().addClass('top');
18918 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18923 this.picker().addClass('bottom');
18925 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18928 parseDate : function(value)
18930 if(!value || value instanceof Date){
18933 var v = Date.parseDate(value, this.format);
18934 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18935 v = Date.parseDate(value, 'Y-m-d');
18937 if(!v && this.altFormats){
18938 if(!this.altFormatsArray){
18939 this.altFormatsArray = this.altFormats.split("|");
18941 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18942 v = Date.parseDate(value, this.altFormatsArray[i]);
18948 formatDate : function(date, fmt)
18950 return (!date || !(date instanceof Date)) ?
18951 date : date.dateFormat(fmt || this.format);
18954 onFocus : function()
18956 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18960 onBlur : function()
18962 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18964 var d = this.inputEl().getValue();
18971 showPopup : function()
18973 this.picker().show();
18977 this.fireEvent('showpopup', this, this.date);
18980 hidePopup : function()
18982 if(this.isInline) {
18985 this.picker().hide();
18986 this.viewMode = this.startViewMode;
18989 this.fireEvent('hidepopup', this, this.date);
18993 onMousedown: function(e)
18995 e.stopPropagation();
18996 e.preventDefault();
19001 Roo.bootstrap.DateField.superclass.keyup.call(this);
19005 setValue: function(v)
19007 if(this.fireEvent('beforeselect', this, v) !== false){
19008 var d = new Date(this.parseDate(v) ).clearTime();
19010 if(isNaN(d.getTime())){
19011 this.date = this.viewDate = '';
19012 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19016 v = this.formatDate(d);
19018 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19020 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19024 this.fireEvent('select', this, this.date);
19028 getValue: function()
19030 return this.formatDate(this.date);
19033 fireKey: function(e)
19035 if (!this.picker().isVisible()){
19036 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19042 var dateChanged = false,
19044 newDate, newViewDate;
19049 e.preventDefault();
19053 if (!this.keyboardNavigation) {
19056 dir = e.keyCode == 37 ? -1 : 1;
19059 newDate = this.moveYear(this.date, dir);
19060 newViewDate = this.moveYear(this.viewDate, dir);
19061 } else if (e.shiftKey){
19062 newDate = this.moveMonth(this.date, dir);
19063 newViewDate = this.moveMonth(this.viewDate, dir);
19065 newDate = new Date(this.date);
19066 newDate.setUTCDate(this.date.getUTCDate() + dir);
19067 newViewDate = new Date(this.viewDate);
19068 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19070 if (this.dateWithinRange(newDate)){
19071 this.date = newDate;
19072 this.viewDate = newViewDate;
19073 this.setValue(this.formatDate(this.date));
19075 e.preventDefault();
19076 dateChanged = true;
19081 if (!this.keyboardNavigation) {
19084 dir = e.keyCode == 38 ? -1 : 1;
19086 newDate = this.moveYear(this.date, dir);
19087 newViewDate = this.moveYear(this.viewDate, dir);
19088 } else if (e.shiftKey){
19089 newDate = this.moveMonth(this.date, dir);
19090 newViewDate = this.moveMonth(this.viewDate, dir);
19092 newDate = new Date(this.date);
19093 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19094 newViewDate = new Date(this.viewDate);
19095 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19097 if (this.dateWithinRange(newDate)){
19098 this.date = newDate;
19099 this.viewDate = newViewDate;
19100 this.setValue(this.formatDate(this.date));
19102 e.preventDefault();
19103 dateChanged = true;
19107 this.setValue(this.formatDate(this.date));
19109 e.preventDefault();
19112 this.setValue(this.formatDate(this.date));
19126 onClick: function(e)
19128 e.stopPropagation();
19129 e.preventDefault();
19131 var target = e.getTarget();
19133 if(target.nodeName.toLowerCase() === 'i'){
19134 target = Roo.get(target).dom.parentNode;
19137 var nodeName = target.nodeName;
19138 var className = target.className;
19139 var html = target.innerHTML;
19140 //Roo.log(nodeName);
19142 switch(nodeName.toLowerCase()) {
19144 switch(className) {
19150 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19151 switch(this.viewMode){
19153 this.viewDate = this.moveMonth(this.viewDate, dir);
19157 this.viewDate = this.moveYear(this.viewDate, dir);
19163 var date = new Date();
19164 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19166 this.setValue(this.formatDate(this.date));
19173 if (className.indexOf('disabled') < 0) {
19174 this.viewDate.setUTCDate(1);
19175 if (className.indexOf('month') > -1) {
19176 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19178 var year = parseInt(html, 10) || 0;
19179 this.viewDate.setUTCFullYear(year);
19183 if(this.singleMode){
19184 this.setValue(this.formatDate(this.viewDate));
19195 //Roo.log(className);
19196 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19197 var day = parseInt(html, 10) || 1;
19198 var year = this.viewDate.getUTCFullYear(),
19199 month = this.viewDate.getUTCMonth();
19201 if (className.indexOf('old') > -1) {
19208 } else if (className.indexOf('new') > -1) {
19216 //Roo.log([year,month,day]);
19217 this.date = this.UTCDate(year, month, day,0,0,0,0);
19218 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19220 //Roo.log(this.formatDate(this.date));
19221 this.setValue(this.formatDate(this.date));
19228 setStartDate: function(startDate)
19230 this.startDate = startDate || -Infinity;
19231 if (this.startDate !== -Infinity) {
19232 this.startDate = this.parseDate(this.startDate);
19235 this.updateNavArrows();
19238 setEndDate: function(endDate)
19240 this.endDate = endDate || Infinity;
19241 if (this.endDate !== Infinity) {
19242 this.endDate = this.parseDate(this.endDate);
19245 this.updateNavArrows();
19248 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19250 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19251 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19252 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19254 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19255 return parseInt(d, 10);
19258 this.updateNavArrows();
19261 updateNavArrows: function()
19263 if(this.singleMode){
19267 var d = new Date(this.viewDate),
19268 year = d.getUTCFullYear(),
19269 month = d.getUTCMonth();
19271 Roo.each(this.picker().select('.prev', true).elements, function(v){
19273 switch (this.viewMode) {
19276 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19282 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19289 Roo.each(this.picker().select('.next', true).elements, function(v){
19291 switch (this.viewMode) {
19294 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19300 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19308 moveMonth: function(date, dir)
19313 var new_date = new Date(date.valueOf()),
19314 day = new_date.getUTCDate(),
19315 month = new_date.getUTCMonth(),
19316 mag = Math.abs(dir),
19318 dir = dir > 0 ? 1 : -1;
19321 // If going back one month, make sure month is not current month
19322 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19324 return new_date.getUTCMonth() == month;
19326 // If going forward one month, make sure month is as expected
19327 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19329 return new_date.getUTCMonth() != new_month;
19331 new_month = month + dir;
19332 new_date.setUTCMonth(new_month);
19333 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19334 if (new_month < 0 || new_month > 11) {
19335 new_month = (new_month + 12) % 12;
19338 // For magnitudes >1, move one month at a time...
19339 for (var i=0; i<mag; i++) {
19340 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19341 new_date = this.moveMonth(new_date, dir);
19343 // ...then reset the day, keeping it in the new month
19344 new_month = new_date.getUTCMonth();
19345 new_date.setUTCDate(day);
19347 return new_month != new_date.getUTCMonth();
19350 // Common date-resetting loop -- if date is beyond end of month, make it
19353 new_date.setUTCDate(--day);
19354 new_date.setUTCMonth(new_month);
19359 moveYear: function(date, dir)
19361 return this.moveMonth(date, dir*12);
19364 dateWithinRange: function(date)
19366 return date >= this.startDate && date <= this.endDate;
19372 this.picker().remove();
19375 validateValue : function(value)
19377 if(this.getVisibilityEl().hasClass('hidden')){
19381 if(value.length < 1) {
19382 if(this.allowBlank){
19388 if(value.length < this.minLength){
19391 if(value.length > this.maxLength){
19395 var vt = Roo.form.VTypes;
19396 if(!vt[this.vtype](value, this)){
19400 if(typeof this.validator == "function"){
19401 var msg = this.validator(value);
19407 if(this.regex && !this.regex.test(value)){
19411 if(typeof(this.parseDate(value)) == 'undefined'){
19415 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19419 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19429 this.date = this.viewDate = '';
19431 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19436 Roo.apply(Roo.bootstrap.DateField, {
19447 html: '<i class="fa fa-arrow-left"/>'
19457 html: '<i class="fa fa-arrow-right"/>'
19499 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19500 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19501 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19502 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19503 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19516 navFnc: 'FullYear',
19521 navFnc: 'FullYear',
19526 Roo.apply(Roo.bootstrap.DateField, {
19530 cls: 'datepicker dropdown-menu roo-dynamic',
19534 cls: 'datepicker-days',
19538 cls: 'table-condensed',
19540 Roo.bootstrap.DateField.head,
19544 Roo.bootstrap.DateField.footer
19551 cls: 'datepicker-months',
19555 cls: 'table-condensed',
19557 Roo.bootstrap.DateField.head,
19558 Roo.bootstrap.DateField.content,
19559 Roo.bootstrap.DateField.footer
19566 cls: 'datepicker-years',
19570 cls: 'table-condensed',
19572 Roo.bootstrap.DateField.head,
19573 Roo.bootstrap.DateField.content,
19574 Roo.bootstrap.DateField.footer
19593 * @class Roo.bootstrap.TimeField
19594 * @extends Roo.bootstrap.Input
19595 * Bootstrap DateField class
19599 * Create a new TimeField
19600 * @param {Object} config The config object
19603 Roo.bootstrap.TimeField = function(config){
19604 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19608 * Fires when this field show.
19609 * @param {Roo.bootstrap.DateField} thisthis
19610 * @param {Mixed} date The date value
19615 * Fires when this field hide.
19616 * @param {Roo.bootstrap.DateField} this
19617 * @param {Mixed} date The date value
19622 * Fires when select a date.
19623 * @param {Roo.bootstrap.DateField} this
19624 * @param {Mixed} date The date value
19630 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19633 * @cfg {String} format
19634 * The default time format string which can be overriden for localization support. The format must be
19635 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19639 onRender: function(ct, position)
19642 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19644 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19646 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19648 this.pop = this.picker().select('>.datepicker-time',true).first();
19649 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19651 this.picker().on('mousedown', this.onMousedown, this);
19652 this.picker().on('click', this.onClick, this);
19654 this.picker().addClass('datepicker-dropdown');
19659 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19660 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19661 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19662 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19663 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19664 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19668 fireKey: function(e){
19669 if (!this.picker().isVisible()){
19670 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19676 e.preventDefault();
19684 this.onTogglePeriod();
19687 this.onIncrementMinutes();
19690 this.onDecrementMinutes();
19699 onClick: function(e) {
19700 e.stopPropagation();
19701 e.preventDefault();
19704 picker : function()
19706 return this.el.select('.datepicker', true).first();
19709 fillTime: function()
19711 var time = this.pop.select('tbody', true).first();
19713 time.dom.innerHTML = '';
19728 cls: 'hours-up glyphicon glyphicon-chevron-up'
19748 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19769 cls: 'timepicker-hour',
19784 cls: 'timepicker-minute',
19799 cls: 'btn btn-primary period',
19821 cls: 'hours-down glyphicon glyphicon-chevron-down'
19841 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19859 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19866 var hours = this.time.getHours();
19867 var minutes = this.time.getMinutes();
19880 hours = hours - 12;
19884 hours = '0' + hours;
19888 minutes = '0' + minutes;
19891 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19892 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19893 this.pop.select('button', true).first().dom.innerHTML = period;
19899 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19901 var cls = ['bottom'];
19903 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19910 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19915 this.picker().addClass(cls.join('-'));
19919 Roo.each(cls, function(c){
19921 _this.picker().setTop(_this.inputEl().getHeight());
19925 _this.picker().setTop(0 - _this.picker().getHeight());
19930 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19934 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19941 onFocus : function()
19943 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19947 onBlur : function()
19949 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19955 this.picker().show();
19960 this.fireEvent('show', this, this.date);
19965 this.picker().hide();
19968 this.fireEvent('hide', this, this.date);
19971 setTime : function()
19974 this.setValue(this.time.format(this.format));
19976 this.fireEvent('select', this, this.date);
19981 onMousedown: function(e){
19982 e.stopPropagation();
19983 e.preventDefault();
19986 onIncrementHours: function()
19988 Roo.log('onIncrementHours');
19989 this.time = this.time.add(Date.HOUR, 1);
19994 onDecrementHours: function()
19996 Roo.log('onDecrementHours');
19997 this.time = this.time.add(Date.HOUR, -1);
20001 onIncrementMinutes: function()
20003 Roo.log('onIncrementMinutes');
20004 this.time = this.time.add(Date.MINUTE, 1);
20008 onDecrementMinutes: function()
20010 Roo.log('onDecrementMinutes');
20011 this.time = this.time.add(Date.MINUTE, -1);
20015 onTogglePeriod: function()
20017 Roo.log('onTogglePeriod');
20018 this.time = this.time.add(Date.HOUR, 12);
20025 Roo.apply(Roo.bootstrap.TimeField, {
20055 cls: 'btn btn-info ok',
20067 Roo.apply(Roo.bootstrap.TimeField, {
20071 cls: 'datepicker dropdown-menu',
20075 cls: 'datepicker-time',
20079 cls: 'table-condensed',
20081 Roo.bootstrap.TimeField.content,
20082 Roo.bootstrap.TimeField.footer
20101 * @class Roo.bootstrap.MonthField
20102 * @extends Roo.bootstrap.Input
20103 * Bootstrap MonthField class
20105 * @cfg {String} language default en
20108 * Create a new MonthField
20109 * @param {Object} config The config object
20112 Roo.bootstrap.MonthField = function(config){
20113 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20118 * Fires when this field show.
20119 * @param {Roo.bootstrap.MonthField} this
20120 * @param {Mixed} date The date value
20125 * Fires when this field hide.
20126 * @param {Roo.bootstrap.MonthField} this
20127 * @param {Mixed} date The date value
20132 * Fires when select a date.
20133 * @param {Roo.bootstrap.MonthField} this
20134 * @param {String} oldvalue The old value
20135 * @param {String} newvalue The new value
20141 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20143 onRender: function(ct, position)
20146 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20148 this.language = this.language || 'en';
20149 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20150 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20152 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20153 this.isInline = false;
20154 this.isInput = true;
20155 this.component = this.el.select('.add-on', true).first() || false;
20156 this.component = (this.component && this.component.length === 0) ? false : this.component;
20157 this.hasInput = this.component && this.inputEL().length;
20159 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20161 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20163 this.picker().on('mousedown', this.onMousedown, this);
20164 this.picker().on('click', this.onClick, this);
20166 this.picker().addClass('datepicker-dropdown');
20168 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20169 v.setStyle('width', '189px');
20176 if(this.isInline) {
20182 setValue: function(v, suppressEvent)
20184 var o = this.getValue();
20186 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20190 if(suppressEvent !== true){
20191 this.fireEvent('select', this, o, v);
20196 getValue: function()
20201 onClick: function(e)
20203 e.stopPropagation();
20204 e.preventDefault();
20206 var target = e.getTarget();
20208 if(target.nodeName.toLowerCase() === 'i'){
20209 target = Roo.get(target).dom.parentNode;
20212 var nodeName = target.nodeName;
20213 var className = target.className;
20214 var html = target.innerHTML;
20216 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20220 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20222 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20228 picker : function()
20230 return this.pickerEl;
20233 fillMonths: function()
20236 var months = this.picker().select('>.datepicker-months td', true).first();
20238 months.dom.innerHTML = '';
20244 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20247 months.createChild(month);
20256 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20257 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20260 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20261 e.removeClass('active');
20263 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20264 e.addClass('active');
20271 if(this.isInline) {
20275 this.picker().removeClass(['bottom', 'top']);
20277 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20279 * place to the top of element!
20283 this.picker().addClass('top');
20284 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20289 this.picker().addClass('bottom');
20291 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20294 onFocus : function()
20296 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20300 onBlur : function()
20302 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20304 var d = this.inputEl().getValue();
20313 this.picker().show();
20314 this.picker().select('>.datepicker-months', true).first().show();
20318 this.fireEvent('show', this, this.date);
20323 if(this.isInline) {
20326 this.picker().hide();
20327 this.fireEvent('hide', this, this.date);
20331 onMousedown: function(e)
20333 e.stopPropagation();
20334 e.preventDefault();
20339 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20343 fireKey: function(e)
20345 if (!this.picker().isVisible()){
20346 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20357 e.preventDefault();
20361 dir = e.keyCode == 37 ? -1 : 1;
20363 this.vIndex = this.vIndex + dir;
20365 if(this.vIndex < 0){
20369 if(this.vIndex > 11){
20373 if(isNaN(this.vIndex)){
20377 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20383 dir = e.keyCode == 38 ? -1 : 1;
20385 this.vIndex = this.vIndex + dir * 4;
20387 if(this.vIndex < 0){
20391 if(this.vIndex > 11){
20395 if(isNaN(this.vIndex)){
20399 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20404 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20405 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20409 e.preventDefault();
20412 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20413 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20429 this.picker().remove();
20434 Roo.apply(Roo.bootstrap.MonthField, {
20453 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20454 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20459 Roo.apply(Roo.bootstrap.MonthField, {
20463 cls: 'datepicker dropdown-menu roo-dynamic',
20467 cls: 'datepicker-months',
20471 cls: 'table-condensed',
20473 Roo.bootstrap.DateField.content
20493 * @class Roo.bootstrap.CheckBox
20494 * @extends Roo.bootstrap.Input
20495 * Bootstrap CheckBox class
20497 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20498 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20499 * @cfg {String} boxLabel The text that appears beside the checkbox
20500 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20501 * @cfg {Boolean} checked initnal the element
20502 * @cfg {Boolean} inline inline the element (default false)
20503 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20504 * @cfg {String} tooltip label tooltip
20507 * Create a new CheckBox
20508 * @param {Object} config The config object
20511 Roo.bootstrap.CheckBox = function(config){
20512 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20517 * Fires when the element is checked or unchecked.
20518 * @param {Roo.bootstrap.CheckBox} this This input
20519 * @param {Boolean} checked The new checked value
20524 * Fires when the element is click.
20525 * @param {Roo.bootstrap.CheckBox} this This input
20532 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20534 inputType: 'checkbox',
20543 getAutoCreate : function()
20545 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20551 cfg.cls = 'form-group ' + this.inputType; //input-group
20554 cfg.cls += ' ' + this.inputType + '-inline';
20560 type : this.inputType,
20561 value : this.inputValue,
20562 cls : 'roo-' + this.inputType, //'form-box',
20563 placeholder : this.placeholder || ''
20567 if(this.inputType != 'radio'){
20571 cls : 'roo-hidden-value',
20572 value : this.checked ? this.inputValue : this.valueOff
20577 if (this.weight) { // Validity check?
20578 cfg.cls += " " + this.inputType + "-" + this.weight;
20581 if (this.disabled) {
20582 input.disabled=true;
20586 input.checked = this.checked;
20591 input.name = this.name;
20593 if(this.inputType != 'radio'){
20594 hidden.name = this.name;
20595 input.name = '_hidden_' + this.name;
20600 input.cls += ' input-' + this.size;
20605 ['xs','sm','md','lg'].map(function(size){
20606 if (settings[size]) {
20607 cfg.cls += ' col-' + size + '-' + settings[size];
20611 var inputblock = input;
20613 if (this.before || this.after) {
20616 cls : 'input-group',
20621 inputblock.cn.push({
20623 cls : 'input-group-addon',
20628 inputblock.cn.push(input);
20630 if(this.inputType != 'radio'){
20631 inputblock.cn.push(hidden);
20635 inputblock.cn.push({
20637 cls : 'input-group-addon',
20644 if (align ==='left' && this.fieldLabel.length) {
20645 // Roo.log("left and has label");
20650 cls : 'control-label',
20651 html : this.fieldLabel
20661 if(this.labelWidth > 12){
20662 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20665 if(this.labelWidth < 13 && this.labelmd == 0){
20666 this.labelmd = this.labelWidth;
20669 if(this.labellg > 0){
20670 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20671 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20674 if(this.labelmd > 0){
20675 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20676 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20679 if(this.labelsm > 0){
20680 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20681 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20684 if(this.labelxs > 0){
20685 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20686 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20689 } else if ( this.fieldLabel.length) {
20690 // Roo.log(" label");
20694 tag: this.boxLabel ? 'span' : 'label',
20696 cls: 'control-label box-input-label',
20697 //cls : 'input-group-addon',
20698 html : this.fieldLabel
20707 // Roo.log(" no label && no align");
20708 cfg.cn = [ inputblock ] ;
20714 var boxLabelCfg = {
20716 //'for': id, // box label is handled by onclick - so no for...
20718 html: this.boxLabel
20722 boxLabelCfg.tooltip = this.tooltip;
20725 cfg.cn.push(boxLabelCfg);
20728 if(this.inputType != 'radio'){
20729 cfg.cn.push(hidden);
20737 * return the real input element.
20739 inputEl: function ()
20741 return this.el.select('input.roo-' + this.inputType,true).first();
20743 hiddenEl: function ()
20745 return this.el.select('input.roo-hidden-value',true).first();
20748 labelEl: function()
20750 return this.el.select('label.control-label',true).first();
20752 /* depricated... */
20756 return this.labelEl();
20759 boxLabelEl: function()
20761 return this.el.select('label.box-label',true).first();
20764 initEvents : function()
20766 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20768 this.inputEl().on('click', this.onClick, this);
20770 if (this.boxLabel) {
20771 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20774 this.startValue = this.getValue();
20777 Roo.bootstrap.CheckBox.register(this);
20781 onClick : function(e)
20783 if(this.fireEvent('click', this, e) !== false){
20784 this.setChecked(!this.checked);
20789 setChecked : function(state,suppressEvent)
20791 this.startValue = this.getValue();
20793 if(this.inputType == 'radio'){
20795 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20796 e.dom.checked = false;
20799 this.inputEl().dom.checked = true;
20801 this.inputEl().dom.value = this.inputValue;
20803 if(suppressEvent !== true){
20804 this.fireEvent('check', this, true);
20812 this.checked = state;
20814 this.inputEl().dom.checked = state;
20817 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20819 if(suppressEvent !== true){
20820 this.fireEvent('check', this, state);
20826 getValue : function()
20828 if(this.inputType == 'radio'){
20829 return this.getGroupValue();
20832 return this.hiddenEl().dom.value;
20836 getGroupValue : function()
20838 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20842 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20845 setValue : function(v,suppressEvent)
20847 if(this.inputType == 'radio'){
20848 this.setGroupValue(v, suppressEvent);
20852 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20857 setGroupValue : function(v, suppressEvent)
20859 this.startValue = this.getValue();
20861 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20862 e.dom.checked = false;
20864 if(e.dom.value == v){
20865 e.dom.checked = true;
20869 if(suppressEvent !== true){
20870 this.fireEvent('check', this, true);
20878 validate : function()
20880 if(this.getVisibilityEl().hasClass('hidden')){
20886 (this.inputType == 'radio' && this.validateRadio()) ||
20887 (this.inputType == 'checkbox' && this.validateCheckbox())
20893 this.markInvalid();
20897 validateRadio : function()
20899 if(this.getVisibilityEl().hasClass('hidden')){
20903 if(this.allowBlank){
20909 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20910 if(!e.dom.checked){
20922 validateCheckbox : function()
20925 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20926 //return (this.getValue() == this.inputValue) ? true : false;
20929 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20937 for(var i in group){
20938 if(group[i].el.isVisible(true)){
20946 for(var i in group){
20951 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20958 * Mark this field as valid
20960 markValid : function()
20964 this.fireEvent('valid', this);
20966 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20969 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20976 if(this.inputType == 'radio'){
20977 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20978 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20979 e.findParent('.form-group', false, true).addClass(_this.validClass);
20986 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20987 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20991 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20997 for(var i in group){
20998 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20999 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21004 * Mark this field as invalid
21005 * @param {String} msg The validation message
21007 markInvalid : function(msg)
21009 if(this.allowBlank){
21015 this.fireEvent('invalid', this, msg);
21017 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21020 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21024 label.markInvalid();
21027 if(this.inputType == 'radio'){
21028 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21029 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21030 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21037 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21038 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21042 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21048 for(var i in group){
21049 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21050 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21055 clearInvalid : function()
21057 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21059 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21061 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21063 if (label && label.iconEl) {
21064 label.iconEl.removeClass(label.validClass);
21065 label.iconEl.removeClass(label.invalidClass);
21069 disable : function()
21071 if(this.inputType != 'radio'){
21072 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21079 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21080 _this.getActionEl().addClass(this.disabledClass);
21081 e.dom.disabled = true;
21085 this.disabled = true;
21086 this.fireEvent("disable", this);
21090 enable : function()
21092 if(this.inputType != 'radio'){
21093 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21100 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21101 _this.getActionEl().removeClass(this.disabledClass);
21102 e.dom.disabled = false;
21106 this.disabled = false;
21107 this.fireEvent("enable", this);
21111 setBoxLabel : function(v)
21116 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21122 Roo.apply(Roo.bootstrap.CheckBox, {
21127 * register a CheckBox Group
21128 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21130 register : function(checkbox)
21132 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21133 this.groups[checkbox.groupId] = {};
21136 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21140 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21144 * fetch a CheckBox Group based on the group ID
21145 * @param {string} the group ID
21146 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21148 get: function(groupId) {
21149 if (typeof(this.groups[groupId]) == 'undefined') {
21153 return this.groups[groupId] ;
21166 * @class Roo.bootstrap.Radio
21167 * @extends Roo.bootstrap.Component
21168 * Bootstrap Radio class
21169 * @cfg {String} boxLabel - the label associated
21170 * @cfg {String} value - the value of radio
21173 * Create a new Radio
21174 * @param {Object} config The config object
21176 Roo.bootstrap.Radio = function(config){
21177 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21181 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21187 getAutoCreate : function()
21191 cls : 'form-group radio',
21196 html : this.boxLabel
21204 initEvents : function()
21206 this.parent().register(this);
21208 this.el.on('click', this.onClick, this);
21212 onClick : function(e)
21214 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21215 this.setChecked(true);
21219 setChecked : function(state, suppressEvent)
21221 this.parent().setValue(this.value, suppressEvent);
21225 setBoxLabel : function(v)
21230 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21245 * @class Roo.bootstrap.SecurePass
21246 * @extends Roo.bootstrap.Input
21247 * Bootstrap SecurePass class
21251 * Create a new SecurePass
21252 * @param {Object} config The config object
21255 Roo.bootstrap.SecurePass = function (config) {
21256 // these go here, so the translation tool can replace them..
21258 PwdEmpty: "Please type a password, and then retype it to confirm.",
21259 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21260 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21261 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21262 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21263 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21264 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21265 TooWeak: "Your password is Too Weak."
21267 this.meterLabel = "Password strength:";
21268 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21269 this.meterClass = [
21270 "roo-password-meter-tooweak",
21271 "roo-password-meter-weak",
21272 "roo-password-meter-medium",
21273 "roo-password-meter-strong",
21274 "roo-password-meter-grey"
21279 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21282 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21284 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21286 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21287 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21288 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21289 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21290 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21291 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21292 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21302 * @cfg {String/Object} Label for the strength meter (defaults to
21303 * 'Password strength:')
21308 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21309 * ['Weak', 'Medium', 'Strong'])
21312 pwdStrengths: false,
21325 initEvents: function ()
21327 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21329 if (this.el.is('input[type=password]') && Roo.isSafari) {
21330 this.el.on('keydown', this.SafariOnKeyDown, this);
21333 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21336 onRender: function (ct, position)
21338 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21339 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21340 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21342 this.trigger.createChild({
21347 cls: 'roo-password-meter-grey col-xs-12',
21350 //width: this.meterWidth + 'px'
21354 cls: 'roo-password-meter-text'
21360 if (this.hideTrigger) {
21361 this.trigger.setDisplayed(false);
21363 this.setSize(this.width || '', this.height || '');
21366 onDestroy: function ()
21368 if (this.trigger) {
21369 this.trigger.removeAllListeners();
21370 this.trigger.remove();
21373 this.wrap.remove();
21375 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21378 checkStrength: function ()
21380 var pwd = this.inputEl().getValue();
21381 if (pwd == this._lastPwd) {
21386 if (this.ClientSideStrongPassword(pwd)) {
21388 } else if (this.ClientSideMediumPassword(pwd)) {
21390 } else if (this.ClientSideWeakPassword(pwd)) {
21396 Roo.log('strength1: ' + strength);
21398 //var pm = this.trigger.child('div/div/div').dom;
21399 var pm = this.trigger.child('div/div');
21400 pm.removeClass(this.meterClass);
21401 pm.addClass(this.meterClass[strength]);
21404 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21406 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21408 this._lastPwd = pwd;
21412 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21414 this._lastPwd = '';
21416 var pm = this.trigger.child('div/div');
21417 pm.removeClass(this.meterClass);
21418 pm.addClass('roo-password-meter-grey');
21421 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21424 this.inputEl().dom.type='password';
21427 validateValue: function (value)
21430 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21433 if (value.length == 0) {
21434 if (this.allowBlank) {
21435 this.clearInvalid();
21439 this.markInvalid(this.errors.PwdEmpty);
21440 this.errorMsg = this.errors.PwdEmpty;
21448 if ('[\x21-\x7e]*'.match(value)) {
21449 this.markInvalid(this.errors.PwdBadChar);
21450 this.errorMsg = this.errors.PwdBadChar;
21453 if (value.length < 6) {
21454 this.markInvalid(this.errors.PwdShort);
21455 this.errorMsg = this.errors.PwdShort;
21458 if (value.length > 16) {
21459 this.markInvalid(this.errors.PwdLong);
21460 this.errorMsg = this.errors.PwdLong;
21464 if (this.ClientSideStrongPassword(value)) {
21466 } else if (this.ClientSideMediumPassword(value)) {
21468 } else if (this.ClientSideWeakPassword(value)) {
21475 if (strength < 2) {
21476 //this.markInvalid(this.errors.TooWeak);
21477 this.errorMsg = this.errors.TooWeak;
21482 console.log('strength2: ' + strength);
21484 //var pm = this.trigger.child('div/div/div').dom;
21486 var pm = this.trigger.child('div/div');
21487 pm.removeClass(this.meterClass);
21488 pm.addClass(this.meterClass[strength]);
21490 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21492 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21494 this.errorMsg = '';
21498 CharacterSetChecks: function (type)
21501 this.fResult = false;
21504 isctype: function (character, type)
21507 case this.kCapitalLetter:
21508 if (character >= 'A' && character <= 'Z') {
21513 case this.kSmallLetter:
21514 if (character >= 'a' && character <= 'z') {
21520 if (character >= '0' && character <= '9') {
21525 case this.kPunctuation:
21526 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21537 IsLongEnough: function (pwd, size)
21539 return !(pwd == null || isNaN(size) || pwd.length < size);
21542 SpansEnoughCharacterSets: function (word, nb)
21544 if (!this.IsLongEnough(word, nb))
21549 var characterSetChecks = new Array(
21550 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21551 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21554 for (var index = 0; index < word.length; ++index) {
21555 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21556 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21557 characterSetChecks[nCharSet].fResult = true;
21564 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21565 if (characterSetChecks[nCharSet].fResult) {
21570 if (nCharSets < nb) {
21576 ClientSideStrongPassword: function (pwd)
21578 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21581 ClientSideMediumPassword: function (pwd)
21583 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21586 ClientSideWeakPassword: function (pwd)
21588 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21591 })//<script type="text/javascript">
21594 * Based Ext JS Library 1.1.1
21595 * Copyright(c) 2006-2007, Ext JS, LLC.
21601 * @class Roo.HtmlEditorCore
21602 * @extends Roo.Component
21603 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21605 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21608 Roo.HtmlEditorCore = function(config){
21611 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21616 * @event initialize
21617 * Fires when the editor is fully initialized (including the iframe)
21618 * @param {Roo.HtmlEditorCore} this
21623 * Fires when the editor is first receives the focus. Any insertion must wait
21624 * until after this event.
21625 * @param {Roo.HtmlEditorCore} this
21629 * @event beforesync
21630 * Fires before the textarea is updated with content from the editor iframe. Return false
21631 * to cancel the sync.
21632 * @param {Roo.HtmlEditorCore} this
21633 * @param {String} html
21637 * @event beforepush
21638 * Fires before the iframe editor is updated with content from the textarea. Return false
21639 * to cancel the push.
21640 * @param {Roo.HtmlEditorCore} this
21641 * @param {String} html
21646 * Fires when the textarea is updated with content from the editor iframe.
21647 * @param {Roo.HtmlEditorCore} this
21648 * @param {String} html
21653 * Fires when the iframe editor is updated with content from the textarea.
21654 * @param {Roo.HtmlEditorCore} this
21655 * @param {String} html
21660 * @event editorevent
21661 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21662 * @param {Roo.HtmlEditorCore} this
21668 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21670 // defaults : white / black...
21671 this.applyBlacklists();
21678 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21682 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21688 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21693 * @cfg {Number} height (in pixels)
21697 * @cfg {Number} width (in pixels)
21702 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21705 stylesheets: false,
21710 // private properties
21711 validationEvent : false,
21713 initialized : false,
21715 sourceEditMode : false,
21716 onFocus : Roo.emptyFn,
21718 hideMode:'offsets',
21722 // blacklist + whitelisted elements..
21729 * Protected method that will not generally be called directly. It
21730 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21731 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21733 getDocMarkup : function(){
21737 // inherit styels from page...??
21738 if (this.stylesheets === false) {
21740 Roo.get(document.head).select('style').each(function(node) {
21741 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21744 Roo.get(document.head).select('link').each(function(node) {
21745 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21748 } else if (!this.stylesheets.length) {
21750 st = '<style type="text/css">' +
21751 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21754 st = '<style type="text/css">' +
21759 st += '<style type="text/css">' +
21760 'IMG { cursor: pointer } ' +
21763 var cls = 'roo-htmleditor-body';
21765 if(this.bodyCls.length){
21766 cls += ' ' + this.bodyCls;
21769 return '<html><head>' + st +
21770 //<style type="text/css">' +
21771 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21773 ' </head><body class="' + cls + '"></body></html>';
21777 onRender : function(ct, position)
21780 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21781 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21784 this.el.dom.style.border = '0 none';
21785 this.el.dom.setAttribute('tabIndex', -1);
21786 this.el.addClass('x-hidden hide');
21790 if(Roo.isIE){ // fix IE 1px bogus margin
21791 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21795 this.frameId = Roo.id();
21799 var iframe = this.owner.wrap.createChild({
21801 cls: 'form-control', // bootstrap..
21803 name: this.frameId,
21804 frameBorder : 'no',
21805 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21810 this.iframe = iframe.dom;
21812 this.assignDocWin();
21814 this.doc.designMode = 'on';
21817 this.doc.write(this.getDocMarkup());
21821 var task = { // must defer to wait for browser to be ready
21823 //console.log("run task?" + this.doc.readyState);
21824 this.assignDocWin();
21825 if(this.doc.body || this.doc.readyState == 'complete'){
21827 this.doc.designMode="on";
21831 Roo.TaskMgr.stop(task);
21832 this.initEditor.defer(10, this);
21839 Roo.TaskMgr.start(task);
21844 onResize : function(w, h)
21846 Roo.log('resize: ' +w + ',' + h );
21847 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21851 if(typeof w == 'number'){
21853 this.iframe.style.width = w + 'px';
21855 if(typeof h == 'number'){
21857 this.iframe.style.height = h + 'px';
21859 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21866 * Toggles the editor between standard and source edit mode.
21867 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21869 toggleSourceEdit : function(sourceEditMode){
21871 this.sourceEditMode = sourceEditMode === true;
21873 if(this.sourceEditMode){
21875 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21878 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21879 //this.iframe.className = '';
21882 //this.setSize(this.owner.wrap.getSize());
21883 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21890 * Protected method that will not generally be called directly. If you need/want
21891 * custom HTML cleanup, this is the method you should override.
21892 * @param {String} html The HTML to be cleaned
21893 * return {String} The cleaned HTML
21895 cleanHtml : function(html){
21896 html = String(html);
21897 if(html.length > 5){
21898 if(Roo.isSafari){ // strip safari nonsense
21899 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21902 if(html == ' '){
21909 * HTML Editor -> Textarea
21910 * Protected method that will not generally be called directly. Syncs the contents
21911 * of the editor iframe with the textarea.
21913 syncValue : function(){
21914 if(this.initialized){
21915 var bd = (this.doc.body || this.doc.documentElement);
21916 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21917 var html = bd.innerHTML;
21919 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21920 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21922 html = '<div style="'+m[0]+'">' + html + '</div>';
21925 html = this.cleanHtml(html);
21926 // fix up the special chars.. normaly like back quotes in word...
21927 // however we do not want to do this with chinese..
21928 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21929 var cc = b.charCodeAt();
21931 (cc >= 0x4E00 && cc < 0xA000 ) ||
21932 (cc >= 0x3400 && cc < 0x4E00 ) ||
21933 (cc >= 0xf900 && cc < 0xfb00 )
21939 if(this.owner.fireEvent('beforesync', this, html) !== false){
21940 this.el.dom.value = html;
21941 this.owner.fireEvent('sync', this, html);
21947 * Protected method that will not generally be called directly. Pushes the value of the textarea
21948 * into the iframe editor.
21950 pushValue : function(){
21951 if(this.initialized){
21952 var v = this.el.dom.value.trim();
21954 // if(v.length < 1){
21958 if(this.owner.fireEvent('beforepush', this, v) !== false){
21959 var d = (this.doc.body || this.doc.documentElement);
21961 this.cleanUpPaste();
21962 this.el.dom.value = d.innerHTML;
21963 this.owner.fireEvent('push', this, v);
21969 deferFocus : function(){
21970 this.focus.defer(10, this);
21974 focus : function(){
21975 if(this.win && !this.sourceEditMode){
21982 assignDocWin: function()
21984 var iframe = this.iframe;
21987 this.doc = iframe.contentWindow.document;
21988 this.win = iframe.contentWindow;
21990 // if (!Roo.get(this.frameId)) {
21993 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21994 // this.win = Roo.get(this.frameId).dom.contentWindow;
21996 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22000 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22001 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22006 initEditor : function(){
22007 //console.log("INIT EDITOR");
22008 this.assignDocWin();
22012 this.doc.designMode="on";
22014 this.doc.write(this.getDocMarkup());
22017 var dbody = (this.doc.body || this.doc.documentElement);
22018 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22019 // this copies styles from the containing element into thsi one..
22020 // not sure why we need all of this..
22021 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22023 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22024 //ss['background-attachment'] = 'fixed'; // w3c
22025 dbody.bgProperties = 'fixed'; // ie
22026 //Roo.DomHelper.applyStyles(dbody, ss);
22027 Roo.EventManager.on(this.doc, {
22028 //'mousedown': this.onEditorEvent,
22029 'mouseup': this.onEditorEvent,
22030 'dblclick': this.onEditorEvent,
22031 'click': this.onEditorEvent,
22032 'keyup': this.onEditorEvent,
22037 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22039 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22040 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22042 this.initialized = true;
22044 this.owner.fireEvent('initialize', this);
22049 onDestroy : function(){
22055 //for (var i =0; i < this.toolbars.length;i++) {
22056 // // fixme - ask toolbars for heights?
22057 // this.toolbars[i].onDestroy();
22060 //this.wrap.dom.innerHTML = '';
22061 //this.wrap.remove();
22066 onFirstFocus : function(){
22068 this.assignDocWin();
22071 this.activated = true;
22074 if(Roo.isGecko){ // prevent silly gecko errors
22076 var s = this.win.getSelection();
22077 if(!s.focusNode || s.focusNode.nodeType != 3){
22078 var r = s.getRangeAt(0);
22079 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22084 this.execCmd('useCSS', true);
22085 this.execCmd('styleWithCSS', false);
22088 this.owner.fireEvent('activate', this);
22092 adjustFont: function(btn){
22093 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22094 //if(Roo.isSafari){ // safari
22097 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22098 if(Roo.isSafari){ // safari
22099 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22100 v = (v < 10) ? 10 : v;
22101 v = (v > 48) ? 48 : v;
22102 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22107 v = Math.max(1, v+adjust);
22109 this.execCmd('FontSize', v );
22112 onEditorEvent : function(e)
22114 this.owner.fireEvent('editorevent', this, e);
22115 // this.updateToolbar();
22116 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22119 insertTag : function(tg)
22121 // could be a bit smarter... -> wrap the current selected tRoo..
22122 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22124 range = this.createRange(this.getSelection());
22125 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22126 wrappingNode.appendChild(range.extractContents());
22127 range.insertNode(wrappingNode);
22134 this.execCmd("formatblock", tg);
22138 insertText : function(txt)
22142 var range = this.createRange();
22143 range.deleteContents();
22144 //alert(Sender.getAttribute('label'));
22146 range.insertNode(this.doc.createTextNode(txt));
22152 * Executes a Midas editor command on the editor document and performs necessary focus and
22153 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22154 * @param {String} cmd The Midas command
22155 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22157 relayCmd : function(cmd, value){
22159 this.execCmd(cmd, value);
22160 this.owner.fireEvent('editorevent', this);
22161 //this.updateToolbar();
22162 this.owner.deferFocus();
22166 * Executes a Midas editor command directly on the editor document.
22167 * For visual commands, you should use {@link #relayCmd} instead.
22168 * <b>This should only be called after the editor is initialized.</b>
22169 * @param {String} cmd The Midas command
22170 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22172 execCmd : function(cmd, value){
22173 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22180 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22182 * @param {String} text | dom node..
22184 insertAtCursor : function(text)
22187 if(!this.activated){
22193 var r = this.doc.selection.createRange();
22204 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22208 // from jquery ui (MIT licenced)
22210 var win = this.win;
22212 if (win.getSelection && win.getSelection().getRangeAt) {
22213 range = win.getSelection().getRangeAt(0);
22214 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22215 range.insertNode(node);
22216 } else if (win.document.selection && win.document.selection.createRange) {
22217 // no firefox support
22218 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22219 win.document.selection.createRange().pasteHTML(txt);
22221 // no firefox support
22222 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22223 this.execCmd('InsertHTML', txt);
22232 mozKeyPress : function(e){
22234 var c = e.getCharCode(), cmd;
22237 c = String.fromCharCode(c).toLowerCase();
22251 this.cleanUpPaste.defer(100, this);
22259 e.preventDefault();
22267 fixKeys : function(){ // load time branching for fastest keydown performance
22269 return function(e){
22270 var k = e.getKey(), r;
22273 r = this.doc.selection.createRange();
22276 r.pasteHTML('    ');
22283 r = this.doc.selection.createRange();
22285 var target = r.parentElement();
22286 if(!target || target.tagName.toLowerCase() != 'li'){
22288 r.pasteHTML('<br />');
22294 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22295 this.cleanUpPaste.defer(100, this);
22301 }else if(Roo.isOpera){
22302 return function(e){
22303 var k = e.getKey();
22307 this.execCmd('InsertHTML','    ');
22310 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22311 this.cleanUpPaste.defer(100, this);
22316 }else if(Roo.isSafari){
22317 return function(e){
22318 var k = e.getKey();
22322 this.execCmd('InsertText','\t');
22326 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22327 this.cleanUpPaste.defer(100, this);
22335 getAllAncestors: function()
22337 var p = this.getSelectedNode();
22340 a.push(p); // push blank onto stack..
22341 p = this.getParentElement();
22345 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22349 a.push(this.doc.body);
22353 lastSelNode : false,
22356 getSelection : function()
22358 this.assignDocWin();
22359 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22362 getSelectedNode: function()
22364 // this may only work on Gecko!!!
22366 // should we cache this!!!!
22371 var range = this.createRange(this.getSelection()).cloneRange();
22374 var parent = range.parentElement();
22376 var testRange = range.duplicate();
22377 testRange.moveToElementText(parent);
22378 if (testRange.inRange(range)) {
22381 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22384 parent = parent.parentElement;
22389 // is ancestor a text element.
22390 var ac = range.commonAncestorContainer;
22391 if (ac.nodeType == 3) {
22392 ac = ac.parentNode;
22395 var ar = ac.childNodes;
22398 var other_nodes = [];
22399 var has_other_nodes = false;
22400 for (var i=0;i<ar.length;i++) {
22401 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22404 // fullly contained node.
22406 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22411 // probably selected..
22412 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22413 other_nodes.push(ar[i]);
22417 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22422 has_other_nodes = true;
22424 if (!nodes.length && other_nodes.length) {
22425 nodes= other_nodes;
22427 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22433 createRange: function(sel)
22435 // this has strange effects when using with
22436 // top toolbar - not sure if it's a great idea.
22437 //this.editor.contentWindow.focus();
22438 if (typeof sel != "undefined") {
22440 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22442 return this.doc.createRange();
22445 return this.doc.createRange();
22448 getParentElement: function()
22451 this.assignDocWin();
22452 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22454 var range = this.createRange(sel);
22457 var p = range.commonAncestorContainer;
22458 while (p.nodeType == 3) { // text node
22469 * Range intersection.. the hard stuff...
22473 * [ -- selected range --- ]
22477 * if end is before start or hits it. fail.
22478 * if start is after end or hits it fail.
22480 * if either hits (but other is outside. - then it's not
22486 // @see http://www.thismuchiknow.co.uk/?p=64.
22487 rangeIntersectsNode : function(range, node)
22489 var nodeRange = node.ownerDocument.createRange();
22491 nodeRange.selectNode(node);
22493 nodeRange.selectNodeContents(node);
22496 var rangeStartRange = range.cloneRange();
22497 rangeStartRange.collapse(true);
22499 var rangeEndRange = range.cloneRange();
22500 rangeEndRange.collapse(false);
22502 var nodeStartRange = nodeRange.cloneRange();
22503 nodeStartRange.collapse(true);
22505 var nodeEndRange = nodeRange.cloneRange();
22506 nodeEndRange.collapse(false);
22508 return rangeStartRange.compareBoundaryPoints(
22509 Range.START_TO_START, nodeEndRange) == -1 &&
22510 rangeEndRange.compareBoundaryPoints(
22511 Range.START_TO_START, nodeStartRange) == 1;
22515 rangeCompareNode : function(range, node)
22517 var nodeRange = node.ownerDocument.createRange();
22519 nodeRange.selectNode(node);
22521 nodeRange.selectNodeContents(node);
22525 range.collapse(true);
22527 nodeRange.collapse(true);
22529 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22530 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22532 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22534 var nodeIsBefore = ss == 1;
22535 var nodeIsAfter = ee == -1;
22537 if (nodeIsBefore && nodeIsAfter) {
22540 if (!nodeIsBefore && nodeIsAfter) {
22541 return 1; //right trailed.
22544 if (nodeIsBefore && !nodeIsAfter) {
22545 return 2; // left trailed.
22551 // private? - in a new class?
22552 cleanUpPaste : function()
22554 // cleans up the whole document..
22555 Roo.log('cleanuppaste');
22557 this.cleanUpChildren(this.doc.body);
22558 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22559 if (clean != this.doc.body.innerHTML) {
22560 this.doc.body.innerHTML = clean;
22565 cleanWordChars : function(input) {// change the chars to hex code
22566 var he = Roo.HtmlEditorCore;
22568 var output = input;
22569 Roo.each(he.swapCodes, function(sw) {
22570 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22572 output = output.replace(swapper, sw[1]);
22579 cleanUpChildren : function (n)
22581 if (!n.childNodes.length) {
22584 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22585 this.cleanUpChild(n.childNodes[i]);
22592 cleanUpChild : function (node)
22595 //console.log(node);
22596 if (node.nodeName == "#text") {
22597 // clean up silly Windows -- stuff?
22600 if (node.nodeName == "#comment") {
22601 node.parentNode.removeChild(node);
22602 // clean up silly Windows -- stuff?
22605 var lcname = node.tagName.toLowerCase();
22606 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22607 // whitelist of tags..
22609 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22611 node.parentNode.removeChild(node);
22616 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22618 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22619 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22621 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22622 // remove_keep_children = true;
22625 if (remove_keep_children) {
22626 this.cleanUpChildren(node);
22627 // inserts everything just before this node...
22628 while (node.childNodes.length) {
22629 var cn = node.childNodes[0];
22630 node.removeChild(cn);
22631 node.parentNode.insertBefore(cn, node);
22633 node.parentNode.removeChild(node);
22637 if (!node.attributes || !node.attributes.length) {
22638 this.cleanUpChildren(node);
22642 function cleanAttr(n,v)
22645 if (v.match(/^\./) || v.match(/^\//)) {
22648 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22651 if (v.match(/^#/)) {
22654 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22655 node.removeAttribute(n);
22659 var cwhite = this.cwhite;
22660 var cblack = this.cblack;
22662 function cleanStyle(n,v)
22664 if (v.match(/expression/)) { //XSS?? should we even bother..
22665 node.removeAttribute(n);
22669 var parts = v.split(/;/);
22672 Roo.each(parts, function(p) {
22673 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22677 var l = p.split(':').shift().replace(/\s+/g,'');
22678 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22680 if ( cwhite.length && cblack.indexOf(l) > -1) {
22681 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22682 //node.removeAttribute(n);
22686 // only allow 'c whitelisted system attributes'
22687 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22688 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22689 //node.removeAttribute(n);
22699 if (clean.length) {
22700 node.setAttribute(n, clean.join(';'));
22702 node.removeAttribute(n);
22708 for (var i = node.attributes.length-1; i > -1 ; i--) {
22709 var a = node.attributes[i];
22712 if (a.name.toLowerCase().substr(0,2)=='on') {
22713 node.removeAttribute(a.name);
22716 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22717 node.removeAttribute(a.name);
22720 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22721 cleanAttr(a.name,a.value); // fixme..
22724 if (a.name == 'style') {
22725 cleanStyle(a.name,a.value);
22728 /// clean up MS crap..
22729 // tecnically this should be a list of valid class'es..
22732 if (a.name == 'class') {
22733 if (a.value.match(/^Mso/)) {
22734 node.className = '';
22737 if (a.value.match(/^body$/)) {
22738 node.className = '';
22749 this.cleanUpChildren(node);
22755 * Clean up MS wordisms...
22757 cleanWord : function(node)
22762 this.cleanWord(this.doc.body);
22765 if (node.nodeName == "#text") {
22766 // clean up silly Windows -- stuff?
22769 if (node.nodeName == "#comment") {
22770 node.parentNode.removeChild(node);
22771 // clean up silly Windows -- stuff?
22775 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22776 node.parentNode.removeChild(node);
22780 // remove - but keep children..
22781 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22782 while (node.childNodes.length) {
22783 var cn = node.childNodes[0];
22784 node.removeChild(cn);
22785 node.parentNode.insertBefore(cn, node);
22787 node.parentNode.removeChild(node);
22788 this.iterateChildren(node, this.cleanWord);
22792 if (node.className.length) {
22794 var cn = node.className.split(/\W+/);
22796 Roo.each(cn, function(cls) {
22797 if (cls.match(/Mso[a-zA-Z]+/)) {
22802 node.className = cna.length ? cna.join(' ') : '';
22804 node.removeAttribute("class");
22808 if (node.hasAttribute("lang")) {
22809 node.removeAttribute("lang");
22812 if (node.hasAttribute("style")) {
22814 var styles = node.getAttribute("style").split(";");
22816 Roo.each(styles, function(s) {
22817 if (!s.match(/:/)) {
22820 var kv = s.split(":");
22821 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22824 // what ever is left... we allow.
22827 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22828 if (!nstyle.length) {
22829 node.removeAttribute('style');
22832 this.iterateChildren(node, this.cleanWord);
22838 * iterateChildren of a Node, calling fn each time, using this as the scole..
22839 * @param {DomNode} node node to iterate children of.
22840 * @param {Function} fn method of this class to call on each item.
22842 iterateChildren : function(node, fn)
22844 if (!node.childNodes.length) {
22847 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22848 fn.call(this, node.childNodes[i])
22854 * cleanTableWidths.
22856 * Quite often pasting from word etc.. results in tables with column and widths.
22857 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22860 cleanTableWidths : function(node)
22865 this.cleanTableWidths(this.doc.body);
22870 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22873 Roo.log(node.tagName);
22874 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22875 this.iterateChildren(node, this.cleanTableWidths);
22878 if (node.hasAttribute('width')) {
22879 node.removeAttribute('width');
22883 if (node.hasAttribute("style")) {
22886 var styles = node.getAttribute("style").split(";");
22888 Roo.each(styles, function(s) {
22889 if (!s.match(/:/)) {
22892 var kv = s.split(":");
22893 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22896 // what ever is left... we allow.
22899 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22900 if (!nstyle.length) {
22901 node.removeAttribute('style');
22905 this.iterateChildren(node, this.cleanTableWidths);
22913 domToHTML : function(currentElement, depth, nopadtext) {
22915 depth = depth || 0;
22916 nopadtext = nopadtext || false;
22918 if (!currentElement) {
22919 return this.domToHTML(this.doc.body);
22922 //Roo.log(currentElement);
22924 var allText = false;
22925 var nodeName = currentElement.nodeName;
22926 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22928 if (nodeName == '#text') {
22930 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22935 if (nodeName != 'BODY') {
22938 // Prints the node tagName, such as <A>, <IMG>, etc
22941 for(i = 0; i < currentElement.attributes.length;i++) {
22943 var aname = currentElement.attributes.item(i).name;
22944 if (!currentElement.attributes.item(i).value.length) {
22947 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22950 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22959 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22962 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22967 // Traverse the tree
22969 var currentElementChild = currentElement.childNodes.item(i);
22970 var allText = true;
22971 var innerHTML = '';
22973 while (currentElementChild) {
22974 // Formatting code (indent the tree so it looks nice on the screen)
22975 var nopad = nopadtext;
22976 if (lastnode == 'SPAN') {
22980 if (currentElementChild.nodeName == '#text') {
22981 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22982 toadd = nopadtext ? toadd : toadd.trim();
22983 if (!nopad && toadd.length > 80) {
22984 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22986 innerHTML += toadd;
22989 currentElementChild = currentElement.childNodes.item(i);
22995 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22997 // Recursively traverse the tree structure of the child node
22998 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22999 lastnode = currentElementChild.nodeName;
23001 currentElementChild=currentElement.childNodes.item(i);
23007 // The remaining code is mostly for formatting the tree
23008 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23013 ret+= "</"+tagName+">";
23019 applyBlacklists : function()
23021 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23022 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23026 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23027 if (b.indexOf(tag) > -1) {
23030 this.white.push(tag);
23034 Roo.each(w, function(tag) {
23035 if (b.indexOf(tag) > -1) {
23038 if (this.white.indexOf(tag) > -1) {
23041 this.white.push(tag);
23046 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23047 if (w.indexOf(tag) > -1) {
23050 this.black.push(tag);
23054 Roo.each(b, function(tag) {
23055 if (w.indexOf(tag) > -1) {
23058 if (this.black.indexOf(tag) > -1) {
23061 this.black.push(tag);
23066 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23067 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23071 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23072 if (b.indexOf(tag) > -1) {
23075 this.cwhite.push(tag);
23079 Roo.each(w, function(tag) {
23080 if (b.indexOf(tag) > -1) {
23083 if (this.cwhite.indexOf(tag) > -1) {
23086 this.cwhite.push(tag);
23091 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23092 if (w.indexOf(tag) > -1) {
23095 this.cblack.push(tag);
23099 Roo.each(b, function(tag) {
23100 if (w.indexOf(tag) > -1) {
23103 if (this.cblack.indexOf(tag) > -1) {
23106 this.cblack.push(tag);
23111 setStylesheets : function(stylesheets)
23113 if(typeof(stylesheets) == 'string'){
23114 Roo.get(this.iframe.contentDocument.head).createChild({
23116 rel : 'stylesheet',
23125 Roo.each(stylesheets, function(s) {
23130 Roo.get(_this.iframe.contentDocument.head).createChild({
23132 rel : 'stylesheet',
23141 removeStylesheets : function()
23145 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23150 setStyle : function(style)
23152 Roo.get(this.iframe.contentDocument.head).createChild({
23161 // hide stuff that is not compatible
23175 * @event specialkey
23179 * @cfg {String} fieldClass @hide
23182 * @cfg {String} focusClass @hide
23185 * @cfg {String} autoCreate @hide
23188 * @cfg {String} inputType @hide
23191 * @cfg {String} invalidClass @hide
23194 * @cfg {String} invalidText @hide
23197 * @cfg {String} msgFx @hide
23200 * @cfg {String} validateOnBlur @hide
23204 Roo.HtmlEditorCore.white = [
23205 'area', 'br', 'img', 'input', 'hr', 'wbr',
23207 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23208 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23209 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23210 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23211 'table', 'ul', 'xmp',
23213 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23216 'dir', 'menu', 'ol', 'ul', 'dl',
23222 Roo.HtmlEditorCore.black = [
23223 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23225 'base', 'basefont', 'bgsound', 'blink', 'body',
23226 'frame', 'frameset', 'head', 'html', 'ilayer',
23227 'iframe', 'layer', 'link', 'meta', 'object',
23228 'script', 'style' ,'title', 'xml' // clean later..
23230 Roo.HtmlEditorCore.clean = [
23231 'script', 'style', 'title', 'xml'
23233 Roo.HtmlEditorCore.remove = [
23238 Roo.HtmlEditorCore.ablack = [
23242 Roo.HtmlEditorCore.aclean = [
23243 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23247 Roo.HtmlEditorCore.pwhite= [
23248 'http', 'https', 'mailto'
23251 // white listed style attributes.
23252 Roo.HtmlEditorCore.cwhite= [
23253 // 'text-align', /// default is to allow most things..
23259 // black listed style attributes.
23260 Roo.HtmlEditorCore.cblack= [
23261 // 'font-size' -- this can be set by the project
23265 Roo.HtmlEditorCore.swapCodes =[
23284 * @class Roo.bootstrap.HtmlEditor
23285 * @extends Roo.bootstrap.TextArea
23286 * Bootstrap HtmlEditor class
23289 * Create a new HtmlEditor
23290 * @param {Object} config The config object
23293 Roo.bootstrap.HtmlEditor = function(config){
23294 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23295 if (!this.toolbars) {
23296 this.toolbars = [];
23299 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23302 * @event initialize
23303 * Fires when the editor is fully initialized (including the iframe)
23304 * @param {HtmlEditor} this
23309 * Fires when the editor is first receives the focus. Any insertion must wait
23310 * until after this event.
23311 * @param {HtmlEditor} this
23315 * @event beforesync
23316 * Fires before the textarea is updated with content from the editor iframe. Return false
23317 * to cancel the sync.
23318 * @param {HtmlEditor} this
23319 * @param {String} html
23323 * @event beforepush
23324 * Fires before the iframe editor is updated with content from the textarea. Return false
23325 * to cancel the push.
23326 * @param {HtmlEditor} this
23327 * @param {String} html
23332 * Fires when the textarea is updated with content from the editor iframe.
23333 * @param {HtmlEditor} this
23334 * @param {String} html
23339 * Fires when the iframe editor is updated with content from the textarea.
23340 * @param {HtmlEditor} this
23341 * @param {String} html
23345 * @event editmodechange
23346 * Fires when the editor switches edit modes
23347 * @param {HtmlEditor} this
23348 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23350 editmodechange: true,
23352 * @event editorevent
23353 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23354 * @param {HtmlEditor} this
23358 * @event firstfocus
23359 * Fires when on first focus - needed by toolbars..
23360 * @param {HtmlEditor} this
23365 * Auto save the htmlEditor value as a file into Events
23366 * @param {HtmlEditor} this
23370 * @event savedpreview
23371 * preview the saved version of htmlEditor
23372 * @param {HtmlEditor} this
23379 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23383 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23388 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23393 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23398 * @cfg {Number} height (in pixels)
23402 * @cfg {Number} width (in pixels)
23407 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23410 stylesheets: false,
23415 // private properties
23416 validationEvent : false,
23418 initialized : false,
23421 onFocus : Roo.emptyFn,
23423 hideMode:'offsets',
23425 tbContainer : false,
23429 toolbarContainer :function() {
23430 return this.wrap.select('.x-html-editor-tb',true).first();
23434 * Protected method that will not generally be called directly. It
23435 * is called when the editor creates its toolbar. Override this method if you need to
23436 * add custom toolbar buttons.
23437 * @param {HtmlEditor} editor
23439 createToolbar : function(){
23440 Roo.log('renewing');
23441 Roo.log("create toolbars");
23443 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23444 this.toolbars[0].render(this.toolbarContainer());
23448 // if (!editor.toolbars || !editor.toolbars.length) {
23449 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23452 // for (var i =0 ; i < editor.toolbars.length;i++) {
23453 // editor.toolbars[i] = Roo.factory(
23454 // typeof(editor.toolbars[i]) == 'string' ?
23455 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23456 // Roo.bootstrap.HtmlEditor);
23457 // editor.toolbars[i].init(editor);
23463 onRender : function(ct, position)
23465 // Roo.log("Call onRender: " + this.xtype);
23467 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23469 this.wrap = this.inputEl().wrap({
23470 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23473 this.editorcore.onRender(ct, position);
23475 if (this.resizable) {
23476 this.resizeEl = new Roo.Resizable(this.wrap, {
23480 minHeight : this.height,
23481 height: this.height,
23482 handles : this.resizable,
23485 resize : function(r, w, h) {
23486 _t.onResize(w,h); // -something
23492 this.createToolbar(this);
23495 if(!this.width && this.resizable){
23496 this.setSize(this.wrap.getSize());
23498 if (this.resizeEl) {
23499 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23500 // should trigger onReize..
23506 onResize : function(w, h)
23508 Roo.log('resize: ' +w + ',' + h );
23509 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23513 if(this.inputEl() ){
23514 if(typeof w == 'number'){
23515 var aw = w - this.wrap.getFrameWidth('lr');
23516 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23519 if(typeof h == 'number'){
23520 var tbh = -11; // fixme it needs to tool bar size!
23521 for (var i =0; i < this.toolbars.length;i++) {
23522 // fixme - ask toolbars for heights?
23523 tbh += this.toolbars[i].el.getHeight();
23524 //if (this.toolbars[i].footer) {
23525 // tbh += this.toolbars[i].footer.el.getHeight();
23533 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23534 ah -= 5; // knock a few pixes off for look..
23535 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23539 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23540 this.editorcore.onResize(ew,eh);
23545 * Toggles the editor between standard and source edit mode.
23546 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23548 toggleSourceEdit : function(sourceEditMode)
23550 this.editorcore.toggleSourceEdit(sourceEditMode);
23552 if(this.editorcore.sourceEditMode){
23553 Roo.log('editor - showing textarea');
23556 // Roo.log(this.syncValue());
23558 this.inputEl().removeClass(['hide', 'x-hidden']);
23559 this.inputEl().dom.removeAttribute('tabIndex');
23560 this.inputEl().focus();
23562 Roo.log('editor - hiding textarea');
23564 // Roo.log(this.pushValue());
23567 this.inputEl().addClass(['hide', 'x-hidden']);
23568 this.inputEl().dom.setAttribute('tabIndex', -1);
23569 //this.deferFocus();
23572 if(this.resizable){
23573 this.setSize(this.wrap.getSize());
23576 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23579 // private (for BoxComponent)
23580 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23582 // private (for BoxComponent)
23583 getResizeEl : function(){
23587 // private (for BoxComponent)
23588 getPositionEl : function(){
23593 initEvents : function(){
23594 this.originalValue = this.getValue();
23598 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23601 // markInvalid : Roo.emptyFn,
23603 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23606 // clearInvalid : Roo.emptyFn,
23608 setValue : function(v){
23609 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23610 this.editorcore.pushValue();
23615 deferFocus : function(){
23616 this.focus.defer(10, this);
23620 focus : function(){
23621 this.editorcore.focus();
23627 onDestroy : function(){
23633 for (var i =0; i < this.toolbars.length;i++) {
23634 // fixme - ask toolbars for heights?
23635 this.toolbars[i].onDestroy();
23638 this.wrap.dom.innerHTML = '';
23639 this.wrap.remove();
23644 onFirstFocus : function(){
23645 //Roo.log("onFirstFocus");
23646 this.editorcore.onFirstFocus();
23647 for (var i =0; i < this.toolbars.length;i++) {
23648 this.toolbars[i].onFirstFocus();
23654 syncValue : function()
23656 this.editorcore.syncValue();
23659 pushValue : function()
23661 this.editorcore.pushValue();
23665 // hide stuff that is not compatible
23679 * @event specialkey
23683 * @cfg {String} fieldClass @hide
23686 * @cfg {String} focusClass @hide
23689 * @cfg {String} autoCreate @hide
23692 * @cfg {String} inputType @hide
23695 * @cfg {String} invalidClass @hide
23698 * @cfg {String} invalidText @hide
23701 * @cfg {String} msgFx @hide
23704 * @cfg {String} validateOnBlur @hide
23713 Roo.namespace('Roo.bootstrap.htmleditor');
23715 * @class Roo.bootstrap.HtmlEditorToolbar1
23720 new Roo.bootstrap.HtmlEditor({
23723 new Roo.bootstrap.HtmlEditorToolbar1({
23724 disable : { fonts: 1 , format: 1, ..., ... , ...],
23730 * @cfg {Object} disable List of elements to disable..
23731 * @cfg {Array} btns List of additional buttons.
23735 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23738 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23741 Roo.apply(this, config);
23743 // default disabled, based on 'good practice'..
23744 this.disable = this.disable || {};
23745 Roo.applyIf(this.disable, {
23748 specialElements : true
23750 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23752 this.editor = config.editor;
23753 this.editorcore = config.editor.editorcore;
23755 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23757 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23758 // dont call parent... till later.
23760 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23765 editorcore : false,
23770 "h1","h2","h3","h4","h5","h6",
23772 "abbr", "acronym", "address", "cite", "samp", "var",
23776 onRender : function(ct, position)
23778 // Roo.log("Call onRender: " + this.xtype);
23780 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23782 this.el.dom.style.marginBottom = '0';
23784 var editorcore = this.editorcore;
23785 var editor= this.editor;
23788 var btn = function(id,cmd , toggle, handler, html){
23790 var event = toggle ? 'toggle' : 'click';
23795 xns: Roo.bootstrap,
23798 enableToggle:toggle !== false,
23800 pressed : toggle ? false : null,
23803 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23804 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23810 // var cb_box = function...
23815 xns: Roo.bootstrap,
23816 glyphicon : 'font',
23820 xns: Roo.bootstrap,
23824 Roo.each(this.formats, function(f) {
23825 style.menu.items.push({
23827 xns: Roo.bootstrap,
23828 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23833 editorcore.insertTag(this.tagname);
23840 children.push(style);
23842 btn('bold',false,true);
23843 btn('italic',false,true);
23844 btn('align-left', 'justifyleft',true);
23845 btn('align-center', 'justifycenter',true);
23846 btn('align-right' , 'justifyright',true);
23847 btn('link', false, false, function(btn) {
23848 //Roo.log("create link?");
23849 var url = prompt(this.createLinkText, this.defaultLinkValue);
23850 if(url && url != 'http:/'+'/'){
23851 this.editorcore.relayCmd('createlink', url);
23854 btn('list','insertunorderedlist',true);
23855 btn('pencil', false,true, function(btn){
23857 this.toggleSourceEdit(btn.pressed);
23860 if (this.editor.btns.length > 0) {
23861 for (var i = 0; i<this.editor.btns.length; i++) {
23862 children.push(this.editor.btns[i]);
23870 xns: Roo.bootstrap,
23875 xns: Roo.bootstrap,
23880 cog.menu.items.push({
23882 xns: Roo.bootstrap,
23883 html : Clean styles,
23888 editorcore.insertTag(this.tagname);
23897 this.xtype = 'NavSimplebar';
23899 for(var i=0;i< children.length;i++) {
23901 this.buttons.add(this.addxtypeChild(children[i]));
23905 editor.on('editorevent', this.updateToolbar, this);
23907 onBtnClick : function(id)
23909 this.editorcore.relayCmd(id);
23910 this.editorcore.focus();
23914 * Protected method that will not generally be called directly. It triggers
23915 * a toolbar update by reading the markup state of the current selection in the editor.
23917 updateToolbar: function(){
23919 if(!this.editorcore.activated){
23920 this.editor.onFirstFocus(); // is this neeed?
23924 var btns = this.buttons;
23925 var doc = this.editorcore.doc;
23926 btns.get('bold').setActive(doc.queryCommandState('bold'));
23927 btns.get('italic').setActive(doc.queryCommandState('italic'));
23928 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23930 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23931 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23932 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23934 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23935 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23938 var ans = this.editorcore.getAllAncestors();
23939 if (this.formatCombo) {
23942 var store = this.formatCombo.store;
23943 this.formatCombo.setValue("");
23944 for (var i =0; i < ans.length;i++) {
23945 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23947 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23955 // hides menus... - so this cant be on a menu...
23956 Roo.bootstrap.MenuMgr.hideAll();
23958 Roo.bootstrap.MenuMgr.hideAll();
23959 //this.editorsyncValue();
23961 onFirstFocus: function() {
23962 this.buttons.each(function(item){
23966 toggleSourceEdit : function(sourceEditMode){
23969 if(sourceEditMode){
23970 Roo.log("disabling buttons");
23971 this.buttons.each( function(item){
23972 if(item.cmd != 'pencil'){
23978 Roo.log("enabling buttons");
23979 if(this.editorcore.initialized){
23980 this.buttons.each( function(item){
23986 Roo.log("calling toggole on editor");
23987 // tell the editor that it's been pressed..
23988 this.editor.toggleSourceEdit(sourceEditMode);
23998 * @class Roo.bootstrap.Table.AbstractSelectionModel
23999 * @extends Roo.util.Observable
24000 * Abstract base class for grid SelectionModels. It provides the interface that should be
24001 * implemented by descendant classes. This class should not be directly instantiated.
24004 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24005 this.locked = false;
24006 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24010 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24011 /** @ignore Called by the grid automatically. Do not call directly. */
24012 init : function(grid){
24018 * Locks the selections.
24021 this.locked = true;
24025 * Unlocks the selections.
24027 unlock : function(){
24028 this.locked = false;
24032 * Returns true if the selections are locked.
24033 * @return {Boolean}
24035 isLocked : function(){
24036 return this.locked;
24040 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24041 * @class Roo.bootstrap.Table.RowSelectionModel
24042 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24043 * It supports multiple selections and keyboard selection/navigation.
24045 * @param {Object} config
24048 Roo.bootstrap.Table.RowSelectionModel = function(config){
24049 Roo.apply(this, config);
24050 this.selections = new Roo.util.MixedCollection(false, function(o){
24055 this.lastActive = false;
24059 * @event selectionchange
24060 * Fires when the selection changes
24061 * @param {SelectionModel} this
24063 "selectionchange" : true,
24065 * @event afterselectionchange
24066 * Fires after the selection changes (eg. by key press or clicking)
24067 * @param {SelectionModel} this
24069 "afterselectionchange" : true,
24071 * @event beforerowselect
24072 * Fires when a row is selected being selected, return false to cancel.
24073 * @param {SelectionModel} this
24074 * @param {Number} rowIndex The selected index
24075 * @param {Boolean} keepExisting False if other selections will be cleared
24077 "beforerowselect" : true,
24080 * Fires when a row is selected.
24081 * @param {SelectionModel} this
24082 * @param {Number} rowIndex The selected index
24083 * @param {Roo.data.Record} r The record
24085 "rowselect" : true,
24087 * @event rowdeselect
24088 * Fires when a row is deselected.
24089 * @param {SelectionModel} this
24090 * @param {Number} rowIndex The selected index
24092 "rowdeselect" : true
24094 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24095 this.locked = false;
24098 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24100 * @cfg {Boolean} singleSelect
24101 * True to allow selection of only one row at a time (defaults to false)
24103 singleSelect : false,
24106 initEvents : function()
24109 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24110 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24111 //}else{ // allow click to work like normal
24112 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24114 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24115 this.grid.on("rowclick", this.handleMouseDown, this);
24117 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24118 "up" : function(e){
24120 this.selectPrevious(e.shiftKey);
24121 }else if(this.last !== false && this.lastActive !== false){
24122 var last = this.last;
24123 this.selectRange(this.last, this.lastActive-1);
24124 this.grid.getView().focusRow(this.lastActive);
24125 if(last !== false){
24129 this.selectFirstRow();
24131 this.fireEvent("afterselectionchange", this);
24133 "down" : function(e){
24135 this.selectNext(e.shiftKey);
24136 }else if(this.last !== false && this.lastActive !== false){
24137 var last = this.last;
24138 this.selectRange(this.last, this.lastActive+1);
24139 this.grid.getView().focusRow(this.lastActive);
24140 if(last !== false){
24144 this.selectFirstRow();
24146 this.fireEvent("afterselectionchange", this);
24150 this.grid.store.on('load', function(){
24151 this.selections.clear();
24154 var view = this.grid.view;
24155 view.on("refresh", this.onRefresh, this);
24156 view.on("rowupdated", this.onRowUpdated, this);
24157 view.on("rowremoved", this.onRemove, this);
24162 onRefresh : function()
24164 var ds = this.grid.store, i, v = this.grid.view;
24165 var s = this.selections;
24166 s.each(function(r){
24167 if((i = ds.indexOfId(r.id)) != -1){
24176 onRemove : function(v, index, r){
24177 this.selections.remove(r);
24181 onRowUpdated : function(v, index, r){
24182 if(this.isSelected(r)){
24183 v.onRowSelect(index);
24189 * @param {Array} records The records to select
24190 * @param {Boolean} keepExisting (optional) True to keep existing selections
24192 selectRecords : function(records, keepExisting)
24195 this.clearSelections();
24197 var ds = this.grid.store;
24198 for(var i = 0, len = records.length; i < len; i++){
24199 this.selectRow(ds.indexOf(records[i]), true);
24204 * Gets the number of selected rows.
24207 getCount : function(){
24208 return this.selections.length;
24212 * Selects the first row in the grid.
24214 selectFirstRow : function(){
24219 * Select the last row.
24220 * @param {Boolean} keepExisting (optional) True to keep existing selections
24222 selectLastRow : function(keepExisting){
24223 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24224 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24228 * Selects the row immediately following the last selected row.
24229 * @param {Boolean} keepExisting (optional) True to keep existing selections
24231 selectNext : function(keepExisting)
24233 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24234 this.selectRow(this.last+1, keepExisting);
24235 this.grid.getView().focusRow(this.last);
24240 * Selects the row that precedes the last selected row.
24241 * @param {Boolean} keepExisting (optional) True to keep existing selections
24243 selectPrevious : function(keepExisting){
24245 this.selectRow(this.last-1, keepExisting);
24246 this.grid.getView().focusRow(this.last);
24251 * Returns the selected records
24252 * @return {Array} Array of selected records
24254 getSelections : function(){
24255 return [].concat(this.selections.items);
24259 * Returns the first selected record.
24262 getSelected : function(){
24263 return this.selections.itemAt(0);
24268 * Clears all selections.
24270 clearSelections : function(fast)
24276 var ds = this.grid.store;
24277 var s = this.selections;
24278 s.each(function(r){
24279 this.deselectRow(ds.indexOfId(r.id));
24283 this.selections.clear();
24290 * Selects all rows.
24292 selectAll : function(){
24296 this.selections.clear();
24297 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24298 this.selectRow(i, true);
24303 * Returns True if there is a selection.
24304 * @return {Boolean}
24306 hasSelection : function(){
24307 return this.selections.length > 0;
24311 * Returns True if the specified row is selected.
24312 * @param {Number/Record} record The record or index of the record to check
24313 * @return {Boolean}
24315 isSelected : function(index){
24316 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24317 return (r && this.selections.key(r.id) ? true : false);
24321 * Returns True if the specified record id is selected.
24322 * @param {String} id The id of record to check
24323 * @return {Boolean}
24325 isIdSelected : function(id){
24326 return (this.selections.key(id) ? true : false);
24331 handleMouseDBClick : function(e, t){
24335 handleMouseDown : function(e, t)
24337 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24338 if(this.isLocked() || rowIndex < 0 ){
24341 if(e.shiftKey && this.last !== false){
24342 var last = this.last;
24343 this.selectRange(last, rowIndex, e.ctrlKey);
24344 this.last = last; // reset the last
24348 var isSelected = this.isSelected(rowIndex);
24349 //Roo.log("select row:" + rowIndex);
24351 this.deselectRow(rowIndex);
24353 this.selectRow(rowIndex, true);
24357 if(e.button !== 0 && isSelected){
24358 alert('rowIndex 2: ' + rowIndex);
24359 view.focusRow(rowIndex);
24360 }else if(e.ctrlKey && isSelected){
24361 this.deselectRow(rowIndex);
24362 }else if(!isSelected){
24363 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24364 view.focusRow(rowIndex);
24368 this.fireEvent("afterselectionchange", this);
24371 handleDragableRowClick : function(grid, rowIndex, e)
24373 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24374 this.selectRow(rowIndex, false);
24375 grid.view.focusRow(rowIndex);
24376 this.fireEvent("afterselectionchange", this);
24381 * Selects multiple rows.
24382 * @param {Array} rows Array of the indexes of the row to select
24383 * @param {Boolean} keepExisting (optional) True to keep existing selections
24385 selectRows : function(rows, keepExisting){
24387 this.clearSelections();
24389 for(var i = 0, len = rows.length; i < len; i++){
24390 this.selectRow(rows[i], true);
24395 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24396 * @param {Number} startRow The index of the first row in the range
24397 * @param {Number} endRow The index of the last row in the range
24398 * @param {Boolean} keepExisting (optional) True to retain existing selections
24400 selectRange : function(startRow, endRow, keepExisting){
24405 this.clearSelections();
24407 if(startRow <= endRow){
24408 for(var i = startRow; i <= endRow; i++){
24409 this.selectRow(i, true);
24412 for(var i = startRow; i >= endRow; i--){
24413 this.selectRow(i, true);
24419 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24420 * @param {Number} startRow The index of the first row in the range
24421 * @param {Number} endRow The index of the last row in the range
24423 deselectRange : function(startRow, endRow, preventViewNotify){
24427 for(var i = startRow; i <= endRow; i++){
24428 this.deselectRow(i, preventViewNotify);
24434 * @param {Number} row The index of the row to select
24435 * @param {Boolean} keepExisting (optional) True to keep existing selections
24437 selectRow : function(index, keepExisting, preventViewNotify)
24439 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24442 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24443 if(!keepExisting || this.singleSelect){
24444 this.clearSelections();
24447 var r = this.grid.store.getAt(index);
24448 //console.log('selectRow - record id :' + r.id);
24450 this.selections.add(r);
24451 this.last = this.lastActive = index;
24452 if(!preventViewNotify){
24453 var proxy = new Roo.Element(
24454 this.grid.getRowDom(index)
24456 proxy.addClass('bg-info info');
24458 this.fireEvent("rowselect", this, index, r);
24459 this.fireEvent("selectionchange", this);
24465 * @param {Number} row The index of the row to deselect
24467 deselectRow : function(index, preventViewNotify)
24472 if(this.last == index){
24475 if(this.lastActive == index){
24476 this.lastActive = false;
24479 var r = this.grid.store.getAt(index);
24484 this.selections.remove(r);
24485 //.console.log('deselectRow - record id :' + r.id);
24486 if(!preventViewNotify){
24488 var proxy = new Roo.Element(
24489 this.grid.getRowDom(index)
24491 proxy.removeClass('bg-info info');
24493 this.fireEvent("rowdeselect", this, index);
24494 this.fireEvent("selectionchange", this);
24498 restoreLast : function(){
24500 this.last = this._last;
24505 acceptsNav : function(row, col, cm){
24506 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24510 onEditorKey : function(field, e){
24511 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24516 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24518 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24520 }else if(k == e.ENTER && !e.ctrlKey){
24524 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24526 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24528 }else if(k == e.ESC){
24532 g.startEditing(newCell[0], newCell[1]);
24538 * Ext JS Library 1.1.1
24539 * Copyright(c) 2006-2007, Ext JS, LLC.
24541 * Originally Released Under LGPL - original licence link has changed is not relivant.
24544 * <script type="text/javascript">
24548 * @class Roo.bootstrap.PagingToolbar
24549 * @extends Roo.bootstrap.NavSimplebar
24550 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24552 * Create a new PagingToolbar
24553 * @param {Object} config The config object
24554 * @param {Roo.data.Store} store
24556 Roo.bootstrap.PagingToolbar = function(config)
24558 // old args format still supported... - xtype is prefered..
24559 // created from xtype...
24561 this.ds = config.dataSource;
24563 if (config.store && !this.ds) {
24564 this.store= Roo.factory(config.store, Roo.data);
24565 this.ds = this.store;
24566 this.ds.xmodule = this.xmodule || false;
24569 this.toolbarItems = [];
24570 if (config.items) {
24571 this.toolbarItems = config.items;
24574 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24579 this.bind(this.ds);
24582 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24586 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24588 * @cfg {Roo.data.Store} dataSource
24589 * The underlying data store providing the paged data
24592 * @cfg {String/HTMLElement/Element} container
24593 * container The id or element that will contain the toolbar
24596 * @cfg {Boolean} displayInfo
24597 * True to display the displayMsg (defaults to false)
24600 * @cfg {Number} pageSize
24601 * The number of records to display per page (defaults to 20)
24605 * @cfg {String} displayMsg
24606 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24608 displayMsg : 'Displaying {0} - {1} of {2}',
24610 * @cfg {String} emptyMsg
24611 * The message to display when no records are found (defaults to "No data to display")
24613 emptyMsg : 'No data to display',
24615 * Customizable piece of the default paging text (defaults to "Page")
24618 beforePageText : "Page",
24620 * Customizable piece of the default paging text (defaults to "of %0")
24623 afterPageText : "of {0}",
24625 * Customizable piece of the default paging text (defaults to "First Page")
24628 firstText : "First Page",
24630 * Customizable piece of the default paging text (defaults to "Previous Page")
24633 prevText : "Previous Page",
24635 * Customizable piece of the default paging text (defaults to "Next Page")
24638 nextText : "Next Page",
24640 * Customizable piece of the default paging text (defaults to "Last Page")
24643 lastText : "Last Page",
24645 * Customizable piece of the default paging text (defaults to "Refresh")
24648 refreshText : "Refresh",
24652 onRender : function(ct, position)
24654 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24655 this.navgroup.parentId = this.id;
24656 this.navgroup.onRender(this.el, null);
24657 // add the buttons to the navgroup
24659 if(this.displayInfo){
24660 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24661 this.displayEl = this.el.select('.x-paging-info', true).first();
24662 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24663 // this.displayEl = navel.el.select('span',true).first();
24669 Roo.each(_this.buttons, function(e){ // this might need to use render????
24670 Roo.factory(e).render(_this.el);
24674 Roo.each(_this.toolbarItems, function(e) {
24675 _this.navgroup.addItem(e);
24679 this.first = this.navgroup.addItem({
24680 tooltip: this.firstText,
24682 icon : 'fa fa-backward',
24684 preventDefault: true,
24685 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24688 this.prev = this.navgroup.addItem({
24689 tooltip: this.prevText,
24691 icon : 'fa fa-step-backward',
24693 preventDefault: true,
24694 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24696 //this.addSeparator();
24699 var field = this.navgroup.addItem( {
24701 cls : 'x-paging-position',
24703 html : this.beforePageText +
24704 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24705 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24708 this.field = field.el.select('input', true).first();
24709 this.field.on("keydown", this.onPagingKeydown, this);
24710 this.field.on("focus", function(){this.dom.select();});
24713 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24714 //this.field.setHeight(18);
24715 //this.addSeparator();
24716 this.next = this.navgroup.addItem({
24717 tooltip: this.nextText,
24719 html : ' <i class="fa fa-step-forward">',
24721 preventDefault: true,
24722 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24724 this.last = this.navgroup.addItem({
24725 tooltip: this.lastText,
24726 icon : 'fa fa-forward',
24729 preventDefault: true,
24730 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24732 //this.addSeparator();
24733 this.loading = this.navgroup.addItem({
24734 tooltip: this.refreshText,
24735 icon: 'fa fa-refresh',
24736 preventDefault: true,
24737 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24743 updateInfo : function(){
24744 if(this.displayEl){
24745 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24746 var msg = count == 0 ?
24750 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24752 this.displayEl.update(msg);
24757 onLoad : function(ds, r, o)
24759 this.cursor = o.params.start ? o.params.start : 0;
24761 var d = this.getPageData(),
24766 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24767 this.field.dom.value = ap;
24768 this.first.setDisabled(ap == 1);
24769 this.prev.setDisabled(ap == 1);
24770 this.next.setDisabled(ap == ps);
24771 this.last.setDisabled(ap == ps);
24772 this.loading.enable();
24777 getPageData : function(){
24778 var total = this.ds.getTotalCount();
24781 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24782 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24787 onLoadError : function(){
24788 this.loading.enable();
24792 onPagingKeydown : function(e){
24793 var k = e.getKey();
24794 var d = this.getPageData();
24796 var v = this.field.dom.value, pageNum;
24797 if(!v || isNaN(pageNum = parseInt(v, 10))){
24798 this.field.dom.value = d.activePage;
24801 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24802 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24805 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))
24807 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24808 this.field.dom.value = pageNum;
24809 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24812 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24814 var v = this.field.dom.value, pageNum;
24815 var increment = (e.shiftKey) ? 10 : 1;
24816 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24819 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24820 this.field.dom.value = d.activePage;
24823 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24825 this.field.dom.value = parseInt(v, 10) + increment;
24826 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24827 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24834 beforeLoad : function(){
24836 this.loading.disable();
24841 onClick : function(which){
24850 ds.load({params:{start: 0, limit: this.pageSize}});
24853 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24856 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24859 var total = ds.getTotalCount();
24860 var extra = total % this.pageSize;
24861 var lastStart = extra ? (total - extra) : total-this.pageSize;
24862 ds.load({params:{start: lastStart, limit: this.pageSize}});
24865 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24871 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24872 * @param {Roo.data.Store} store The data store to unbind
24874 unbind : function(ds){
24875 ds.un("beforeload", this.beforeLoad, this);
24876 ds.un("load", this.onLoad, this);
24877 ds.un("loadexception", this.onLoadError, this);
24878 ds.un("remove", this.updateInfo, this);
24879 ds.un("add", this.updateInfo, this);
24880 this.ds = undefined;
24884 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24885 * @param {Roo.data.Store} store The data store to bind
24887 bind : function(ds){
24888 ds.on("beforeload", this.beforeLoad, this);
24889 ds.on("load", this.onLoad, this);
24890 ds.on("loadexception", this.onLoadError, this);
24891 ds.on("remove", this.updateInfo, this);
24892 ds.on("add", this.updateInfo, this);
24903 * @class Roo.bootstrap.MessageBar
24904 * @extends Roo.bootstrap.Component
24905 * Bootstrap MessageBar class
24906 * @cfg {String} html contents of the MessageBar
24907 * @cfg {String} weight (info | success | warning | danger) default info
24908 * @cfg {String} beforeClass insert the bar before the given class
24909 * @cfg {Boolean} closable (true | false) default false
24910 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24913 * Create a new Element
24914 * @param {Object} config The config object
24917 Roo.bootstrap.MessageBar = function(config){
24918 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24921 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24927 beforeClass: 'bootstrap-sticky-wrap',
24929 getAutoCreate : function(){
24933 cls: 'alert alert-dismissable alert-' + this.weight,
24938 html: this.html || ''
24944 cfg.cls += ' alert-messages-fixed';
24958 onRender : function(ct, position)
24960 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24963 var cfg = Roo.apply({}, this.getAutoCreate());
24967 cfg.cls += ' ' + this.cls;
24970 cfg.style = this.style;
24972 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24974 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24977 this.el.select('>button.close').on('click', this.hide, this);
24983 if (!this.rendered) {
24989 this.fireEvent('show', this);
24995 if (!this.rendered) {
25001 this.fireEvent('hide', this);
25004 update : function()
25006 // var e = this.el.dom.firstChild;
25008 // if(this.closable){
25009 // e = e.nextSibling;
25012 // e.data = this.html || '';
25014 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25030 * @class Roo.bootstrap.Graph
25031 * @extends Roo.bootstrap.Component
25032 * Bootstrap Graph class
25036 @cfg {String} graphtype bar | vbar | pie
25037 @cfg {number} g_x coodinator | centre x (pie)
25038 @cfg {number} g_y coodinator | centre y (pie)
25039 @cfg {number} g_r radius (pie)
25040 @cfg {number} g_height height of the chart (respected by all elements in the set)
25041 @cfg {number} g_width width of the chart (respected by all elements in the set)
25042 @cfg {Object} title The title of the chart
25045 -opts (object) options for the chart
25047 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25048 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25050 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.
25051 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25053 o stretch (boolean)
25055 -opts (object) options for the pie
25058 o startAngle (number)
25059 o endAngle (number)
25063 * Create a new Input
25064 * @param {Object} config The config object
25067 Roo.bootstrap.Graph = function(config){
25068 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25074 * The img click event for the img.
25075 * @param {Roo.EventObject} e
25081 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25092 //g_colors: this.colors,
25099 getAutoCreate : function(){
25110 onRender : function(ct,position){
25113 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25115 if (typeof(Raphael) == 'undefined') {
25116 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25120 this.raphael = Raphael(this.el.dom);
25122 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25123 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25124 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25125 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25127 r.text(160, 10, "Single Series Chart").attr(txtattr);
25128 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25129 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25130 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25132 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25133 r.barchart(330, 10, 300, 220, data1);
25134 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25135 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25138 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25139 // r.barchart(30, 30, 560, 250, xdata, {
25140 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25141 // axis : "0 0 1 1",
25142 // axisxlabels : xdata
25143 // //yvalues : cols,
25146 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25148 // this.load(null,xdata,{
25149 // axis : "0 0 1 1",
25150 // axisxlabels : xdata
25155 load : function(graphtype,xdata,opts)
25157 this.raphael.clear();
25159 graphtype = this.graphtype;
25164 var r = this.raphael,
25165 fin = function () {
25166 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25168 fout = function () {
25169 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25171 pfin = function() {
25172 this.sector.stop();
25173 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25176 this.label[0].stop();
25177 this.label[0].attr({ r: 7.5 });
25178 this.label[1].attr({ "font-weight": 800 });
25181 pfout = function() {
25182 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25185 this.label[0].animate({ r: 5 }, 500, "bounce");
25186 this.label[1].attr({ "font-weight": 400 });
25192 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25195 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25198 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25199 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25201 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25208 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25213 setTitle: function(o)
25218 initEvents: function() {
25221 this.el.on('click', this.onClick, this);
25225 onClick : function(e)
25227 Roo.log('img onclick');
25228 this.fireEvent('click', this, e);
25240 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25243 * @class Roo.bootstrap.dash.NumberBox
25244 * @extends Roo.bootstrap.Component
25245 * Bootstrap NumberBox class
25246 * @cfg {String} headline Box headline
25247 * @cfg {String} content Box content
25248 * @cfg {String} icon Box icon
25249 * @cfg {String} footer Footer text
25250 * @cfg {String} fhref Footer href
25253 * Create a new NumberBox
25254 * @param {Object} config The config object
25258 Roo.bootstrap.dash.NumberBox = function(config){
25259 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25263 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25272 getAutoCreate : function(){
25276 cls : 'small-box ',
25284 cls : 'roo-headline',
25285 html : this.headline
25289 cls : 'roo-content',
25290 html : this.content
25304 cls : 'ion ' + this.icon
25313 cls : 'small-box-footer',
25314 href : this.fhref || '#',
25318 cfg.cn.push(footer);
25325 onRender : function(ct,position){
25326 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25333 setHeadline: function (value)
25335 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25338 setFooter: function (value, href)
25340 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25343 this.el.select('a.small-box-footer',true).first().attr('href', href);
25348 setContent: function (value)
25350 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25353 initEvents: function()
25367 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25370 * @class Roo.bootstrap.dash.TabBox
25371 * @extends Roo.bootstrap.Component
25372 * Bootstrap TabBox class
25373 * @cfg {String} title Title of the TabBox
25374 * @cfg {String} icon Icon of the TabBox
25375 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25376 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25379 * Create a new TabBox
25380 * @param {Object} config The config object
25384 Roo.bootstrap.dash.TabBox = function(config){
25385 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25390 * When a pane is added
25391 * @param {Roo.bootstrap.dash.TabPane} pane
25395 * @event activatepane
25396 * When a pane is activated
25397 * @param {Roo.bootstrap.dash.TabPane} pane
25399 "activatepane" : true
25407 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25412 tabScrollable : false,
25414 getChildContainer : function()
25416 return this.el.select('.tab-content', true).first();
25419 getAutoCreate : function(){
25423 cls: 'pull-left header',
25431 cls: 'fa ' + this.icon
25437 cls: 'nav nav-tabs pull-right',
25443 if(this.tabScrollable){
25450 cls: 'nav nav-tabs pull-right',
25461 cls: 'nav-tabs-custom',
25466 cls: 'tab-content no-padding',
25474 initEvents : function()
25476 //Roo.log('add add pane handler');
25477 this.on('addpane', this.onAddPane, this);
25480 * Updates the box title
25481 * @param {String} html to set the title to.
25483 setTitle : function(value)
25485 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25487 onAddPane : function(pane)
25489 this.panes.push(pane);
25490 //Roo.log('addpane');
25492 // tabs are rendere left to right..
25493 if(!this.showtabs){
25497 var ctr = this.el.select('.nav-tabs', true).first();
25500 var existing = ctr.select('.nav-tab',true);
25501 var qty = existing.getCount();;
25504 var tab = ctr.createChild({
25506 cls : 'nav-tab' + (qty ? '' : ' active'),
25514 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25517 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25519 pane.el.addClass('active');
25524 onTabClick : function(ev,un,ob,pane)
25526 //Roo.log('tab - prev default');
25527 ev.preventDefault();
25530 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25531 pane.tab.addClass('active');
25532 //Roo.log(pane.title);
25533 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25534 // technically we should have a deactivate event.. but maybe add later.
25535 // and it should not de-activate the selected tab...
25536 this.fireEvent('activatepane', pane);
25537 pane.el.addClass('active');
25538 pane.fireEvent('activate');
25543 getActivePane : function()
25546 Roo.each(this.panes, function(p) {
25547 if(p.el.hasClass('active')){
25568 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25570 * @class Roo.bootstrap.TabPane
25571 * @extends Roo.bootstrap.Component
25572 * Bootstrap TabPane class
25573 * @cfg {Boolean} active (false | true) Default false
25574 * @cfg {String} title title of panel
25578 * Create a new TabPane
25579 * @param {Object} config The config object
25582 Roo.bootstrap.dash.TabPane = function(config){
25583 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25589 * When a pane is activated
25590 * @param {Roo.bootstrap.dash.TabPane} pane
25597 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25602 // the tabBox that this is attached to.
25605 getAutoCreate : function()
25613 cfg.cls += ' active';
25618 initEvents : function()
25620 //Roo.log('trigger add pane handler');
25621 this.parent().fireEvent('addpane', this)
25625 * Updates the tab title
25626 * @param {String} html to set the title to.
25628 setTitle: function(str)
25634 this.tab.select('a', true).first().dom.innerHTML = str;
25651 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25654 * @class Roo.bootstrap.menu.Menu
25655 * @extends Roo.bootstrap.Component
25656 * Bootstrap Menu class - container for Menu
25657 * @cfg {String} html Text of the menu
25658 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25659 * @cfg {String} icon Font awesome icon
25660 * @cfg {String} pos Menu align to (top | bottom) default bottom
25664 * Create a new Menu
25665 * @param {Object} config The config object
25669 Roo.bootstrap.menu.Menu = function(config){
25670 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25674 * @event beforeshow
25675 * Fires before this menu is displayed
25676 * @param {Roo.bootstrap.menu.Menu} this
25680 * @event beforehide
25681 * Fires before this menu is hidden
25682 * @param {Roo.bootstrap.menu.Menu} this
25687 * Fires after this menu is displayed
25688 * @param {Roo.bootstrap.menu.Menu} this
25693 * Fires after this menu is hidden
25694 * @param {Roo.bootstrap.menu.Menu} this
25699 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25700 * @param {Roo.bootstrap.menu.Menu} this
25701 * @param {Roo.EventObject} e
25708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25712 weight : 'default',
25717 getChildContainer : function() {
25718 if(this.isSubMenu){
25722 return this.el.select('ul.dropdown-menu', true).first();
25725 getAutoCreate : function()
25730 cls : 'roo-menu-text',
25738 cls : 'fa ' + this.icon
25749 cls : 'dropdown-button btn btn-' + this.weight,
25754 cls : 'dropdown-toggle btn btn-' + this.weight,
25764 cls : 'dropdown-menu'
25770 if(this.pos == 'top'){
25771 cfg.cls += ' dropup';
25774 if(this.isSubMenu){
25777 cls : 'dropdown-menu'
25784 onRender : function(ct, position)
25786 this.isSubMenu = ct.hasClass('dropdown-submenu');
25788 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25791 initEvents : function()
25793 if(this.isSubMenu){
25797 this.hidden = true;
25799 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25800 this.triggerEl.on('click', this.onTriggerPress, this);
25802 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25803 this.buttonEl.on('click', this.onClick, this);
25809 if(this.isSubMenu){
25813 return this.el.select('ul.dropdown-menu', true).first();
25816 onClick : function(e)
25818 this.fireEvent("click", this, e);
25821 onTriggerPress : function(e)
25823 if (this.isVisible()) {
25830 isVisible : function(){
25831 return !this.hidden;
25836 this.fireEvent("beforeshow", this);
25838 this.hidden = false;
25839 this.el.addClass('open');
25841 Roo.get(document).on("mouseup", this.onMouseUp, this);
25843 this.fireEvent("show", this);
25850 this.fireEvent("beforehide", this);
25852 this.hidden = true;
25853 this.el.removeClass('open');
25855 Roo.get(document).un("mouseup", this.onMouseUp);
25857 this.fireEvent("hide", this);
25860 onMouseUp : function()
25874 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25877 * @class Roo.bootstrap.menu.Item
25878 * @extends Roo.bootstrap.Component
25879 * Bootstrap MenuItem class
25880 * @cfg {Boolean} submenu (true | false) default false
25881 * @cfg {String} html text of the item
25882 * @cfg {String} href the link
25883 * @cfg {Boolean} disable (true | false) default false
25884 * @cfg {Boolean} preventDefault (true | false) default true
25885 * @cfg {String} icon Font awesome icon
25886 * @cfg {String} pos Submenu align to (left | right) default right
25890 * Create a new Item
25891 * @param {Object} config The config object
25895 Roo.bootstrap.menu.Item = function(config){
25896 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25900 * Fires when the mouse is hovering over this menu
25901 * @param {Roo.bootstrap.menu.Item} this
25902 * @param {Roo.EventObject} e
25907 * Fires when the mouse exits this menu
25908 * @param {Roo.bootstrap.menu.Item} this
25909 * @param {Roo.EventObject} e
25915 * The raw click event for the entire grid.
25916 * @param {Roo.EventObject} e
25922 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25927 preventDefault: true,
25932 getAutoCreate : function()
25937 cls : 'roo-menu-item-text',
25945 cls : 'fa ' + this.icon
25954 href : this.href || '#',
25961 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25965 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25967 if(this.pos == 'left'){
25968 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25975 initEvents : function()
25977 this.el.on('mouseover', this.onMouseOver, this);
25978 this.el.on('mouseout', this.onMouseOut, this);
25980 this.el.select('a', true).first().on('click', this.onClick, this);
25984 onClick : function(e)
25986 if(this.preventDefault){
25987 e.preventDefault();
25990 this.fireEvent("click", this, e);
25993 onMouseOver : function(e)
25995 if(this.submenu && this.pos == 'left'){
25996 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25999 this.fireEvent("mouseover", this, e);
26002 onMouseOut : function(e)
26004 this.fireEvent("mouseout", this, e);
26016 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26019 * @class Roo.bootstrap.menu.Separator
26020 * @extends Roo.bootstrap.Component
26021 * Bootstrap Separator class
26024 * Create a new Separator
26025 * @param {Object} config The config object
26029 Roo.bootstrap.menu.Separator = function(config){
26030 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26033 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26035 getAutoCreate : function(){
26056 * @class Roo.bootstrap.Tooltip
26057 * Bootstrap Tooltip class
26058 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26059 * to determine which dom element triggers the tooltip.
26061 * It needs to add support for additional attributes like tooltip-position
26064 * Create a new Toolti
26065 * @param {Object} config The config object
26068 Roo.bootstrap.Tooltip = function(config){
26069 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26071 this.alignment = Roo.bootstrap.Tooltip.alignment;
26073 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26074 this.alignment = config.alignment;
26079 Roo.apply(Roo.bootstrap.Tooltip, {
26081 * @function init initialize tooltip monitoring.
26085 currentTip : false,
26086 currentRegion : false,
26092 Roo.get(document).on('mouseover', this.enter ,this);
26093 Roo.get(document).on('mouseout', this.leave, this);
26096 this.currentTip = new Roo.bootstrap.Tooltip();
26099 enter : function(ev)
26101 var dom = ev.getTarget();
26103 //Roo.log(['enter',dom]);
26104 var el = Roo.fly(dom);
26105 if (this.currentEl) {
26107 //Roo.log(this.currentEl);
26108 //Roo.log(this.currentEl.contains(dom));
26109 if (this.currentEl == el) {
26112 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26118 if (this.currentTip.el) {
26119 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26123 if(!el || el.dom == document){
26129 // you can not look for children, as if el is the body.. then everythign is the child..
26130 if (!el.attr('tooltip')) { //
26131 if (!el.select("[tooltip]").elements.length) {
26134 // is the mouse over this child...?
26135 bindEl = el.select("[tooltip]").first();
26136 var xy = ev.getXY();
26137 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26138 //Roo.log("not in region.");
26141 //Roo.log("child element over..");
26144 this.currentEl = bindEl;
26145 this.currentTip.bind(bindEl);
26146 this.currentRegion = Roo.lib.Region.getRegion(dom);
26147 this.currentTip.enter();
26150 leave : function(ev)
26152 var dom = ev.getTarget();
26153 //Roo.log(['leave',dom]);
26154 if (!this.currentEl) {
26159 if (dom != this.currentEl.dom) {
26162 var xy = ev.getXY();
26163 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26166 // only activate leave if mouse cursor is outside... bounding box..
26171 if (this.currentTip) {
26172 this.currentTip.leave();
26174 //Roo.log('clear currentEl');
26175 this.currentEl = false;
26180 'left' : ['r-l', [-2,0], 'right'],
26181 'right' : ['l-r', [2,0], 'left'],
26182 'bottom' : ['t-b', [0,2], 'top'],
26183 'top' : [ 'b-t', [0,-2], 'bottom']
26189 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26194 delay : null, // can be { show : 300 , hide: 500}
26198 hoverState : null, //???
26200 placement : 'bottom',
26204 getAutoCreate : function(){
26211 cls : 'tooltip-arrow'
26214 cls : 'tooltip-inner'
26221 bind : function(el)
26227 enter : function () {
26229 if (this.timeout != null) {
26230 clearTimeout(this.timeout);
26233 this.hoverState = 'in';
26234 //Roo.log("enter - show");
26235 if (!this.delay || !this.delay.show) {
26240 this.timeout = setTimeout(function () {
26241 if (_t.hoverState == 'in') {
26244 }, this.delay.show);
26248 clearTimeout(this.timeout);
26250 this.hoverState = 'out';
26251 if (!this.delay || !this.delay.hide) {
26257 this.timeout = setTimeout(function () {
26258 //Roo.log("leave - timeout");
26260 if (_t.hoverState == 'out') {
26262 Roo.bootstrap.Tooltip.currentEl = false;
26267 show : function (msg)
26270 this.render(document.body);
26273 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26275 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26277 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26279 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26281 var placement = typeof this.placement == 'function' ?
26282 this.placement.call(this, this.el, on_el) :
26285 var autoToken = /\s?auto?\s?/i;
26286 var autoPlace = autoToken.test(placement);
26288 placement = placement.replace(autoToken, '') || 'top';
26292 //this.el.setXY([0,0]);
26294 //this.el.dom.style.display='block';
26296 //this.el.appendTo(on_el);
26298 var p = this.getPosition();
26299 var box = this.el.getBox();
26305 var align = this.alignment[placement];
26307 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26309 if(placement == 'top' || placement == 'bottom'){
26311 placement = 'right';
26314 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26315 placement = 'left';
26318 var scroll = Roo.select('body', true).first().getScroll();
26320 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26324 align = this.alignment[placement];
26327 this.el.alignTo(this.bindEl, align[0],align[1]);
26328 //var arrow = this.el.select('.arrow',true).first();
26329 //arrow.set(align[2],
26331 this.el.addClass(placement);
26333 this.el.addClass('in fade');
26335 this.hoverState = null;
26337 if (this.el.hasClass('fade')) {
26348 //this.el.setXY([0,0]);
26349 this.el.removeClass('in');
26365 * @class Roo.bootstrap.LocationPicker
26366 * @extends Roo.bootstrap.Component
26367 * Bootstrap LocationPicker class
26368 * @cfg {Number} latitude Position when init default 0
26369 * @cfg {Number} longitude Position when init default 0
26370 * @cfg {Number} zoom default 15
26371 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26372 * @cfg {Boolean} mapTypeControl default false
26373 * @cfg {Boolean} disableDoubleClickZoom default false
26374 * @cfg {Boolean} scrollwheel default true
26375 * @cfg {Boolean} streetViewControl default false
26376 * @cfg {Number} radius default 0
26377 * @cfg {String} locationName
26378 * @cfg {Boolean} draggable default true
26379 * @cfg {Boolean} enableAutocomplete default false
26380 * @cfg {Boolean} enableReverseGeocode default true
26381 * @cfg {String} markerTitle
26384 * Create a new LocationPicker
26385 * @param {Object} config The config object
26389 Roo.bootstrap.LocationPicker = function(config){
26391 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26396 * Fires when the picker initialized.
26397 * @param {Roo.bootstrap.LocationPicker} this
26398 * @param {Google Location} location
26402 * @event positionchanged
26403 * Fires when the picker position changed.
26404 * @param {Roo.bootstrap.LocationPicker} this
26405 * @param {Google Location} location
26407 positionchanged : true,
26410 * Fires when the map resize.
26411 * @param {Roo.bootstrap.LocationPicker} this
26416 * Fires when the map show.
26417 * @param {Roo.bootstrap.LocationPicker} this
26422 * Fires when the map hide.
26423 * @param {Roo.bootstrap.LocationPicker} this
26428 * Fires when click the map.
26429 * @param {Roo.bootstrap.LocationPicker} this
26430 * @param {Map event} e
26434 * @event mapRightClick
26435 * Fires when right click the map.
26436 * @param {Roo.bootstrap.LocationPicker} this
26437 * @param {Map event} e
26439 mapRightClick : true,
26441 * @event markerClick
26442 * Fires when click the marker.
26443 * @param {Roo.bootstrap.LocationPicker} this
26444 * @param {Map event} e
26446 markerClick : true,
26448 * @event markerRightClick
26449 * Fires when right click the marker.
26450 * @param {Roo.bootstrap.LocationPicker} this
26451 * @param {Map event} e
26453 markerRightClick : true,
26455 * @event OverlayViewDraw
26456 * Fires when OverlayView Draw
26457 * @param {Roo.bootstrap.LocationPicker} this
26459 OverlayViewDraw : true,
26461 * @event OverlayViewOnAdd
26462 * Fires when OverlayView Draw
26463 * @param {Roo.bootstrap.LocationPicker} this
26465 OverlayViewOnAdd : true,
26467 * @event OverlayViewOnRemove
26468 * Fires when OverlayView Draw
26469 * @param {Roo.bootstrap.LocationPicker} this
26471 OverlayViewOnRemove : true,
26473 * @event OverlayViewShow
26474 * Fires when OverlayView Draw
26475 * @param {Roo.bootstrap.LocationPicker} this
26476 * @param {Pixel} cpx
26478 OverlayViewShow : true,
26480 * @event OverlayViewHide
26481 * Fires when OverlayView Draw
26482 * @param {Roo.bootstrap.LocationPicker} this
26484 OverlayViewHide : true,
26486 * @event loadexception
26487 * Fires when load google lib failed.
26488 * @param {Roo.bootstrap.LocationPicker} this
26490 loadexception : true
26495 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26497 gMapContext: false,
26503 mapTypeControl: false,
26504 disableDoubleClickZoom: false,
26506 streetViewControl: false,
26510 enableAutocomplete: false,
26511 enableReverseGeocode: true,
26514 getAutoCreate: function()
26519 cls: 'roo-location-picker'
26525 initEvents: function(ct, position)
26527 if(!this.el.getWidth() || this.isApplied()){
26531 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26536 initial: function()
26538 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26539 this.fireEvent('loadexception', this);
26543 if(!this.mapTypeId){
26544 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26547 this.gMapContext = this.GMapContext();
26549 this.initOverlayView();
26551 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26555 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26556 _this.setPosition(_this.gMapContext.marker.position);
26559 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26560 _this.fireEvent('mapClick', this, event);
26564 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26565 _this.fireEvent('mapRightClick', this, event);
26569 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26570 _this.fireEvent('markerClick', this, event);
26574 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26575 _this.fireEvent('markerRightClick', this, event);
26579 this.setPosition(this.gMapContext.location);
26581 this.fireEvent('initial', this, this.gMapContext.location);
26584 initOverlayView: function()
26588 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26592 _this.fireEvent('OverlayViewDraw', _this);
26597 _this.fireEvent('OverlayViewOnAdd', _this);
26600 onRemove: function()
26602 _this.fireEvent('OverlayViewOnRemove', _this);
26605 show: function(cpx)
26607 _this.fireEvent('OverlayViewShow', _this, cpx);
26612 _this.fireEvent('OverlayViewHide', _this);
26618 fromLatLngToContainerPixel: function(event)
26620 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26623 isApplied: function()
26625 return this.getGmapContext() == false ? false : true;
26628 getGmapContext: function()
26630 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26633 GMapContext: function()
26635 var position = new google.maps.LatLng(this.latitude, this.longitude);
26637 var _map = new google.maps.Map(this.el.dom, {
26640 mapTypeId: this.mapTypeId,
26641 mapTypeControl: this.mapTypeControl,
26642 disableDoubleClickZoom: this.disableDoubleClickZoom,
26643 scrollwheel: this.scrollwheel,
26644 streetViewControl: this.streetViewControl,
26645 locationName: this.locationName,
26646 draggable: this.draggable,
26647 enableAutocomplete: this.enableAutocomplete,
26648 enableReverseGeocode: this.enableReverseGeocode
26651 var _marker = new google.maps.Marker({
26652 position: position,
26654 title: this.markerTitle,
26655 draggable: this.draggable
26662 location: position,
26663 radius: this.radius,
26664 locationName: this.locationName,
26665 addressComponents: {
26666 formatted_address: null,
26667 addressLine1: null,
26668 addressLine2: null,
26670 streetNumber: null,
26674 stateOrProvince: null
26677 domContainer: this.el.dom,
26678 geodecoder: new google.maps.Geocoder()
26682 drawCircle: function(center, radius, options)
26684 if (this.gMapContext.circle != null) {
26685 this.gMapContext.circle.setMap(null);
26689 options = Roo.apply({}, options, {
26690 strokeColor: "#0000FF",
26691 strokeOpacity: .35,
26693 fillColor: "#0000FF",
26697 options.map = this.gMapContext.map;
26698 options.radius = radius;
26699 options.center = center;
26700 this.gMapContext.circle = new google.maps.Circle(options);
26701 return this.gMapContext.circle;
26707 setPosition: function(location)
26709 this.gMapContext.location = location;
26710 this.gMapContext.marker.setPosition(location);
26711 this.gMapContext.map.panTo(location);
26712 this.drawCircle(location, this.gMapContext.radius, {});
26716 if (this.gMapContext.settings.enableReverseGeocode) {
26717 this.gMapContext.geodecoder.geocode({
26718 latLng: this.gMapContext.location
26719 }, function(results, status) {
26721 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26722 _this.gMapContext.locationName = results[0].formatted_address;
26723 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26725 _this.fireEvent('positionchanged', this, location);
26732 this.fireEvent('positionchanged', this, location);
26737 google.maps.event.trigger(this.gMapContext.map, "resize");
26739 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26741 this.fireEvent('resize', this);
26744 setPositionByLatLng: function(latitude, longitude)
26746 this.setPosition(new google.maps.LatLng(latitude, longitude));
26749 getCurrentPosition: function()
26752 latitude: this.gMapContext.location.lat(),
26753 longitude: this.gMapContext.location.lng()
26757 getAddressName: function()
26759 return this.gMapContext.locationName;
26762 getAddressComponents: function()
26764 return this.gMapContext.addressComponents;
26767 address_component_from_google_geocode: function(address_components)
26771 for (var i = 0; i < address_components.length; i++) {
26772 var component = address_components[i];
26773 if (component.types.indexOf("postal_code") >= 0) {
26774 result.postalCode = component.short_name;
26775 } else if (component.types.indexOf("street_number") >= 0) {
26776 result.streetNumber = component.short_name;
26777 } else if (component.types.indexOf("route") >= 0) {
26778 result.streetName = component.short_name;
26779 } else if (component.types.indexOf("neighborhood") >= 0) {
26780 result.city = component.short_name;
26781 } else if (component.types.indexOf("locality") >= 0) {
26782 result.city = component.short_name;
26783 } else if (component.types.indexOf("sublocality") >= 0) {
26784 result.district = component.short_name;
26785 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26786 result.stateOrProvince = component.short_name;
26787 } else if (component.types.indexOf("country") >= 0) {
26788 result.country = component.short_name;
26792 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26793 result.addressLine2 = "";
26797 setZoomLevel: function(zoom)
26799 this.gMapContext.map.setZoom(zoom);
26812 this.fireEvent('show', this);
26823 this.fireEvent('hide', this);
26828 Roo.apply(Roo.bootstrap.LocationPicker, {
26830 OverlayView : function(map, options)
26832 options = options || {};
26846 * @class Roo.bootstrap.Alert
26847 * @extends Roo.bootstrap.Component
26848 * Bootstrap Alert class
26849 * @cfg {String} title The title of alert
26850 * @cfg {String} html The content of alert
26851 * @cfg {String} weight ( success | info | warning | danger )
26852 * @cfg {String} faicon font-awesomeicon
26855 * Create a new alert
26856 * @param {Object} config The config object
26860 Roo.bootstrap.Alert = function(config){
26861 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26865 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26872 getAutoCreate : function()
26881 cls : 'roo-alert-icon'
26886 cls : 'roo-alert-title',
26891 cls : 'roo-alert-text',
26898 cfg.cn[0].cls += ' fa ' + this.faicon;
26902 cfg.cls += ' alert-' + this.weight;
26908 initEvents: function()
26910 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26913 setTitle : function(str)
26915 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26918 setText : function(str)
26920 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26923 setWeight : function(weight)
26926 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26929 this.weight = weight;
26931 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26934 setIcon : function(icon)
26937 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26940 this.faicon = icon;
26942 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26963 * @class Roo.bootstrap.UploadCropbox
26964 * @extends Roo.bootstrap.Component
26965 * Bootstrap UploadCropbox class
26966 * @cfg {String} emptyText show when image has been loaded
26967 * @cfg {String} rotateNotify show when image too small to rotate
26968 * @cfg {Number} errorTimeout default 3000
26969 * @cfg {Number} minWidth default 300
26970 * @cfg {Number} minHeight default 300
26971 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26972 * @cfg {Boolean} isDocument (true|false) default false
26973 * @cfg {String} url action url
26974 * @cfg {String} paramName default 'imageUpload'
26975 * @cfg {String} method default POST
26976 * @cfg {Boolean} loadMask (true|false) default true
26977 * @cfg {Boolean} loadingText default 'Loading...'
26980 * Create a new UploadCropbox
26981 * @param {Object} config The config object
26984 Roo.bootstrap.UploadCropbox = function(config){
26985 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26989 * @event beforeselectfile
26990 * Fire before select file
26991 * @param {Roo.bootstrap.UploadCropbox} this
26993 "beforeselectfile" : true,
26996 * Fire after initEvent
26997 * @param {Roo.bootstrap.UploadCropbox} this
27002 * Fire after initEvent
27003 * @param {Roo.bootstrap.UploadCropbox} this
27004 * @param {String} data
27009 * Fire when preparing the file data
27010 * @param {Roo.bootstrap.UploadCropbox} this
27011 * @param {Object} file
27016 * Fire when get exception
27017 * @param {Roo.bootstrap.UploadCropbox} this
27018 * @param {XMLHttpRequest} xhr
27020 "exception" : true,
27022 * @event beforeloadcanvas
27023 * Fire before load the canvas
27024 * @param {Roo.bootstrap.UploadCropbox} this
27025 * @param {String} src
27027 "beforeloadcanvas" : true,
27030 * Fire when trash image
27031 * @param {Roo.bootstrap.UploadCropbox} this
27036 * Fire when download the image
27037 * @param {Roo.bootstrap.UploadCropbox} this
27041 * @event footerbuttonclick
27042 * Fire when footerbuttonclick
27043 * @param {Roo.bootstrap.UploadCropbox} this
27044 * @param {String} type
27046 "footerbuttonclick" : true,
27050 * @param {Roo.bootstrap.UploadCropbox} this
27055 * Fire when rotate the image
27056 * @param {Roo.bootstrap.UploadCropbox} this
27057 * @param {String} pos
27062 * Fire when inspect the file
27063 * @param {Roo.bootstrap.UploadCropbox} this
27064 * @param {Object} file
27069 * Fire when xhr upload the file
27070 * @param {Roo.bootstrap.UploadCropbox} this
27071 * @param {Object} data
27076 * Fire when arrange the file data
27077 * @param {Roo.bootstrap.UploadCropbox} this
27078 * @param {Object} formData
27083 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27086 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27088 emptyText : 'Click to upload image',
27089 rotateNotify : 'Image is too small to rotate',
27090 errorTimeout : 3000,
27104 cropType : 'image/jpeg',
27106 canvasLoaded : false,
27107 isDocument : false,
27109 paramName : 'imageUpload',
27111 loadingText : 'Loading...',
27114 getAutoCreate : function()
27118 cls : 'roo-upload-cropbox',
27122 cls : 'roo-upload-cropbox-selector',
27127 cls : 'roo-upload-cropbox-body',
27128 style : 'cursor:pointer',
27132 cls : 'roo-upload-cropbox-preview'
27136 cls : 'roo-upload-cropbox-thumb'
27140 cls : 'roo-upload-cropbox-empty-notify',
27141 html : this.emptyText
27145 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27146 html : this.rotateNotify
27152 cls : 'roo-upload-cropbox-footer',
27155 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27165 onRender : function(ct, position)
27167 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27169 if (this.buttons.length) {
27171 Roo.each(this.buttons, function(bb) {
27173 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27175 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27181 this.maskEl = this.el;
27185 initEvents : function()
27187 this.urlAPI = (window.createObjectURL && window) ||
27188 (window.URL && URL.revokeObjectURL && URL) ||
27189 (window.webkitURL && webkitURL);
27191 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27192 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27194 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27195 this.selectorEl.hide();
27197 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27198 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27200 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27201 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27202 this.thumbEl.hide();
27204 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27205 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27207 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27208 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27209 this.errorEl.hide();
27211 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27212 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27213 this.footerEl.hide();
27215 this.setThumbBoxSize();
27221 this.fireEvent('initial', this);
27228 window.addEventListener("resize", function() { _this.resize(); } );
27230 this.bodyEl.on('click', this.beforeSelectFile, this);
27233 this.bodyEl.on('touchstart', this.onTouchStart, this);
27234 this.bodyEl.on('touchmove', this.onTouchMove, this);
27235 this.bodyEl.on('touchend', this.onTouchEnd, this);
27239 this.bodyEl.on('mousedown', this.onMouseDown, this);
27240 this.bodyEl.on('mousemove', this.onMouseMove, this);
27241 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27242 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27243 Roo.get(document).on('mouseup', this.onMouseUp, this);
27246 this.selectorEl.on('change', this.onFileSelected, this);
27252 this.baseScale = 1;
27254 this.baseRotate = 1;
27255 this.dragable = false;
27256 this.pinching = false;
27259 this.cropData = false;
27260 this.notifyEl.dom.innerHTML = this.emptyText;
27262 this.selectorEl.dom.value = '';
27266 resize : function()
27268 if(this.fireEvent('resize', this) != false){
27269 this.setThumbBoxPosition();
27270 this.setCanvasPosition();
27274 onFooterButtonClick : function(e, el, o, type)
27277 case 'rotate-left' :
27278 this.onRotateLeft(e);
27280 case 'rotate-right' :
27281 this.onRotateRight(e);
27284 this.beforeSelectFile(e);
27299 this.fireEvent('footerbuttonclick', this, type);
27302 beforeSelectFile : function(e)
27304 e.preventDefault();
27306 if(this.fireEvent('beforeselectfile', this) != false){
27307 this.selectorEl.dom.click();
27311 onFileSelected : function(e)
27313 e.preventDefault();
27315 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27319 var file = this.selectorEl.dom.files[0];
27321 if(this.fireEvent('inspect', this, file) != false){
27322 this.prepare(file);
27327 trash : function(e)
27329 this.fireEvent('trash', this);
27332 download : function(e)
27334 this.fireEvent('download', this);
27337 loadCanvas : function(src)
27339 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27343 this.imageEl = document.createElement('img');
27347 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27349 this.imageEl.src = src;
27353 onLoadCanvas : function()
27355 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27356 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27358 this.bodyEl.un('click', this.beforeSelectFile, this);
27360 this.notifyEl.hide();
27361 this.thumbEl.show();
27362 this.footerEl.show();
27364 this.baseRotateLevel();
27366 if(this.isDocument){
27367 this.setThumbBoxSize();
27370 this.setThumbBoxPosition();
27372 this.baseScaleLevel();
27378 this.canvasLoaded = true;
27381 this.maskEl.unmask();
27386 setCanvasPosition : function()
27388 if(!this.canvasEl){
27392 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27393 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27395 this.previewEl.setLeft(pw);
27396 this.previewEl.setTop(ph);
27400 onMouseDown : function(e)
27404 this.dragable = true;
27405 this.pinching = false;
27407 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27408 this.dragable = false;
27412 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27413 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27417 onMouseMove : function(e)
27421 if(!this.canvasLoaded){
27425 if (!this.dragable){
27429 var minX = Math.ceil(this.thumbEl.getLeft(true));
27430 var minY = Math.ceil(this.thumbEl.getTop(true));
27432 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27433 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27435 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27436 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27438 x = x - this.mouseX;
27439 y = y - this.mouseY;
27441 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27442 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27444 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27445 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27447 this.previewEl.setLeft(bgX);
27448 this.previewEl.setTop(bgY);
27450 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27451 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27454 onMouseUp : function(e)
27458 this.dragable = false;
27461 onMouseWheel : function(e)
27465 this.startScale = this.scale;
27467 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27469 if(!this.zoomable()){
27470 this.scale = this.startScale;
27479 zoomable : function()
27481 var minScale = this.thumbEl.getWidth() / this.minWidth;
27483 if(this.minWidth < this.minHeight){
27484 minScale = this.thumbEl.getHeight() / this.minHeight;
27487 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27488 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27492 (this.rotate == 0 || this.rotate == 180) &&
27494 width > this.imageEl.OriginWidth ||
27495 height > this.imageEl.OriginHeight ||
27496 (width < this.minWidth && height < this.minHeight)
27504 (this.rotate == 90 || this.rotate == 270) &&
27506 width > this.imageEl.OriginWidth ||
27507 height > this.imageEl.OriginHeight ||
27508 (width < this.minHeight && height < this.minWidth)
27515 !this.isDocument &&
27516 (this.rotate == 0 || this.rotate == 180) &&
27518 width < this.minWidth ||
27519 width > this.imageEl.OriginWidth ||
27520 height < this.minHeight ||
27521 height > this.imageEl.OriginHeight
27528 !this.isDocument &&
27529 (this.rotate == 90 || this.rotate == 270) &&
27531 width < this.minHeight ||
27532 width > this.imageEl.OriginWidth ||
27533 height < this.minWidth ||
27534 height > this.imageEl.OriginHeight
27544 onRotateLeft : function(e)
27546 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27548 var minScale = this.thumbEl.getWidth() / this.minWidth;
27550 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27551 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27553 this.startScale = this.scale;
27555 while (this.getScaleLevel() < minScale){
27557 this.scale = this.scale + 1;
27559 if(!this.zoomable()){
27564 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27565 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27570 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27577 this.scale = this.startScale;
27579 this.onRotateFail();
27584 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27586 if(this.isDocument){
27587 this.setThumbBoxSize();
27588 this.setThumbBoxPosition();
27589 this.setCanvasPosition();
27594 this.fireEvent('rotate', this, 'left');
27598 onRotateRight : function(e)
27600 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27602 var minScale = this.thumbEl.getWidth() / this.minWidth;
27604 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27605 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27607 this.startScale = this.scale;
27609 while (this.getScaleLevel() < minScale){
27611 this.scale = this.scale + 1;
27613 if(!this.zoomable()){
27618 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27619 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27624 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27631 this.scale = this.startScale;
27633 this.onRotateFail();
27638 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27640 if(this.isDocument){
27641 this.setThumbBoxSize();
27642 this.setThumbBoxPosition();
27643 this.setCanvasPosition();
27648 this.fireEvent('rotate', this, 'right');
27651 onRotateFail : function()
27653 this.errorEl.show(true);
27657 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27662 this.previewEl.dom.innerHTML = '';
27664 var canvasEl = document.createElement("canvas");
27666 var contextEl = canvasEl.getContext("2d");
27668 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27669 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27670 var center = this.imageEl.OriginWidth / 2;
27672 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27673 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27674 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27675 center = this.imageEl.OriginHeight / 2;
27678 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27680 contextEl.translate(center, center);
27681 contextEl.rotate(this.rotate * Math.PI / 180);
27683 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27685 this.canvasEl = document.createElement("canvas");
27687 this.contextEl = this.canvasEl.getContext("2d");
27689 switch (this.rotate) {
27692 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27693 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27695 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27700 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27701 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27703 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27704 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);
27708 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27713 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27714 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27716 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27717 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);
27721 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);
27726 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27727 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27729 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27730 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27734 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);
27741 this.previewEl.appendChild(this.canvasEl);
27743 this.setCanvasPosition();
27748 if(!this.canvasLoaded){
27752 var imageCanvas = document.createElement("canvas");
27754 var imageContext = imageCanvas.getContext("2d");
27756 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27757 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27759 var center = imageCanvas.width / 2;
27761 imageContext.translate(center, center);
27763 imageContext.rotate(this.rotate * Math.PI / 180);
27765 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27767 var canvas = document.createElement("canvas");
27769 var context = canvas.getContext("2d");
27771 canvas.width = this.minWidth;
27772 canvas.height = this.minHeight;
27774 switch (this.rotate) {
27777 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27778 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27780 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27781 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27783 var targetWidth = this.minWidth - 2 * x;
27784 var targetHeight = this.minHeight - 2 * y;
27788 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27789 scale = targetWidth / width;
27792 if(x > 0 && y == 0){
27793 scale = targetHeight / height;
27796 if(x > 0 && y > 0){
27797 scale = targetWidth / width;
27799 if(width < height){
27800 scale = targetHeight / height;
27804 context.scale(scale, scale);
27806 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27807 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27809 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27810 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27812 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27817 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27818 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27820 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27821 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27823 var targetWidth = this.minWidth - 2 * x;
27824 var targetHeight = this.minHeight - 2 * y;
27828 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27829 scale = targetWidth / width;
27832 if(x > 0 && y == 0){
27833 scale = targetHeight / height;
27836 if(x > 0 && y > 0){
27837 scale = targetWidth / width;
27839 if(width < height){
27840 scale = targetHeight / height;
27844 context.scale(scale, scale);
27846 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27847 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27849 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27850 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27852 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27854 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27859 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27860 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27862 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27863 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27865 var targetWidth = this.minWidth - 2 * x;
27866 var targetHeight = this.minHeight - 2 * y;
27870 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27871 scale = targetWidth / width;
27874 if(x > 0 && y == 0){
27875 scale = targetHeight / height;
27878 if(x > 0 && y > 0){
27879 scale = targetWidth / width;
27881 if(width < height){
27882 scale = targetHeight / height;
27886 context.scale(scale, scale);
27888 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27889 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27891 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27892 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27894 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27895 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27897 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27902 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27903 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27905 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27906 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27908 var targetWidth = this.minWidth - 2 * x;
27909 var targetHeight = this.minHeight - 2 * y;
27913 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27914 scale = targetWidth / width;
27917 if(x > 0 && y == 0){
27918 scale = targetHeight / height;
27921 if(x > 0 && y > 0){
27922 scale = targetWidth / width;
27924 if(width < height){
27925 scale = targetHeight / height;
27929 context.scale(scale, scale);
27931 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27932 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27934 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27935 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27937 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27939 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27946 this.cropData = canvas.toDataURL(this.cropType);
27948 if(this.fireEvent('crop', this, this.cropData) !== false){
27949 this.process(this.file, this.cropData);
27956 setThumbBoxSize : function()
27960 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27961 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27962 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27964 this.minWidth = width;
27965 this.minHeight = height;
27967 if(this.rotate == 90 || this.rotate == 270){
27968 this.minWidth = height;
27969 this.minHeight = width;
27974 width = Math.ceil(this.minWidth * height / this.minHeight);
27976 if(this.minWidth > this.minHeight){
27978 height = Math.ceil(this.minHeight * width / this.minWidth);
27981 this.thumbEl.setStyle({
27982 width : width + 'px',
27983 height : height + 'px'
27990 setThumbBoxPosition : function()
27992 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27993 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27995 this.thumbEl.setLeft(x);
27996 this.thumbEl.setTop(y);
28000 baseRotateLevel : function()
28002 this.baseRotate = 1;
28005 typeof(this.exif) != 'undefined' &&
28006 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28007 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28009 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28012 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28016 baseScaleLevel : function()
28020 if(this.isDocument){
28022 if(this.baseRotate == 6 || this.baseRotate == 8){
28024 height = this.thumbEl.getHeight();
28025 this.baseScale = height / this.imageEl.OriginWidth;
28027 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28028 width = this.thumbEl.getWidth();
28029 this.baseScale = width / this.imageEl.OriginHeight;
28035 height = this.thumbEl.getHeight();
28036 this.baseScale = height / this.imageEl.OriginHeight;
28038 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28039 width = this.thumbEl.getWidth();
28040 this.baseScale = width / this.imageEl.OriginWidth;
28046 if(this.baseRotate == 6 || this.baseRotate == 8){
28048 width = this.thumbEl.getHeight();
28049 this.baseScale = width / this.imageEl.OriginHeight;
28051 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28052 height = this.thumbEl.getWidth();
28053 this.baseScale = height / this.imageEl.OriginHeight;
28056 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28057 height = this.thumbEl.getWidth();
28058 this.baseScale = height / this.imageEl.OriginHeight;
28060 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28061 width = this.thumbEl.getHeight();
28062 this.baseScale = width / this.imageEl.OriginWidth;
28069 width = this.thumbEl.getWidth();
28070 this.baseScale = width / this.imageEl.OriginWidth;
28072 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28073 height = this.thumbEl.getHeight();
28074 this.baseScale = height / this.imageEl.OriginHeight;
28077 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28079 height = this.thumbEl.getHeight();
28080 this.baseScale = height / this.imageEl.OriginHeight;
28082 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28083 width = this.thumbEl.getWidth();
28084 this.baseScale = width / this.imageEl.OriginWidth;
28092 getScaleLevel : function()
28094 return this.baseScale * Math.pow(1.1, this.scale);
28097 onTouchStart : function(e)
28099 if(!this.canvasLoaded){
28100 this.beforeSelectFile(e);
28104 var touches = e.browserEvent.touches;
28110 if(touches.length == 1){
28111 this.onMouseDown(e);
28115 if(touches.length != 2){
28121 for(var i = 0, finger; finger = touches[i]; i++){
28122 coords.push(finger.pageX, finger.pageY);
28125 var x = Math.pow(coords[0] - coords[2], 2);
28126 var y = Math.pow(coords[1] - coords[3], 2);
28128 this.startDistance = Math.sqrt(x + y);
28130 this.startScale = this.scale;
28132 this.pinching = true;
28133 this.dragable = false;
28137 onTouchMove : function(e)
28139 if(!this.pinching && !this.dragable){
28143 var touches = e.browserEvent.touches;
28150 this.onMouseMove(e);
28156 for(var i = 0, finger; finger = touches[i]; i++){
28157 coords.push(finger.pageX, finger.pageY);
28160 var x = Math.pow(coords[0] - coords[2], 2);
28161 var y = Math.pow(coords[1] - coords[3], 2);
28163 this.endDistance = Math.sqrt(x + y);
28165 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28167 if(!this.zoomable()){
28168 this.scale = this.startScale;
28176 onTouchEnd : function(e)
28178 this.pinching = false;
28179 this.dragable = false;
28183 process : function(file, crop)
28186 this.maskEl.mask(this.loadingText);
28189 this.xhr = new XMLHttpRequest();
28191 file.xhr = this.xhr;
28193 this.xhr.open(this.method, this.url, true);
28196 "Accept": "application/json",
28197 "Cache-Control": "no-cache",
28198 "X-Requested-With": "XMLHttpRequest"
28201 for (var headerName in headers) {
28202 var headerValue = headers[headerName];
28204 this.xhr.setRequestHeader(headerName, headerValue);
28210 this.xhr.onload = function()
28212 _this.xhrOnLoad(_this.xhr);
28215 this.xhr.onerror = function()
28217 _this.xhrOnError(_this.xhr);
28220 var formData = new FormData();
28222 formData.append('returnHTML', 'NO');
28225 formData.append('crop', crop);
28228 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28229 formData.append(this.paramName, file, file.name);
28232 if(typeof(file.filename) != 'undefined'){
28233 formData.append('filename', file.filename);
28236 if(typeof(file.mimetype) != 'undefined'){
28237 formData.append('mimetype', file.mimetype);
28240 if(this.fireEvent('arrange', this, formData) != false){
28241 this.xhr.send(formData);
28245 xhrOnLoad : function(xhr)
28248 this.maskEl.unmask();
28251 if (xhr.readyState !== 4) {
28252 this.fireEvent('exception', this, xhr);
28256 var response = Roo.decode(xhr.responseText);
28258 if(!response.success){
28259 this.fireEvent('exception', this, xhr);
28263 var response = Roo.decode(xhr.responseText);
28265 this.fireEvent('upload', this, response);
28269 xhrOnError : function()
28272 this.maskEl.unmask();
28275 Roo.log('xhr on error');
28277 var response = Roo.decode(xhr.responseText);
28283 prepare : function(file)
28286 this.maskEl.mask(this.loadingText);
28292 if(typeof(file) === 'string'){
28293 this.loadCanvas(file);
28297 if(!file || !this.urlAPI){
28302 this.cropType = file.type;
28306 if(this.fireEvent('prepare', this, this.file) != false){
28308 var reader = new FileReader();
28310 reader.onload = function (e) {
28311 if (e.target.error) {
28312 Roo.log(e.target.error);
28316 var buffer = e.target.result,
28317 dataView = new DataView(buffer),
28319 maxOffset = dataView.byteLength - 4,
28323 if (dataView.getUint16(0) === 0xffd8) {
28324 while (offset < maxOffset) {
28325 markerBytes = dataView.getUint16(offset);
28327 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28328 markerLength = dataView.getUint16(offset + 2) + 2;
28329 if (offset + markerLength > dataView.byteLength) {
28330 Roo.log('Invalid meta data: Invalid segment size.');
28334 if(markerBytes == 0xffe1){
28335 _this.parseExifData(
28342 offset += markerLength;
28352 var url = _this.urlAPI.createObjectURL(_this.file);
28354 _this.loadCanvas(url);
28359 reader.readAsArrayBuffer(this.file);
28365 parseExifData : function(dataView, offset, length)
28367 var tiffOffset = offset + 10,
28371 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28372 // No Exif data, might be XMP data instead
28376 // Check for the ASCII code for "Exif" (0x45786966):
28377 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28378 // No Exif data, might be XMP data instead
28381 if (tiffOffset + 8 > dataView.byteLength) {
28382 Roo.log('Invalid Exif data: Invalid segment size.');
28385 // Check for the two null bytes:
28386 if (dataView.getUint16(offset + 8) !== 0x0000) {
28387 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28390 // Check the byte alignment:
28391 switch (dataView.getUint16(tiffOffset)) {
28393 littleEndian = true;
28396 littleEndian = false;
28399 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28402 // Check for the TIFF tag marker (0x002A):
28403 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28404 Roo.log('Invalid Exif data: Missing TIFF marker.');
28407 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28408 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28410 this.parseExifTags(
28413 tiffOffset + dirOffset,
28418 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28423 if (dirOffset + 6 > dataView.byteLength) {
28424 Roo.log('Invalid Exif data: Invalid directory offset.');
28427 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28428 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28429 if (dirEndOffset + 4 > dataView.byteLength) {
28430 Roo.log('Invalid Exif data: Invalid directory size.');
28433 for (i = 0; i < tagsNumber; i += 1) {
28437 dirOffset + 2 + 12 * i, // tag offset
28441 // Return the offset to the next directory:
28442 return dataView.getUint32(dirEndOffset, littleEndian);
28445 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28447 var tag = dataView.getUint16(offset, littleEndian);
28449 this.exif[tag] = this.getExifValue(
28453 dataView.getUint16(offset + 2, littleEndian), // tag type
28454 dataView.getUint32(offset + 4, littleEndian), // tag length
28459 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28461 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28470 Roo.log('Invalid Exif data: Invalid tag type.');
28474 tagSize = tagType.size * length;
28475 // Determine if the value is contained in the dataOffset bytes,
28476 // or if the value at the dataOffset is a pointer to the actual data:
28477 dataOffset = tagSize > 4 ?
28478 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28479 if (dataOffset + tagSize > dataView.byteLength) {
28480 Roo.log('Invalid Exif data: Invalid data offset.');
28483 if (length === 1) {
28484 return tagType.getValue(dataView, dataOffset, littleEndian);
28487 for (i = 0; i < length; i += 1) {
28488 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28491 if (tagType.ascii) {
28493 // Concatenate the chars:
28494 for (i = 0; i < values.length; i += 1) {
28496 // Ignore the terminating NULL byte(s):
28497 if (c === '\u0000') {
28509 Roo.apply(Roo.bootstrap.UploadCropbox, {
28511 'Orientation': 0x0112
28515 1: 0, //'top-left',
28517 3: 180, //'bottom-right',
28518 // 4: 'bottom-left',
28520 6: 90, //'right-top',
28521 // 7: 'right-bottom',
28522 8: 270 //'left-bottom'
28526 // byte, 8-bit unsigned int:
28528 getValue: function (dataView, dataOffset) {
28529 return dataView.getUint8(dataOffset);
28533 // ascii, 8-bit byte:
28535 getValue: function (dataView, dataOffset) {
28536 return String.fromCharCode(dataView.getUint8(dataOffset));
28541 // short, 16 bit int:
28543 getValue: function (dataView, dataOffset, littleEndian) {
28544 return dataView.getUint16(dataOffset, littleEndian);
28548 // long, 32 bit int:
28550 getValue: function (dataView, dataOffset, littleEndian) {
28551 return dataView.getUint32(dataOffset, littleEndian);
28555 // rational = two long values, first is numerator, second is denominator:
28557 getValue: function (dataView, dataOffset, littleEndian) {
28558 return dataView.getUint32(dataOffset, littleEndian) /
28559 dataView.getUint32(dataOffset + 4, littleEndian);
28563 // slong, 32 bit signed int:
28565 getValue: function (dataView, dataOffset, littleEndian) {
28566 return dataView.getInt32(dataOffset, littleEndian);
28570 // srational, two slongs, first is numerator, second is denominator:
28572 getValue: function (dataView, dataOffset, littleEndian) {
28573 return dataView.getInt32(dataOffset, littleEndian) /
28574 dataView.getInt32(dataOffset + 4, littleEndian);
28584 cls : 'btn-group roo-upload-cropbox-rotate-left',
28585 action : 'rotate-left',
28589 cls : 'btn btn-default',
28590 html : '<i class="fa fa-undo"></i>'
28596 cls : 'btn-group roo-upload-cropbox-picture',
28597 action : 'picture',
28601 cls : 'btn btn-default',
28602 html : '<i class="fa fa-picture-o"></i>'
28608 cls : 'btn-group roo-upload-cropbox-rotate-right',
28609 action : 'rotate-right',
28613 cls : 'btn btn-default',
28614 html : '<i class="fa fa-repeat"></i>'
28622 cls : 'btn-group roo-upload-cropbox-rotate-left',
28623 action : 'rotate-left',
28627 cls : 'btn btn-default',
28628 html : '<i class="fa fa-undo"></i>'
28634 cls : 'btn-group roo-upload-cropbox-download',
28635 action : 'download',
28639 cls : 'btn btn-default',
28640 html : '<i class="fa fa-download"></i>'
28646 cls : 'btn-group roo-upload-cropbox-crop',
28651 cls : 'btn btn-default',
28652 html : '<i class="fa fa-crop"></i>'
28658 cls : 'btn-group roo-upload-cropbox-trash',
28663 cls : 'btn btn-default',
28664 html : '<i class="fa fa-trash"></i>'
28670 cls : 'btn-group roo-upload-cropbox-rotate-right',
28671 action : 'rotate-right',
28675 cls : 'btn btn-default',
28676 html : '<i class="fa fa-repeat"></i>'
28684 cls : 'btn-group roo-upload-cropbox-rotate-left',
28685 action : 'rotate-left',
28689 cls : 'btn btn-default',
28690 html : '<i class="fa fa-undo"></i>'
28696 cls : 'btn-group roo-upload-cropbox-rotate-right',
28697 action : 'rotate-right',
28701 cls : 'btn btn-default',
28702 html : '<i class="fa fa-repeat"></i>'
28715 * @class Roo.bootstrap.DocumentManager
28716 * @extends Roo.bootstrap.Component
28717 * Bootstrap DocumentManager class
28718 * @cfg {String} paramName default 'imageUpload'
28719 * @cfg {String} toolTipName default 'filename'
28720 * @cfg {String} method default POST
28721 * @cfg {String} url action url
28722 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28723 * @cfg {Boolean} multiple multiple upload default true
28724 * @cfg {Number} thumbSize default 300
28725 * @cfg {String} fieldLabel
28726 * @cfg {Number} labelWidth default 4
28727 * @cfg {String} labelAlign (left|top) default left
28728 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28729 * @cfg {Number} labellg set the width of label (1-12)
28730 * @cfg {Number} labelmd set the width of label (1-12)
28731 * @cfg {Number} labelsm set the width of label (1-12)
28732 * @cfg {Number} labelxs set the width of label (1-12)
28735 * Create a new DocumentManager
28736 * @param {Object} config The config object
28739 Roo.bootstrap.DocumentManager = function(config){
28740 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28743 this.delegates = [];
28748 * Fire when initial the DocumentManager
28749 * @param {Roo.bootstrap.DocumentManager} this
28754 * inspect selected file
28755 * @param {Roo.bootstrap.DocumentManager} this
28756 * @param {File} file
28761 * Fire when xhr load exception
28762 * @param {Roo.bootstrap.DocumentManager} this
28763 * @param {XMLHttpRequest} xhr
28765 "exception" : true,
28767 * @event afterupload
28768 * Fire when xhr load exception
28769 * @param {Roo.bootstrap.DocumentManager} this
28770 * @param {XMLHttpRequest} xhr
28772 "afterupload" : true,
28775 * prepare the form data
28776 * @param {Roo.bootstrap.DocumentManager} this
28777 * @param {Object} formData
28782 * Fire when remove the file
28783 * @param {Roo.bootstrap.DocumentManager} this
28784 * @param {Object} file
28789 * Fire after refresh the file
28790 * @param {Roo.bootstrap.DocumentManager} this
28795 * Fire after click the image
28796 * @param {Roo.bootstrap.DocumentManager} this
28797 * @param {Object} file
28802 * Fire when upload a image and editable set to true
28803 * @param {Roo.bootstrap.DocumentManager} this
28804 * @param {Object} file
28808 * @event beforeselectfile
28809 * Fire before select file
28810 * @param {Roo.bootstrap.DocumentManager} this
28812 "beforeselectfile" : true,
28815 * Fire before process file
28816 * @param {Roo.bootstrap.DocumentManager} this
28817 * @param {Object} file
28821 * @event previewrendered
28822 * Fire when preview rendered
28823 * @param {Roo.bootstrap.DocumentManager} this
28824 * @param {Object} file
28826 "previewrendered" : true,
28829 "previewResize" : true
28834 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28843 paramName : 'imageUpload',
28844 toolTipName : 'filename',
28847 labelAlign : 'left',
28857 getAutoCreate : function()
28859 var managerWidget = {
28861 cls : 'roo-document-manager',
28865 cls : 'roo-document-manager-selector',
28870 cls : 'roo-document-manager-uploader',
28874 cls : 'roo-document-manager-upload-btn',
28875 html : '<i class="fa fa-plus"></i>'
28886 cls : 'column col-md-12',
28891 if(this.fieldLabel.length){
28896 cls : 'column col-md-12',
28897 html : this.fieldLabel
28901 cls : 'column col-md-12',
28906 if(this.labelAlign == 'left'){
28911 html : this.fieldLabel
28920 if(this.labelWidth > 12){
28921 content[0].style = "width: " + this.labelWidth + 'px';
28924 if(this.labelWidth < 13 && this.labelmd == 0){
28925 this.labelmd = this.labelWidth;
28928 if(this.labellg > 0){
28929 content[0].cls += ' col-lg-' + this.labellg;
28930 content[1].cls += ' col-lg-' + (12 - this.labellg);
28933 if(this.labelmd > 0){
28934 content[0].cls += ' col-md-' + this.labelmd;
28935 content[1].cls += ' col-md-' + (12 - this.labelmd);
28938 if(this.labelsm > 0){
28939 content[0].cls += ' col-sm-' + this.labelsm;
28940 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28943 if(this.labelxs > 0){
28944 content[0].cls += ' col-xs-' + this.labelxs;
28945 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28953 cls : 'row clearfix',
28961 initEvents : function()
28963 this.managerEl = this.el.select('.roo-document-manager', true).first();
28964 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28966 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28967 this.selectorEl.hide();
28970 this.selectorEl.attr('multiple', 'multiple');
28973 this.selectorEl.on('change', this.onFileSelected, this);
28975 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28976 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28978 this.uploader.on('click', this.onUploaderClick, this);
28980 this.renderProgressDialog();
28984 window.addEventListener("resize", function() { _this.refresh(); } );
28986 this.fireEvent('initial', this);
28989 renderProgressDialog : function()
28993 this.progressDialog = new Roo.bootstrap.Modal({
28994 cls : 'roo-document-manager-progress-dialog',
28995 allow_close : false,
29005 btnclick : function() {
29006 _this.uploadCancel();
29012 this.progressDialog.render(Roo.get(document.body));
29014 this.progress = new Roo.bootstrap.Progress({
29015 cls : 'roo-document-manager-progress',
29020 this.progress.render(this.progressDialog.getChildContainer());
29022 this.progressBar = new Roo.bootstrap.ProgressBar({
29023 cls : 'roo-document-manager-progress-bar',
29026 aria_valuemax : 12,
29030 this.progressBar.render(this.progress.getChildContainer());
29033 onUploaderClick : function(e)
29035 e.preventDefault();
29037 if(this.fireEvent('beforeselectfile', this) != false){
29038 this.selectorEl.dom.click();
29043 onFileSelected : function(e)
29045 e.preventDefault();
29047 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29051 Roo.each(this.selectorEl.dom.files, function(file){
29052 if(this.fireEvent('inspect', this, file) != false){
29053 this.files.push(file);
29063 this.selectorEl.dom.value = '';
29065 if(!this.files || !this.files.length){
29069 if(this.boxes > 0 && this.files.length > this.boxes){
29070 this.files = this.files.slice(0, this.boxes);
29073 this.uploader.show();
29075 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29076 this.uploader.hide();
29085 Roo.each(this.files, function(file){
29087 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29088 var f = this.renderPreview(file);
29093 if(file.type.indexOf('image') != -1){
29094 this.delegates.push(
29096 _this.process(file);
29097 }).createDelegate(this)
29105 _this.process(file);
29106 }).createDelegate(this)
29111 this.files = files;
29113 this.delegates = this.delegates.concat(docs);
29115 if(!this.delegates.length){
29120 this.progressBar.aria_valuemax = this.delegates.length;
29127 arrange : function()
29129 if(!this.delegates.length){
29130 this.progressDialog.hide();
29135 var delegate = this.delegates.shift();
29137 this.progressDialog.show();
29139 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29141 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29146 refresh : function()
29148 this.uploader.show();
29150 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29151 this.uploader.hide();
29154 Roo.isTouch ? this.closable(false) : this.closable(true);
29156 this.fireEvent('refresh', this);
29159 onRemove : function(e, el, o)
29161 e.preventDefault();
29163 this.fireEvent('remove', this, o);
29167 remove : function(o)
29171 Roo.each(this.files, function(file){
29172 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29181 this.files = files;
29188 Roo.each(this.files, function(file){
29193 file.target.remove();
29202 onClick : function(e, el, o)
29204 e.preventDefault();
29206 this.fireEvent('click', this, o);
29210 closable : function(closable)
29212 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29214 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29226 xhrOnLoad : function(xhr)
29228 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29232 if (xhr.readyState !== 4) {
29234 this.fireEvent('exception', this, xhr);
29238 var response = Roo.decode(xhr.responseText);
29240 if(!response.success){
29242 this.fireEvent('exception', this, xhr);
29246 var file = this.renderPreview(response.data);
29248 this.files.push(file);
29252 this.fireEvent('afterupload', this, xhr);
29256 xhrOnError : function(xhr)
29258 Roo.log('xhr on error');
29260 var response = Roo.decode(xhr.responseText);
29267 process : function(file)
29269 if(this.fireEvent('process', this, file) !== false){
29270 if(this.editable && file.type.indexOf('image') != -1){
29271 this.fireEvent('edit', this, file);
29275 this.uploadStart(file, false);
29282 uploadStart : function(file, crop)
29284 this.xhr = new XMLHttpRequest();
29286 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29291 file.xhr = this.xhr;
29293 this.managerEl.createChild({
29295 cls : 'roo-document-manager-loading',
29299 tooltip : file.name,
29300 cls : 'roo-document-manager-thumb',
29301 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29307 this.xhr.open(this.method, this.url, true);
29310 "Accept": "application/json",
29311 "Cache-Control": "no-cache",
29312 "X-Requested-With": "XMLHttpRequest"
29315 for (var headerName in headers) {
29316 var headerValue = headers[headerName];
29318 this.xhr.setRequestHeader(headerName, headerValue);
29324 this.xhr.onload = function()
29326 _this.xhrOnLoad(_this.xhr);
29329 this.xhr.onerror = function()
29331 _this.xhrOnError(_this.xhr);
29334 var formData = new FormData();
29336 formData.append('returnHTML', 'NO');
29339 formData.append('crop', crop);
29342 formData.append(this.paramName, file, file.name);
29349 if(this.fireEvent('prepare', this, formData, options) != false){
29351 if(options.manually){
29355 this.xhr.send(formData);
29359 this.uploadCancel();
29362 uploadCancel : function()
29368 this.delegates = [];
29370 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29377 renderPreview : function(file)
29379 if(typeof(file.target) != 'undefined' && file.target){
29383 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29385 var previewEl = this.managerEl.createChild({
29387 cls : 'roo-document-manager-preview',
29391 tooltip : file[this.toolTipName],
29392 cls : 'roo-document-manager-thumb',
29393 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29398 html : '<i class="fa fa-times-circle"></i>'
29403 var close = previewEl.select('button.close', true).first();
29405 close.on('click', this.onRemove, this, file);
29407 file.target = previewEl;
29409 var image = previewEl.select('img', true).first();
29413 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29415 image.on('click', this.onClick, this, file);
29417 this.fireEvent('previewrendered', this, file);
29423 onPreviewLoad : function(file, image)
29425 if(typeof(file.target) == 'undefined' || !file.target){
29429 var width = image.dom.naturalWidth || image.dom.width;
29430 var height = image.dom.naturalHeight || image.dom.height;
29432 if(!this.previewResize) {
29436 if(width > height){
29437 file.target.addClass('wide');
29441 file.target.addClass('tall');
29446 uploadFromSource : function(file, crop)
29448 this.xhr = new XMLHttpRequest();
29450 this.managerEl.createChild({
29452 cls : 'roo-document-manager-loading',
29456 tooltip : file.name,
29457 cls : 'roo-document-manager-thumb',
29458 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29464 this.xhr.open(this.method, this.url, true);
29467 "Accept": "application/json",
29468 "Cache-Control": "no-cache",
29469 "X-Requested-With": "XMLHttpRequest"
29472 for (var headerName in headers) {
29473 var headerValue = headers[headerName];
29475 this.xhr.setRequestHeader(headerName, headerValue);
29481 this.xhr.onload = function()
29483 _this.xhrOnLoad(_this.xhr);
29486 this.xhr.onerror = function()
29488 _this.xhrOnError(_this.xhr);
29491 var formData = new FormData();
29493 formData.append('returnHTML', 'NO');
29495 formData.append('crop', crop);
29497 if(typeof(file.filename) != 'undefined'){
29498 formData.append('filename', file.filename);
29501 if(typeof(file.mimetype) != 'undefined'){
29502 formData.append('mimetype', file.mimetype);
29507 if(this.fireEvent('prepare', this, formData) != false){
29508 this.xhr.send(formData);
29518 * @class Roo.bootstrap.DocumentViewer
29519 * @extends Roo.bootstrap.Component
29520 * Bootstrap DocumentViewer class
29521 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29522 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29525 * Create a new DocumentViewer
29526 * @param {Object} config The config object
29529 Roo.bootstrap.DocumentViewer = function(config){
29530 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29535 * Fire after initEvent
29536 * @param {Roo.bootstrap.DocumentViewer} this
29542 * @param {Roo.bootstrap.DocumentViewer} this
29547 * Fire after download button
29548 * @param {Roo.bootstrap.DocumentViewer} this
29553 * Fire after trash button
29554 * @param {Roo.bootstrap.DocumentViewer} this
29561 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29563 showDownload : true,
29567 getAutoCreate : function()
29571 cls : 'roo-document-viewer',
29575 cls : 'roo-document-viewer-body',
29579 cls : 'roo-document-viewer-thumb',
29583 cls : 'roo-document-viewer-image'
29591 cls : 'roo-document-viewer-footer',
29594 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29598 cls : 'btn-group roo-document-viewer-download',
29602 cls : 'btn btn-default',
29603 html : '<i class="fa fa-download"></i>'
29609 cls : 'btn-group roo-document-viewer-trash',
29613 cls : 'btn btn-default',
29614 html : '<i class="fa fa-trash"></i>'
29627 initEvents : function()
29629 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29630 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29632 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29633 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29635 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29636 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29638 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29639 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29641 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29642 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29644 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29645 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29647 this.bodyEl.on('click', this.onClick, this);
29648 this.downloadBtn.on('click', this.onDownload, this);
29649 this.trashBtn.on('click', this.onTrash, this);
29651 this.downloadBtn.hide();
29652 this.trashBtn.hide();
29654 if(this.showDownload){
29655 this.downloadBtn.show();
29658 if(this.showTrash){
29659 this.trashBtn.show();
29662 if(!this.showDownload && !this.showTrash) {
29663 this.footerEl.hide();
29668 initial : function()
29670 this.fireEvent('initial', this);
29674 onClick : function(e)
29676 e.preventDefault();
29678 this.fireEvent('click', this);
29681 onDownload : function(e)
29683 e.preventDefault();
29685 this.fireEvent('download', this);
29688 onTrash : function(e)
29690 e.preventDefault();
29692 this.fireEvent('trash', this);
29704 * @class Roo.bootstrap.NavProgressBar
29705 * @extends Roo.bootstrap.Component
29706 * Bootstrap NavProgressBar class
29709 * Create a new nav progress bar
29710 * @param {Object} config The config object
29713 Roo.bootstrap.NavProgressBar = function(config){
29714 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29716 this.bullets = this.bullets || [];
29718 // Roo.bootstrap.NavProgressBar.register(this);
29722 * Fires when the active item changes
29723 * @param {Roo.bootstrap.NavProgressBar} this
29724 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29725 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29732 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29737 getAutoCreate : function()
29739 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29743 cls : 'roo-navigation-bar-group',
29747 cls : 'roo-navigation-top-bar'
29751 cls : 'roo-navigation-bullets-bar',
29755 cls : 'roo-navigation-bar'
29762 cls : 'roo-navigation-bottom-bar'
29772 initEvents: function()
29777 onRender : function(ct, position)
29779 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29781 if(this.bullets.length){
29782 Roo.each(this.bullets, function(b){
29791 addItem : function(cfg)
29793 var item = new Roo.bootstrap.NavProgressItem(cfg);
29795 item.parentId = this.id;
29796 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29799 var top = new Roo.bootstrap.Element({
29801 cls : 'roo-navigation-bar-text'
29804 var bottom = new Roo.bootstrap.Element({
29806 cls : 'roo-navigation-bar-text'
29809 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29810 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29812 var topText = new Roo.bootstrap.Element({
29814 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29817 var bottomText = new Roo.bootstrap.Element({
29819 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29822 topText.onRender(top.el, null);
29823 bottomText.onRender(bottom.el, null);
29826 item.bottomEl = bottom;
29829 this.barItems.push(item);
29834 getActive : function()
29836 var active = false;
29838 Roo.each(this.barItems, function(v){
29840 if (!v.isActive()) {
29852 setActiveItem : function(item)
29856 Roo.each(this.barItems, function(v){
29857 if (v.rid == item.rid) {
29861 if (v.isActive()) {
29862 v.setActive(false);
29867 item.setActive(true);
29869 this.fireEvent('changed', this, item, prev);
29872 getBarItem: function(rid)
29876 Roo.each(this.barItems, function(e) {
29877 if (e.rid != rid) {
29888 indexOfItem : function(item)
29892 Roo.each(this.barItems, function(v, i){
29894 if (v.rid != item.rid) {
29905 setActiveNext : function()
29907 var i = this.indexOfItem(this.getActive());
29909 if (i > this.barItems.length) {
29913 this.setActiveItem(this.barItems[i+1]);
29916 setActivePrev : function()
29918 var i = this.indexOfItem(this.getActive());
29924 this.setActiveItem(this.barItems[i-1]);
29927 format : function()
29929 if(!this.barItems.length){
29933 var width = 100 / this.barItems.length;
29935 Roo.each(this.barItems, function(i){
29936 i.el.setStyle('width', width + '%');
29937 i.topEl.el.setStyle('width', width + '%');
29938 i.bottomEl.el.setStyle('width', width + '%');
29947 * Nav Progress Item
29952 * @class Roo.bootstrap.NavProgressItem
29953 * @extends Roo.bootstrap.Component
29954 * Bootstrap NavProgressItem class
29955 * @cfg {String} rid the reference id
29956 * @cfg {Boolean} active (true|false) Is item active default false
29957 * @cfg {Boolean} disabled (true|false) Is item active default false
29958 * @cfg {String} html
29959 * @cfg {String} position (top|bottom) text position default bottom
29960 * @cfg {String} icon show icon instead of number
29963 * Create a new NavProgressItem
29964 * @param {Object} config The config object
29966 Roo.bootstrap.NavProgressItem = function(config){
29967 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29972 * The raw click event for the entire grid.
29973 * @param {Roo.bootstrap.NavProgressItem} this
29974 * @param {Roo.EventObject} e
29981 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29987 position : 'bottom',
29990 getAutoCreate : function()
29992 var iconCls = 'roo-navigation-bar-item-icon';
29994 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29998 cls: 'roo-navigation-bar-item',
30008 cfg.cls += ' active';
30011 cfg.cls += ' disabled';
30017 disable : function()
30019 this.setDisabled(true);
30022 enable : function()
30024 this.setDisabled(false);
30027 initEvents: function()
30029 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30031 this.iconEl.on('click', this.onClick, this);
30034 onClick : function(e)
30036 e.preventDefault();
30042 if(this.fireEvent('click', this, e) === false){
30046 this.parent().setActiveItem(this);
30049 isActive: function ()
30051 return this.active;
30054 setActive : function(state)
30056 if(this.active == state){
30060 this.active = state;
30063 this.el.addClass('active');
30067 this.el.removeClass('active');
30072 setDisabled : function(state)
30074 if(this.disabled == state){
30078 this.disabled = state;
30081 this.el.addClass('disabled');
30085 this.el.removeClass('disabled');
30088 tooltipEl : function()
30090 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30103 * @class Roo.bootstrap.FieldLabel
30104 * @extends Roo.bootstrap.Component
30105 * Bootstrap FieldLabel class
30106 * @cfg {String} html contents of the element
30107 * @cfg {String} tag tag of the element default label
30108 * @cfg {String} cls class of the element
30109 * @cfg {String} target label target
30110 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30111 * @cfg {String} invalidClass default "text-warning"
30112 * @cfg {String} validClass default "text-success"
30113 * @cfg {String} iconTooltip default "This field is required"
30114 * @cfg {String} indicatorpos (left|right) default left
30117 * Create a new FieldLabel
30118 * @param {Object} config The config object
30121 Roo.bootstrap.FieldLabel = function(config){
30122 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30127 * Fires after the field has been marked as invalid.
30128 * @param {Roo.form.FieldLabel} this
30129 * @param {String} msg The validation message
30134 * Fires after the field has been validated with no errors.
30135 * @param {Roo.form.FieldLabel} this
30141 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30148 invalidClass : 'has-warning',
30149 validClass : 'has-success',
30150 iconTooltip : 'This field is required',
30151 indicatorpos : 'left',
30153 getAutoCreate : function(){
30156 if (!this.allowBlank) {
30162 cls : 'roo-bootstrap-field-label ' + this.cls,
30167 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30168 tooltip : this.iconTooltip
30177 if(this.indicatorpos == 'right'){
30180 cls : 'roo-bootstrap-field-label ' + this.cls,
30189 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30190 tooltip : this.iconTooltip
30199 initEvents: function()
30201 Roo.bootstrap.Element.superclass.initEvents.call(this);
30203 this.indicator = this.indicatorEl();
30205 if(this.indicator){
30206 this.indicator.removeClass('visible');
30207 this.indicator.addClass('invisible');
30210 Roo.bootstrap.FieldLabel.register(this);
30213 indicatorEl : function()
30215 var indicator = this.el.select('i.roo-required-indicator',true).first();
30226 * Mark this field as valid
30228 markValid : function()
30230 if(this.indicator){
30231 this.indicator.removeClass('visible');
30232 this.indicator.addClass('invisible');
30235 this.el.removeClass(this.invalidClass);
30237 this.el.addClass(this.validClass);
30239 this.fireEvent('valid', this);
30243 * Mark this field as invalid
30244 * @param {String} msg The validation message
30246 markInvalid : function(msg)
30248 if(this.indicator){
30249 this.indicator.removeClass('invisible');
30250 this.indicator.addClass('visible');
30253 this.el.removeClass(this.validClass);
30255 this.el.addClass(this.invalidClass);
30257 this.fireEvent('invalid', this, msg);
30263 Roo.apply(Roo.bootstrap.FieldLabel, {
30268 * register a FieldLabel Group
30269 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30271 register : function(label)
30273 if(this.groups.hasOwnProperty(label.target)){
30277 this.groups[label.target] = label;
30281 * fetch a FieldLabel Group based on the target
30282 * @param {string} target
30283 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30285 get: function(target) {
30286 if (typeof(this.groups[target]) == 'undefined') {
30290 return this.groups[target] ;
30299 * page DateSplitField.
30305 * @class Roo.bootstrap.DateSplitField
30306 * @extends Roo.bootstrap.Component
30307 * Bootstrap DateSplitField class
30308 * @cfg {string} fieldLabel - the label associated
30309 * @cfg {Number} labelWidth set the width of label (0-12)
30310 * @cfg {String} labelAlign (top|left)
30311 * @cfg {Boolean} dayAllowBlank (true|false) default false
30312 * @cfg {Boolean} monthAllowBlank (true|false) default false
30313 * @cfg {Boolean} yearAllowBlank (true|false) default false
30314 * @cfg {string} dayPlaceholder
30315 * @cfg {string} monthPlaceholder
30316 * @cfg {string} yearPlaceholder
30317 * @cfg {string} dayFormat default 'd'
30318 * @cfg {string} monthFormat default 'm'
30319 * @cfg {string} yearFormat default 'Y'
30320 * @cfg {Number} labellg set the width of label (1-12)
30321 * @cfg {Number} labelmd set the width of label (1-12)
30322 * @cfg {Number} labelsm set the width of label (1-12)
30323 * @cfg {Number} labelxs set the width of label (1-12)
30327 * Create a new DateSplitField
30328 * @param {Object} config The config object
30331 Roo.bootstrap.DateSplitField = function(config){
30332 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30338 * getting the data of years
30339 * @param {Roo.bootstrap.DateSplitField} this
30340 * @param {Object} years
30345 * getting the data of days
30346 * @param {Roo.bootstrap.DateSplitField} this
30347 * @param {Object} days
30352 * Fires after the field has been marked as invalid.
30353 * @param {Roo.form.Field} this
30354 * @param {String} msg The validation message
30359 * Fires after the field has been validated with no errors.
30360 * @param {Roo.form.Field} this
30366 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30369 labelAlign : 'top',
30371 dayAllowBlank : false,
30372 monthAllowBlank : false,
30373 yearAllowBlank : false,
30374 dayPlaceholder : '',
30375 monthPlaceholder : '',
30376 yearPlaceholder : '',
30380 isFormField : true,
30386 getAutoCreate : function()
30390 cls : 'row roo-date-split-field-group',
30395 cls : 'form-hidden-field roo-date-split-field-group-value',
30401 var labelCls = 'col-md-12';
30402 var contentCls = 'col-md-4';
30404 if(this.fieldLabel){
30408 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30412 html : this.fieldLabel
30417 if(this.labelAlign == 'left'){
30419 if(this.labelWidth > 12){
30420 label.style = "width: " + this.labelWidth + 'px';
30423 if(this.labelWidth < 13 && this.labelmd == 0){
30424 this.labelmd = this.labelWidth;
30427 if(this.labellg > 0){
30428 labelCls = ' col-lg-' + this.labellg;
30429 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30432 if(this.labelmd > 0){
30433 labelCls = ' col-md-' + this.labelmd;
30434 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30437 if(this.labelsm > 0){
30438 labelCls = ' col-sm-' + this.labelsm;
30439 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30442 if(this.labelxs > 0){
30443 labelCls = ' col-xs-' + this.labelxs;
30444 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30448 label.cls += ' ' + labelCls;
30450 cfg.cn.push(label);
30453 Roo.each(['day', 'month', 'year'], function(t){
30456 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30463 inputEl: function ()
30465 return this.el.select('.roo-date-split-field-group-value', true).first();
30468 onRender : function(ct, position)
30472 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30474 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30476 this.dayField = new Roo.bootstrap.ComboBox({
30477 allowBlank : this.dayAllowBlank,
30478 alwaysQuery : true,
30479 displayField : 'value',
30482 forceSelection : true,
30484 placeholder : this.dayPlaceholder,
30485 selectOnFocus : true,
30486 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30487 triggerAction : 'all',
30489 valueField : 'value',
30490 store : new Roo.data.SimpleStore({
30491 data : (function() {
30493 _this.fireEvent('days', _this, days);
30496 fields : [ 'value' ]
30499 select : function (_self, record, index)
30501 _this.setValue(_this.getValue());
30506 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30508 this.monthField = new Roo.bootstrap.MonthField({
30509 after : '<i class=\"fa fa-calendar\"></i>',
30510 allowBlank : this.monthAllowBlank,
30511 placeholder : this.monthPlaceholder,
30514 render : function (_self)
30516 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30517 e.preventDefault();
30521 select : function (_self, oldvalue, newvalue)
30523 _this.setValue(_this.getValue());
30528 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30530 this.yearField = new Roo.bootstrap.ComboBox({
30531 allowBlank : this.yearAllowBlank,
30532 alwaysQuery : true,
30533 displayField : 'value',
30536 forceSelection : true,
30538 placeholder : this.yearPlaceholder,
30539 selectOnFocus : true,
30540 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30541 triggerAction : 'all',
30543 valueField : 'value',
30544 store : new Roo.data.SimpleStore({
30545 data : (function() {
30547 _this.fireEvent('years', _this, years);
30550 fields : [ 'value' ]
30553 select : function (_self, record, index)
30555 _this.setValue(_this.getValue());
30560 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30563 setValue : function(v, format)
30565 this.inputEl.dom.value = v;
30567 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30569 var d = Date.parseDate(v, f);
30576 this.setDay(d.format(this.dayFormat));
30577 this.setMonth(d.format(this.monthFormat));
30578 this.setYear(d.format(this.yearFormat));
30585 setDay : function(v)
30587 this.dayField.setValue(v);
30588 this.inputEl.dom.value = this.getValue();
30593 setMonth : function(v)
30595 this.monthField.setValue(v, true);
30596 this.inputEl.dom.value = this.getValue();
30601 setYear : function(v)
30603 this.yearField.setValue(v);
30604 this.inputEl.dom.value = this.getValue();
30609 getDay : function()
30611 return this.dayField.getValue();
30614 getMonth : function()
30616 return this.monthField.getValue();
30619 getYear : function()
30621 return this.yearField.getValue();
30624 getValue : function()
30626 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30628 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30638 this.inputEl.dom.value = '';
30643 validate : function()
30645 var d = this.dayField.validate();
30646 var m = this.monthField.validate();
30647 var y = this.yearField.validate();
30652 (!this.dayAllowBlank && !d) ||
30653 (!this.monthAllowBlank && !m) ||
30654 (!this.yearAllowBlank && !y)
30659 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30668 this.markInvalid();
30673 markValid : function()
30676 var label = this.el.select('label', true).first();
30677 var icon = this.el.select('i.fa-star', true).first();
30683 this.fireEvent('valid', this);
30687 * Mark this field as invalid
30688 * @param {String} msg The validation message
30690 markInvalid : function(msg)
30693 var label = this.el.select('label', true).first();
30694 var icon = this.el.select('i.fa-star', true).first();
30696 if(label && !icon){
30697 this.el.select('.roo-date-split-field-label', true).createChild({
30699 cls : 'text-danger fa fa-lg fa-star',
30700 tooltip : 'This field is required',
30701 style : 'margin-right:5px;'
30705 this.fireEvent('invalid', this, msg);
30708 clearInvalid : function()
30710 var label = this.el.select('label', true).first();
30711 var icon = this.el.select('i.fa-star', true).first();
30717 this.fireEvent('valid', this);
30720 getName: function()
30730 * http://masonry.desandro.com
30732 * The idea is to render all the bricks based on vertical width...
30734 * The original code extends 'outlayer' - we might need to use that....
30740 * @class Roo.bootstrap.LayoutMasonry
30741 * @extends Roo.bootstrap.Component
30742 * Bootstrap Layout Masonry class
30745 * Create a new Element
30746 * @param {Object} config The config object
30749 Roo.bootstrap.LayoutMasonry = function(config){
30751 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30755 Roo.bootstrap.LayoutMasonry.register(this);
30761 * Fire after layout the items
30762 * @param {Roo.bootstrap.LayoutMasonry} this
30763 * @param {Roo.EventObject} e
30770 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30773 * @cfg {Boolean} isLayoutInstant = no animation?
30775 isLayoutInstant : false, // needed?
30778 * @cfg {Number} boxWidth width of the columns
30783 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30788 * @cfg {Number} padWidth padding below box..
30793 * @cfg {Number} gutter gutter width..
30798 * @cfg {Number} maxCols maximum number of columns
30804 * @cfg {Boolean} isAutoInitial defalut true
30806 isAutoInitial : true,
30811 * @cfg {Boolean} isHorizontal defalut false
30813 isHorizontal : false,
30815 currentSize : null,
30821 bricks: null, //CompositeElement
30825 _isLayoutInited : false,
30827 // isAlternative : false, // only use for vertical layout...
30830 * @cfg {Number} alternativePadWidth padding below box..
30832 alternativePadWidth : 50,
30834 selectedBrick : [],
30836 getAutoCreate : function(){
30838 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30842 cls: 'blog-masonary-wrapper ' + this.cls,
30844 cls : 'mas-boxes masonary'
30851 getChildContainer: function( )
30853 if (this.boxesEl) {
30854 return this.boxesEl;
30857 this.boxesEl = this.el.select('.mas-boxes').first();
30859 return this.boxesEl;
30863 initEvents : function()
30867 if(this.isAutoInitial){
30868 Roo.log('hook children rendered');
30869 this.on('childrenrendered', function() {
30870 Roo.log('children rendered');
30876 initial : function()
30878 this.selectedBrick = [];
30880 this.currentSize = this.el.getBox(true);
30882 Roo.EventManager.onWindowResize(this.resize, this);
30884 if(!this.isAutoInitial){
30892 //this.layout.defer(500,this);
30896 resize : function()
30898 var cs = this.el.getBox(true);
30901 this.currentSize.width == cs.width &&
30902 this.currentSize.x == cs.x &&
30903 this.currentSize.height == cs.height &&
30904 this.currentSize.y == cs.y
30906 Roo.log("no change in with or X or Y");
30910 this.currentSize = cs;
30916 layout : function()
30918 this._resetLayout();
30920 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30922 this.layoutItems( isInstant );
30924 this._isLayoutInited = true;
30926 this.fireEvent('layout', this);
30930 _resetLayout : function()
30932 if(this.isHorizontal){
30933 this.horizontalMeasureColumns();
30937 this.verticalMeasureColumns();
30941 verticalMeasureColumns : function()
30943 this.getContainerWidth();
30945 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30946 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30950 var boxWidth = this.boxWidth + this.padWidth;
30952 if(this.containerWidth < this.boxWidth){
30953 boxWidth = this.containerWidth
30956 var containerWidth = this.containerWidth;
30958 var cols = Math.floor(containerWidth / boxWidth);
30960 this.cols = Math.max( cols, 1 );
30962 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30964 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30966 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30968 this.colWidth = boxWidth + avail - this.padWidth;
30970 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30971 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30974 horizontalMeasureColumns : function()
30976 this.getContainerWidth();
30978 var boxWidth = this.boxWidth;
30980 if(this.containerWidth < boxWidth){
30981 boxWidth = this.containerWidth;
30984 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30986 this.el.setHeight(boxWidth);
30990 getContainerWidth : function()
30992 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30995 layoutItems : function( isInstant )
30997 Roo.log(this.bricks);
30999 var items = Roo.apply([], this.bricks);
31001 if(this.isHorizontal){
31002 this._horizontalLayoutItems( items , isInstant );
31006 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31007 // this._verticalAlternativeLayoutItems( items , isInstant );
31011 this._verticalLayoutItems( items , isInstant );
31015 _verticalLayoutItems : function ( items , isInstant)
31017 if ( !items || !items.length ) {
31022 ['xs', 'xs', 'xs', 'tall'],
31023 ['xs', 'xs', 'tall'],
31024 ['xs', 'xs', 'sm'],
31025 ['xs', 'xs', 'xs'],
31031 ['sm', 'xs', 'xs'],
31035 ['tall', 'xs', 'xs', 'xs'],
31036 ['tall', 'xs', 'xs'],
31048 Roo.each(items, function(item, k){
31050 switch (item.size) {
31051 // these layouts take up a full box,
31062 boxes.push([item]);
31085 var filterPattern = function(box, length)
31093 var pattern = box.slice(0, length);
31097 Roo.each(pattern, function(i){
31098 format.push(i.size);
31101 Roo.each(standard, function(s){
31103 if(String(s) != String(format)){
31112 if(!match && length == 1){
31117 filterPattern(box, length - 1);
31121 queue.push(pattern);
31123 box = box.slice(length, box.length);
31125 filterPattern(box, 4);
31131 Roo.each(boxes, function(box, k){
31137 if(box.length == 1){
31142 filterPattern(box, 4);
31146 this._processVerticalLayoutQueue( queue, isInstant );
31150 // _verticalAlternativeLayoutItems : function( items , isInstant )
31152 // if ( !items || !items.length ) {
31156 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31160 _horizontalLayoutItems : function ( items , isInstant)
31162 if ( !items || !items.length || items.length < 3) {
31168 var eItems = items.slice(0, 3);
31170 items = items.slice(3, items.length);
31173 ['xs', 'xs', 'xs', 'wide'],
31174 ['xs', 'xs', 'wide'],
31175 ['xs', 'xs', 'sm'],
31176 ['xs', 'xs', 'xs'],
31182 ['sm', 'xs', 'xs'],
31186 ['wide', 'xs', 'xs', 'xs'],
31187 ['wide', 'xs', 'xs'],
31200 Roo.each(items, function(item, k){
31202 switch (item.size) {
31213 boxes.push([item]);
31237 var filterPattern = function(box, length)
31245 var pattern = box.slice(0, length);
31249 Roo.each(pattern, function(i){
31250 format.push(i.size);
31253 Roo.each(standard, function(s){
31255 if(String(s) != String(format)){
31264 if(!match && length == 1){
31269 filterPattern(box, length - 1);
31273 queue.push(pattern);
31275 box = box.slice(length, box.length);
31277 filterPattern(box, 4);
31283 Roo.each(boxes, function(box, k){
31289 if(box.length == 1){
31294 filterPattern(box, 4);
31301 var pos = this.el.getBox(true);
31305 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31307 var hit_end = false;
31309 Roo.each(queue, function(box){
31313 Roo.each(box, function(b){
31315 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31325 Roo.each(box, function(b){
31327 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31330 mx = Math.max(mx, b.x);
31334 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31338 Roo.each(box, function(b){
31340 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31354 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31357 /** Sets position of item in DOM
31358 * @param {Element} item
31359 * @param {Number} x - horizontal position
31360 * @param {Number} y - vertical position
31361 * @param {Boolean} isInstant - disables transitions
31363 _processVerticalLayoutQueue : function( queue, isInstant )
31365 var pos = this.el.getBox(true);
31370 for (var i = 0; i < this.cols; i++){
31374 Roo.each(queue, function(box, k){
31376 var col = k % this.cols;
31378 Roo.each(box, function(b,kk){
31380 b.el.position('absolute');
31382 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31383 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31385 if(b.size == 'md-left' || b.size == 'md-right'){
31386 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31387 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31390 b.el.setWidth(width);
31391 b.el.setHeight(height);
31393 b.el.select('iframe',true).setSize(width,height);
31397 for (var i = 0; i < this.cols; i++){
31399 if(maxY[i] < maxY[col]){
31404 col = Math.min(col, i);
31408 x = pos.x + col * (this.colWidth + this.padWidth);
31412 var positions = [];
31414 switch (box.length){
31416 positions = this.getVerticalOneBoxColPositions(x, y, box);
31419 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31422 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31425 positions = this.getVerticalFourBoxColPositions(x, y, box);
31431 Roo.each(box, function(b,kk){
31433 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31435 var sz = b.el.getSize();
31437 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31445 for (var i = 0; i < this.cols; i++){
31446 mY = Math.max(mY, maxY[i]);
31449 this.el.setHeight(mY - pos.y);
31453 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31455 // var pos = this.el.getBox(true);
31458 // var maxX = pos.right;
31460 // var maxHeight = 0;
31462 // Roo.each(items, function(item, k){
31466 // item.el.position('absolute');
31468 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31470 // item.el.setWidth(width);
31472 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31474 // item.el.setHeight(height);
31477 // item.el.setXY([x, y], isInstant ? false : true);
31479 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31482 // y = y + height + this.alternativePadWidth;
31484 // maxHeight = maxHeight + height + this.alternativePadWidth;
31488 // this.el.setHeight(maxHeight);
31492 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31494 var pos = this.el.getBox(true);
31499 var maxX = pos.right;
31501 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31503 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31505 Roo.each(queue, function(box, k){
31507 Roo.each(box, function(b, kk){
31509 b.el.position('absolute');
31511 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31512 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31514 if(b.size == 'md-left' || b.size == 'md-right'){
31515 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31516 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31519 b.el.setWidth(width);
31520 b.el.setHeight(height);
31528 var positions = [];
31530 switch (box.length){
31532 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31535 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31538 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31541 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31547 Roo.each(box, function(b,kk){
31549 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31551 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31559 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31561 Roo.each(eItems, function(b,k){
31563 b.size = (k == 0) ? 'sm' : 'xs';
31564 b.x = (k == 0) ? 2 : 1;
31565 b.y = (k == 0) ? 2 : 1;
31567 b.el.position('absolute');
31569 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31571 b.el.setWidth(width);
31573 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31575 b.el.setHeight(height);
31579 var positions = [];
31582 x : maxX - this.unitWidth * 2 - this.gutter,
31587 x : maxX - this.unitWidth,
31588 y : minY + (this.unitWidth + this.gutter) * 2
31592 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31596 Roo.each(eItems, function(b,k){
31598 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31604 getVerticalOneBoxColPositions : function(x, y, box)
31608 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31610 if(box[0].size == 'md-left'){
31614 if(box[0].size == 'md-right'){
31619 x : x + (this.unitWidth + this.gutter) * rand,
31626 getVerticalTwoBoxColPositions : function(x, y, box)
31630 if(box[0].size == 'xs'){
31634 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31638 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31652 x : x + (this.unitWidth + this.gutter) * 2,
31653 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31660 getVerticalThreeBoxColPositions : function(x, y, box)
31664 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31672 x : x + (this.unitWidth + this.gutter) * 1,
31677 x : x + (this.unitWidth + this.gutter) * 2,
31685 if(box[0].size == 'xs' && box[1].size == 'xs'){
31694 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31698 x : x + (this.unitWidth + this.gutter) * 1,
31712 x : x + (this.unitWidth + this.gutter) * 2,
31717 x : x + (this.unitWidth + this.gutter) * 2,
31718 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31725 getVerticalFourBoxColPositions : function(x, y, box)
31729 if(box[0].size == 'xs'){
31738 y : y + (this.unitHeight + this.gutter) * 1
31743 y : y + (this.unitHeight + this.gutter) * 2
31747 x : x + (this.unitWidth + this.gutter) * 1,
31761 x : x + (this.unitWidth + this.gutter) * 2,
31766 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31767 y : y + (this.unitHeight + this.gutter) * 1
31771 x : x + (this.unitWidth + this.gutter) * 2,
31772 y : y + (this.unitWidth + this.gutter) * 2
31779 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31783 if(box[0].size == 'md-left'){
31785 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31792 if(box[0].size == 'md-right'){
31794 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31795 y : minY + (this.unitWidth + this.gutter) * 1
31801 var rand = Math.floor(Math.random() * (4 - box[0].y));
31804 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31805 y : minY + (this.unitWidth + this.gutter) * rand
31812 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31816 if(box[0].size == 'xs'){
31819 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31824 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31825 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31833 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31838 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31839 y : minY + (this.unitWidth + this.gutter) * 2
31846 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31850 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31853 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31858 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31859 y : minY + (this.unitWidth + this.gutter) * 1
31863 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31864 y : minY + (this.unitWidth + this.gutter) * 2
31871 if(box[0].size == 'xs' && box[1].size == 'xs'){
31874 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31879 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31884 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31885 y : minY + (this.unitWidth + this.gutter) * 1
31893 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31898 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31899 y : minY + (this.unitWidth + this.gutter) * 2
31903 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31904 y : minY + (this.unitWidth + this.gutter) * 2
31911 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31915 if(box[0].size == 'xs'){
31918 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31923 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31928 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),
31933 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31934 y : minY + (this.unitWidth + this.gutter) * 1
31942 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31947 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31948 y : minY + (this.unitWidth + this.gutter) * 2
31952 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31953 y : minY + (this.unitWidth + this.gutter) * 2
31957 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),
31958 y : minY + (this.unitWidth + this.gutter) * 2
31966 * remove a Masonry Brick
31967 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31969 removeBrick : function(brick_id)
31975 for (var i = 0; i<this.bricks.length; i++) {
31976 if (this.bricks[i].id == brick_id) {
31977 this.bricks.splice(i,1);
31978 this.el.dom.removeChild(Roo.get(brick_id).dom);
31985 * adds a Masonry Brick
31986 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31988 addBrick : function(cfg)
31990 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31991 //this.register(cn);
31992 cn.parentId = this.id;
31993 cn.render(this.el);
31998 * register a Masonry Brick
31999 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32002 register : function(brick)
32004 this.bricks.push(brick);
32005 brick.masonryId = this.id;
32009 * clear all the Masonry Brick
32011 clearAll : function()
32014 //this.getChildContainer().dom.innerHTML = "";
32015 this.el.dom.innerHTML = '';
32018 getSelected : function()
32020 if (!this.selectedBrick) {
32024 return this.selectedBrick;
32028 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32032 * register a Masonry Layout
32033 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32036 register : function(layout)
32038 this.groups[layout.id] = layout;
32041 * fetch a Masonry Layout based on the masonry layout ID
32042 * @param {string} the masonry layout to add
32043 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32046 get: function(layout_id) {
32047 if (typeof(this.groups[layout_id]) == 'undefined') {
32050 return this.groups[layout_id] ;
32062 * http://masonry.desandro.com
32064 * The idea is to render all the bricks based on vertical width...
32066 * The original code extends 'outlayer' - we might need to use that....
32072 * @class Roo.bootstrap.LayoutMasonryAuto
32073 * @extends Roo.bootstrap.Component
32074 * Bootstrap Layout Masonry class
32077 * Create a new Element
32078 * @param {Object} config The config object
32081 Roo.bootstrap.LayoutMasonryAuto = function(config){
32082 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32085 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32088 * @cfg {Boolean} isFitWidth - resize the width..
32090 isFitWidth : false, // options..
32092 * @cfg {Boolean} isOriginLeft = left align?
32094 isOriginLeft : true,
32096 * @cfg {Boolean} isOriginTop = top align?
32098 isOriginTop : false,
32100 * @cfg {Boolean} isLayoutInstant = no animation?
32102 isLayoutInstant : false, // needed?
32104 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32106 isResizingContainer : true,
32108 * @cfg {Number} columnWidth width of the columns
32114 * @cfg {Number} maxCols maximum number of columns
32119 * @cfg {Number} padHeight padding below box..
32125 * @cfg {Boolean} isAutoInitial defalut true
32128 isAutoInitial : true,
32134 initialColumnWidth : 0,
32135 currentSize : null,
32137 colYs : null, // array.
32144 bricks: null, //CompositeElement
32145 cols : 0, // array?
32146 // element : null, // wrapped now this.el
32147 _isLayoutInited : null,
32150 getAutoCreate : function(){
32154 cls: 'blog-masonary-wrapper ' + this.cls,
32156 cls : 'mas-boxes masonary'
32163 getChildContainer: function( )
32165 if (this.boxesEl) {
32166 return this.boxesEl;
32169 this.boxesEl = this.el.select('.mas-boxes').first();
32171 return this.boxesEl;
32175 initEvents : function()
32179 if(this.isAutoInitial){
32180 Roo.log('hook children rendered');
32181 this.on('childrenrendered', function() {
32182 Roo.log('children rendered');
32189 initial : function()
32191 this.reloadItems();
32193 this.currentSize = this.el.getBox(true);
32195 /// was window resize... - let's see if this works..
32196 Roo.EventManager.onWindowResize(this.resize, this);
32198 if(!this.isAutoInitial){
32203 this.layout.defer(500,this);
32206 reloadItems: function()
32208 this.bricks = this.el.select('.masonry-brick', true);
32210 this.bricks.each(function(b) {
32211 //Roo.log(b.getSize());
32212 if (!b.attr('originalwidth')) {
32213 b.attr('originalwidth', b.getSize().width);
32218 Roo.log(this.bricks.elements.length);
32221 resize : function()
32224 var cs = this.el.getBox(true);
32226 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32227 Roo.log("no change in with or X");
32230 this.currentSize = cs;
32234 layout : function()
32237 this._resetLayout();
32238 //this._manageStamps();
32240 // don't animate first layout
32241 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32242 this.layoutItems( isInstant );
32244 // flag for initalized
32245 this._isLayoutInited = true;
32248 layoutItems : function( isInstant )
32250 //var items = this._getItemsForLayout( this.items );
32251 // original code supports filtering layout items.. we just ignore it..
32253 this._layoutItems( this.bricks , isInstant );
32255 this._postLayout();
32257 _layoutItems : function ( items , isInstant)
32259 //this.fireEvent( 'layout', this, items );
32262 if ( !items || !items.elements.length ) {
32263 // no items, emit event with empty array
32268 items.each(function(item) {
32269 Roo.log("layout item");
32271 // get x/y object from method
32272 var position = this._getItemLayoutPosition( item );
32274 position.item = item;
32275 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32276 queue.push( position );
32279 this._processLayoutQueue( queue );
32281 /** Sets position of item in DOM
32282 * @param {Element} item
32283 * @param {Number} x - horizontal position
32284 * @param {Number} y - vertical position
32285 * @param {Boolean} isInstant - disables transitions
32287 _processLayoutQueue : function( queue )
32289 for ( var i=0, len = queue.length; i < len; i++ ) {
32290 var obj = queue[i];
32291 obj.item.position('absolute');
32292 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32298 * Any logic you want to do after each layout,
32299 * i.e. size the container
32301 _postLayout : function()
32303 this.resizeContainer();
32306 resizeContainer : function()
32308 if ( !this.isResizingContainer ) {
32311 var size = this._getContainerSize();
32313 this.el.setSize(size.width,size.height);
32314 this.boxesEl.setSize(size.width,size.height);
32320 _resetLayout : function()
32322 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32323 this.colWidth = this.el.getWidth();
32324 //this.gutter = this.el.getWidth();
32326 this.measureColumns();
32332 this.colYs.push( 0 );
32338 measureColumns : function()
32340 this.getContainerWidth();
32341 // if columnWidth is 0, default to outerWidth of first item
32342 if ( !this.columnWidth ) {
32343 var firstItem = this.bricks.first();
32344 Roo.log(firstItem);
32345 this.columnWidth = this.containerWidth;
32346 if (firstItem && firstItem.attr('originalwidth') ) {
32347 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32349 // columnWidth fall back to item of first element
32350 Roo.log("set column width?");
32351 this.initialColumnWidth = this.columnWidth ;
32353 // if first elem has no width, default to size of container
32358 if (this.initialColumnWidth) {
32359 this.columnWidth = this.initialColumnWidth;
32364 // column width is fixed at the top - however if container width get's smaller we should
32367 // this bit calcs how man columns..
32369 var columnWidth = this.columnWidth += this.gutter;
32371 // calculate columns
32372 var containerWidth = this.containerWidth + this.gutter;
32374 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32375 // fix rounding errors, typically with gutters
32376 var excess = columnWidth - containerWidth % columnWidth;
32379 // if overshoot is less than a pixel, round up, otherwise floor it
32380 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32381 cols = Math[ mathMethod ]( cols );
32382 this.cols = Math.max( cols, 1 );
32383 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32385 // padding positioning..
32386 var totalColWidth = this.cols * this.columnWidth;
32387 var padavail = this.containerWidth - totalColWidth;
32388 // so for 2 columns - we need 3 'pads'
32390 var padNeeded = (1+this.cols) * this.padWidth;
32392 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32394 this.columnWidth += padExtra
32395 //this.padWidth = Math.floor(padavail / ( this.cols));
32397 // adjust colum width so that padding is fixed??
32399 // we have 3 columns ... total = width * 3
32400 // we have X left over... that should be used by
32402 //if (this.expandC) {
32410 getContainerWidth : function()
32412 /* // container is parent if fit width
32413 var container = this.isFitWidth ? this.element.parentNode : this.element;
32414 // check that this.size and size are there
32415 // IE8 triggers resize on body size change, so they might not be
32417 var size = getSize( container ); //FIXME
32418 this.containerWidth = size && size.innerWidth; //FIXME
32421 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32425 _getItemLayoutPosition : function( item ) // what is item?
32427 // we resize the item to our columnWidth..
32429 item.setWidth(this.columnWidth);
32430 item.autoBoxAdjust = false;
32432 var sz = item.getSize();
32434 // how many columns does this brick span
32435 var remainder = this.containerWidth % this.columnWidth;
32437 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32438 // round if off by 1 pixel, otherwise use ceil
32439 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32440 colSpan = Math.min( colSpan, this.cols );
32442 // normally this should be '1' as we dont' currently allow multi width columns..
32444 var colGroup = this._getColGroup( colSpan );
32445 // get the minimum Y value from the columns
32446 var minimumY = Math.min.apply( Math, colGroup );
32447 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32449 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32451 // position the brick
32453 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32454 y: this.currentSize.y + minimumY + this.padHeight
32458 // apply setHeight to necessary columns
32459 var setHeight = minimumY + sz.height + this.padHeight;
32460 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32462 var setSpan = this.cols + 1 - colGroup.length;
32463 for ( var i = 0; i < setSpan; i++ ) {
32464 this.colYs[ shortColIndex + i ] = setHeight ;
32471 * @param {Number} colSpan - number of columns the element spans
32472 * @returns {Array} colGroup
32474 _getColGroup : function( colSpan )
32476 if ( colSpan < 2 ) {
32477 // if brick spans only one column, use all the column Ys
32482 // how many different places could this brick fit horizontally
32483 var groupCount = this.cols + 1 - colSpan;
32484 // for each group potential horizontal position
32485 for ( var i = 0; i < groupCount; i++ ) {
32486 // make an array of colY values for that one group
32487 var groupColYs = this.colYs.slice( i, i + colSpan );
32488 // and get the max value of the array
32489 colGroup[i] = Math.max.apply( Math, groupColYs );
32494 _manageStamp : function( stamp )
32496 var stampSize = stamp.getSize();
32497 var offset = stamp.getBox();
32498 // get the columns that this stamp affects
32499 var firstX = this.isOriginLeft ? offset.x : offset.right;
32500 var lastX = firstX + stampSize.width;
32501 var firstCol = Math.floor( firstX / this.columnWidth );
32502 firstCol = Math.max( 0, firstCol );
32504 var lastCol = Math.floor( lastX / this.columnWidth );
32505 // lastCol should not go over if multiple of columnWidth #425
32506 lastCol -= lastX % this.columnWidth ? 0 : 1;
32507 lastCol = Math.min( this.cols - 1, lastCol );
32509 // set colYs to bottom of the stamp
32510 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32513 for ( var i = firstCol; i <= lastCol; i++ ) {
32514 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32519 _getContainerSize : function()
32521 this.maxY = Math.max.apply( Math, this.colYs );
32526 if ( this.isFitWidth ) {
32527 size.width = this._getContainerFitWidth();
32533 _getContainerFitWidth : function()
32535 var unusedCols = 0;
32536 // count unused columns
32539 if ( this.colYs[i] !== 0 ) {
32544 // fit container to columns that have been used
32545 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32548 needsResizeLayout : function()
32550 var previousWidth = this.containerWidth;
32551 this.getContainerWidth();
32552 return previousWidth !== this.containerWidth;
32567 * @class Roo.bootstrap.MasonryBrick
32568 * @extends Roo.bootstrap.Component
32569 * Bootstrap MasonryBrick class
32572 * Create a new MasonryBrick
32573 * @param {Object} config The config object
32576 Roo.bootstrap.MasonryBrick = function(config){
32578 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32580 Roo.bootstrap.MasonryBrick.register(this);
32586 * When a MasonryBrick is clcik
32587 * @param {Roo.bootstrap.MasonryBrick} this
32588 * @param {Roo.EventObject} e
32594 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32597 * @cfg {String} title
32601 * @cfg {String} html
32605 * @cfg {String} bgimage
32609 * @cfg {String} videourl
32613 * @cfg {String} cls
32617 * @cfg {String} href
32621 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32626 * @cfg {String} placetitle (center|bottom)
32631 * @cfg {Boolean} isFitContainer defalut true
32633 isFitContainer : true,
32636 * @cfg {Boolean} preventDefault defalut false
32638 preventDefault : false,
32641 * @cfg {Boolean} inverse defalut false
32643 maskInverse : false,
32645 getAutoCreate : function()
32647 if(!this.isFitContainer){
32648 return this.getSplitAutoCreate();
32651 var cls = 'masonry-brick masonry-brick-full';
32653 if(this.href.length){
32654 cls += ' masonry-brick-link';
32657 if(this.bgimage.length){
32658 cls += ' masonry-brick-image';
32661 if(this.maskInverse){
32662 cls += ' mask-inverse';
32665 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32666 cls += ' enable-mask';
32670 cls += ' masonry-' + this.size + '-brick';
32673 if(this.placetitle.length){
32675 switch (this.placetitle) {
32677 cls += ' masonry-center-title';
32680 cls += ' masonry-bottom-title';
32687 if(!this.html.length && !this.bgimage.length){
32688 cls += ' masonry-center-title';
32691 if(!this.html.length && this.bgimage.length){
32692 cls += ' masonry-bottom-title';
32697 cls += ' ' + this.cls;
32701 tag: (this.href.length) ? 'a' : 'div',
32706 cls: 'masonry-brick-mask'
32710 cls: 'masonry-brick-paragraph',
32716 if(this.href.length){
32717 cfg.href = this.href;
32720 var cn = cfg.cn[1].cn;
32722 if(this.title.length){
32725 cls: 'masonry-brick-title',
32730 if(this.html.length){
32733 cls: 'masonry-brick-text',
32738 if (!this.title.length && !this.html.length) {
32739 cfg.cn[1].cls += ' hide';
32742 if(this.bgimage.length){
32745 cls: 'masonry-brick-image-view',
32750 if(this.videourl.length){
32751 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32752 // youtube support only?
32755 cls: 'masonry-brick-image-view',
32758 allowfullscreen : true
32766 getSplitAutoCreate : function()
32768 var cls = 'masonry-brick masonry-brick-split';
32770 if(this.href.length){
32771 cls += ' masonry-brick-link';
32774 if(this.bgimage.length){
32775 cls += ' masonry-brick-image';
32779 cls += ' masonry-' + this.size + '-brick';
32782 switch (this.placetitle) {
32784 cls += ' masonry-center-title';
32787 cls += ' masonry-bottom-title';
32790 if(!this.bgimage.length){
32791 cls += ' masonry-center-title';
32794 if(this.bgimage.length){
32795 cls += ' masonry-bottom-title';
32801 cls += ' ' + this.cls;
32805 tag: (this.href.length) ? 'a' : 'div',
32810 cls: 'masonry-brick-split-head',
32814 cls: 'masonry-brick-paragraph',
32821 cls: 'masonry-brick-split-body',
32827 if(this.href.length){
32828 cfg.href = this.href;
32831 if(this.title.length){
32832 cfg.cn[0].cn[0].cn.push({
32834 cls: 'masonry-brick-title',
32839 if(this.html.length){
32840 cfg.cn[1].cn.push({
32842 cls: 'masonry-brick-text',
32847 if(this.bgimage.length){
32848 cfg.cn[0].cn.push({
32850 cls: 'masonry-brick-image-view',
32855 if(this.videourl.length){
32856 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32857 // youtube support only?
32858 cfg.cn[0].cn.cn.push({
32860 cls: 'masonry-brick-image-view',
32863 allowfullscreen : true
32870 initEvents: function()
32872 switch (this.size) {
32905 this.el.on('touchstart', this.onTouchStart, this);
32906 this.el.on('touchmove', this.onTouchMove, this);
32907 this.el.on('touchend', this.onTouchEnd, this);
32908 this.el.on('contextmenu', this.onContextMenu, this);
32910 this.el.on('mouseenter' ,this.enter, this);
32911 this.el.on('mouseleave', this.leave, this);
32912 this.el.on('click', this.onClick, this);
32915 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32916 this.parent().bricks.push(this);
32921 onClick: function(e, el)
32923 var time = this.endTimer - this.startTimer;
32924 // Roo.log(e.preventDefault());
32927 e.preventDefault();
32932 if(!this.preventDefault){
32936 e.preventDefault();
32938 if (this.activeClass != '') {
32939 this.selectBrick();
32942 this.fireEvent('click', this, e);
32945 enter: function(e, el)
32947 e.preventDefault();
32949 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32953 if(this.bgimage.length && this.html.length){
32954 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32958 leave: function(e, el)
32960 e.preventDefault();
32962 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32966 if(this.bgimage.length && this.html.length){
32967 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32971 onTouchStart: function(e, el)
32973 // e.preventDefault();
32975 this.touchmoved = false;
32977 if(!this.isFitContainer){
32981 if(!this.bgimage.length || !this.html.length){
32985 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32987 this.timer = new Date().getTime();
32991 onTouchMove: function(e, el)
32993 this.touchmoved = true;
32996 onContextMenu : function(e,el)
32998 e.preventDefault();
32999 e.stopPropagation();
33003 onTouchEnd: function(e, el)
33005 // e.preventDefault();
33007 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33014 if(!this.bgimage.length || !this.html.length){
33016 if(this.href.length){
33017 window.location.href = this.href;
33023 if(!this.isFitContainer){
33027 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33029 window.location.href = this.href;
33032 //selection on single brick only
33033 selectBrick : function() {
33035 if (!this.parentId) {
33039 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33040 var index = m.selectedBrick.indexOf(this.id);
33043 m.selectedBrick.splice(index,1);
33044 this.el.removeClass(this.activeClass);
33048 for(var i = 0; i < m.selectedBrick.length; i++) {
33049 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33050 b.el.removeClass(b.activeClass);
33053 m.selectedBrick = [];
33055 m.selectedBrick.push(this.id);
33056 this.el.addClass(this.activeClass);
33060 isSelected : function(){
33061 return this.el.hasClass(this.activeClass);
33066 Roo.apply(Roo.bootstrap.MasonryBrick, {
33069 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33071 * register a Masonry Brick
33072 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33075 register : function(brick)
33077 //this.groups[brick.id] = brick;
33078 this.groups.add(brick.id, brick);
33081 * fetch a masonry brick based on the masonry brick ID
33082 * @param {string} the masonry brick to add
33083 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33086 get: function(brick_id)
33088 // if (typeof(this.groups[brick_id]) == 'undefined') {
33091 // return this.groups[brick_id] ;
33093 if(this.groups.key(brick_id)) {
33094 return this.groups.key(brick_id);
33112 * @class Roo.bootstrap.Brick
33113 * @extends Roo.bootstrap.Component
33114 * Bootstrap Brick class
33117 * Create a new Brick
33118 * @param {Object} config The config object
33121 Roo.bootstrap.Brick = function(config){
33122 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33128 * When a Brick is click
33129 * @param {Roo.bootstrap.Brick} this
33130 * @param {Roo.EventObject} e
33136 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33139 * @cfg {String} title
33143 * @cfg {String} html
33147 * @cfg {String} bgimage
33151 * @cfg {String} cls
33155 * @cfg {String} href
33159 * @cfg {String} video
33163 * @cfg {Boolean} square
33167 getAutoCreate : function()
33169 var cls = 'roo-brick';
33171 if(this.href.length){
33172 cls += ' roo-brick-link';
33175 if(this.bgimage.length){
33176 cls += ' roo-brick-image';
33179 if(!this.html.length && !this.bgimage.length){
33180 cls += ' roo-brick-center-title';
33183 if(!this.html.length && this.bgimage.length){
33184 cls += ' roo-brick-bottom-title';
33188 cls += ' ' + this.cls;
33192 tag: (this.href.length) ? 'a' : 'div',
33197 cls: 'roo-brick-paragraph',
33203 if(this.href.length){
33204 cfg.href = this.href;
33207 var cn = cfg.cn[0].cn;
33209 if(this.title.length){
33212 cls: 'roo-brick-title',
33217 if(this.html.length){
33220 cls: 'roo-brick-text',
33227 if(this.bgimage.length){
33230 cls: 'roo-brick-image-view',
33238 initEvents: function()
33240 if(this.title.length || this.html.length){
33241 this.el.on('mouseenter' ,this.enter, this);
33242 this.el.on('mouseleave', this.leave, this);
33245 Roo.EventManager.onWindowResize(this.resize, this);
33247 if(this.bgimage.length){
33248 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33249 this.imageEl.on('load', this.onImageLoad, this);
33256 onImageLoad : function()
33261 resize : function()
33263 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33265 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33267 if(this.bgimage.length){
33268 var image = this.el.select('.roo-brick-image-view', true).first();
33270 image.setWidth(paragraph.getWidth());
33273 image.setHeight(paragraph.getWidth());
33276 this.el.setHeight(image.getHeight());
33277 paragraph.setHeight(image.getHeight());
33283 enter: function(e, el)
33285 e.preventDefault();
33287 if(this.bgimage.length){
33288 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33289 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33293 leave: function(e, el)
33295 e.preventDefault();
33297 if(this.bgimage.length){
33298 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33299 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33314 * @class Roo.bootstrap.NumberField
33315 * @extends Roo.bootstrap.Input
33316 * Bootstrap NumberField class
33322 * Create a new NumberField
33323 * @param {Object} config The config object
33326 Roo.bootstrap.NumberField = function(config){
33327 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33330 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33333 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33335 allowDecimals : true,
33337 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33339 decimalSeparator : ".",
33341 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33343 decimalPrecision : 2,
33345 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33347 allowNegative : true,
33350 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33354 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33356 minValue : Number.NEGATIVE_INFINITY,
33358 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33360 maxValue : Number.MAX_VALUE,
33362 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33364 minText : "The minimum value for this field is {0}",
33366 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33368 maxText : "The maximum value for this field is {0}",
33370 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33371 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33373 nanText : "{0} is not a valid number",
33375 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33377 thousandsDelimiter : false,
33379 * @cfg {String} valueAlign alignment of value
33381 valueAlign : "left",
33383 getAutoCreate : function()
33385 var hiddenInput = {
33389 cls: 'hidden-number-input'
33393 hiddenInput.name = this.name;
33398 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33400 this.name = hiddenInput.name;
33402 if(cfg.cn.length > 0) {
33403 cfg.cn.push(hiddenInput);
33410 initEvents : function()
33412 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33414 var allowed = "0123456789";
33416 if(this.allowDecimals){
33417 allowed += this.decimalSeparator;
33420 if(this.allowNegative){
33424 if(this.thousandsDelimiter) {
33428 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33430 var keyPress = function(e){
33432 var k = e.getKey();
33434 var c = e.getCharCode();
33437 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33438 allowed.indexOf(String.fromCharCode(c)) === -1
33444 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33448 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33453 this.el.on("keypress", keyPress, this);
33456 validateValue : function(value)
33459 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33463 var num = this.parseValue(value);
33466 this.markInvalid(String.format(this.nanText, value));
33470 if(num < this.minValue){
33471 this.markInvalid(String.format(this.minText, this.minValue));
33475 if(num > this.maxValue){
33476 this.markInvalid(String.format(this.maxText, this.maxValue));
33483 getValue : function()
33485 var v = this.hiddenEl().getValue();
33487 return this.fixPrecision(this.parseValue(v));
33490 parseValue : function(value)
33492 if(this.thousandsDelimiter) {
33494 r = new RegExp(",", "g");
33495 value = value.replace(r, "");
33498 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33499 return isNaN(value) ? '' : value;
33502 fixPrecision : function(value)
33504 if(this.thousandsDelimiter) {
33506 r = new RegExp(",", "g");
33507 value = value.replace(r, "");
33510 var nan = isNaN(value);
33512 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33513 return nan ? '' : value;
33515 return parseFloat(value).toFixed(this.decimalPrecision);
33518 setValue : function(v)
33520 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33526 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33528 this.inputEl().dom.value = (v == '') ? '' :
33529 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33531 if(!this.allowZero && v === '0') {
33532 this.hiddenEl().dom.value = '';
33533 this.inputEl().dom.value = '';
33540 decimalPrecisionFcn : function(v)
33542 return Math.floor(v);
33545 beforeBlur : function()
33547 var v = this.parseValue(this.getRawValue());
33549 if(v || v === 0 || v === ''){
33554 hiddenEl : function()
33556 return this.el.select('input.hidden-number-input',true).first();
33568 * @class Roo.bootstrap.DocumentSlider
33569 * @extends Roo.bootstrap.Component
33570 * Bootstrap DocumentSlider class
33573 * Create a new DocumentViewer
33574 * @param {Object} config The config object
33577 Roo.bootstrap.DocumentSlider = function(config){
33578 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33585 * Fire after initEvent
33586 * @param {Roo.bootstrap.DocumentSlider} this
33591 * Fire after update
33592 * @param {Roo.bootstrap.DocumentSlider} this
33598 * @param {Roo.bootstrap.DocumentSlider} this
33604 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33610 getAutoCreate : function()
33614 cls : 'roo-document-slider',
33618 cls : 'roo-document-slider-header',
33622 cls : 'roo-document-slider-header-title'
33628 cls : 'roo-document-slider-body',
33632 cls : 'roo-document-slider-prev',
33636 cls : 'fa fa-chevron-left'
33642 cls : 'roo-document-slider-thumb',
33646 cls : 'roo-document-slider-image'
33652 cls : 'roo-document-slider-next',
33656 cls : 'fa fa-chevron-right'
33668 initEvents : function()
33670 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33671 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33673 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33674 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33676 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33677 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33679 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33680 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33682 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33683 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33685 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33686 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33688 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33689 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33691 this.thumbEl.on('click', this.onClick, this);
33693 this.prevIndicator.on('click', this.prev, this);
33695 this.nextIndicator.on('click', this.next, this);
33699 initial : function()
33701 if(this.files.length){
33702 this.indicator = 1;
33706 this.fireEvent('initial', this);
33709 update : function()
33711 this.imageEl.attr('src', this.files[this.indicator - 1]);
33713 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33715 this.prevIndicator.show();
33717 if(this.indicator == 1){
33718 this.prevIndicator.hide();
33721 this.nextIndicator.show();
33723 if(this.indicator == this.files.length){
33724 this.nextIndicator.hide();
33727 this.thumbEl.scrollTo('top');
33729 this.fireEvent('update', this);
33732 onClick : function(e)
33734 e.preventDefault();
33736 this.fireEvent('click', this);
33741 e.preventDefault();
33743 this.indicator = Math.max(1, this.indicator - 1);
33750 e.preventDefault();
33752 this.indicator = Math.min(this.files.length, this.indicator + 1);
33766 * @class Roo.bootstrap.RadioSet
33767 * @extends Roo.bootstrap.Input
33768 * Bootstrap RadioSet class
33769 * @cfg {String} indicatorpos (left|right) default left
33770 * @cfg {Boolean} inline (true|false) inline the element (default true)
33771 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33773 * Create a new RadioSet
33774 * @param {Object} config The config object
33777 Roo.bootstrap.RadioSet = function(config){
33779 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33783 Roo.bootstrap.RadioSet.register(this);
33788 * Fires when the element is checked or unchecked.
33789 * @param {Roo.bootstrap.RadioSet} this This radio
33790 * @param {Roo.bootstrap.Radio} item The checked item
33795 * Fires when the element is click.
33796 * @param {Roo.bootstrap.RadioSet} this This radio set
33797 * @param {Roo.bootstrap.Radio} item The checked item
33798 * @param {Roo.EventObject} e The event object
33805 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33813 indicatorpos : 'left',
33815 getAutoCreate : function()
33819 cls : 'roo-radio-set-label',
33823 html : this.fieldLabel
33828 if(this.indicatorpos == 'left'){
33831 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33832 tooltip : 'This field is required'
33837 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33838 tooltip : 'This field is required'
33844 cls : 'roo-radio-set-items'
33847 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33849 if (align === 'left' && this.fieldLabel.length) {
33852 cls : "roo-radio-set-right",
33858 if(this.labelWidth > 12){
33859 label.style = "width: " + this.labelWidth + 'px';
33862 if(this.labelWidth < 13 && this.labelmd == 0){
33863 this.labelmd = this.labelWidth;
33866 if(this.labellg > 0){
33867 label.cls += ' col-lg-' + this.labellg;
33868 items.cls += ' col-lg-' + (12 - this.labellg);
33871 if(this.labelmd > 0){
33872 label.cls += ' col-md-' + this.labelmd;
33873 items.cls += ' col-md-' + (12 - this.labelmd);
33876 if(this.labelsm > 0){
33877 label.cls += ' col-sm-' + this.labelsm;
33878 items.cls += ' col-sm-' + (12 - this.labelsm);
33881 if(this.labelxs > 0){
33882 label.cls += ' col-xs-' + this.labelxs;
33883 items.cls += ' col-xs-' + (12 - this.labelxs);
33889 cls : 'roo-radio-set',
33893 cls : 'roo-radio-set-input',
33896 value : this.value ? this.value : ''
33903 if(this.weight.length){
33904 cfg.cls += ' roo-radio-' + this.weight;
33908 cfg.cls += ' roo-radio-set-inline';
33912 ['xs','sm','md','lg'].map(function(size){
33913 if (settings[size]) {
33914 cfg.cls += ' col-' + size + '-' + settings[size];
33922 initEvents : function()
33924 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33925 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33927 if(!this.fieldLabel.length){
33928 this.labelEl.hide();
33931 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33932 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33934 this.indicator = this.indicatorEl();
33936 if(this.indicator){
33937 this.indicator.addClass('invisible');
33940 this.originalValue = this.getValue();
33944 inputEl: function ()
33946 return this.el.select('.roo-radio-set-input', true).first();
33949 getChildContainer : function()
33951 return this.itemsEl;
33954 register : function(item)
33956 this.radioes.push(item);
33960 validate : function()
33962 if(this.getVisibilityEl().hasClass('hidden')){
33968 Roo.each(this.radioes, function(i){
33977 if(this.allowBlank) {
33981 if(this.disabled || valid){
33986 this.markInvalid();
33991 markValid : function()
33993 if(this.labelEl.isVisible(true)){
33994 this.indicatorEl().removeClass('visible');
33995 this.indicatorEl().addClass('invisible');
33998 this.el.removeClass([this.invalidClass, this.validClass]);
33999 this.el.addClass(this.validClass);
34001 this.fireEvent('valid', this);
34004 markInvalid : function(msg)
34006 if(this.allowBlank || this.disabled){
34010 if(this.labelEl.isVisible(true)){
34011 this.indicatorEl().removeClass('invisible');
34012 this.indicatorEl().addClass('visible');
34015 this.el.removeClass([this.invalidClass, this.validClass]);
34016 this.el.addClass(this.invalidClass);
34018 this.fireEvent('invalid', this, msg);
34022 setValue : function(v, suppressEvent)
34024 if(this.value === v){
34031 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34034 Roo.each(this.radioes, function(i){
34036 i.el.removeClass('checked');
34039 Roo.each(this.radioes, function(i){
34041 if(i.value === v || i.value.toString() === v.toString()){
34043 i.el.addClass('checked');
34045 if(suppressEvent !== true){
34046 this.fireEvent('check', this, i);
34057 clearInvalid : function(){
34059 if(!this.el || this.preventMark){
34063 this.el.removeClass([this.invalidClass]);
34065 this.fireEvent('valid', this);
34070 Roo.apply(Roo.bootstrap.RadioSet, {
34074 register : function(set)
34076 this.groups[set.name] = set;
34079 get: function(name)
34081 if (typeof(this.groups[name]) == 'undefined') {
34085 return this.groups[name] ;
34091 * Ext JS Library 1.1.1
34092 * Copyright(c) 2006-2007, Ext JS, LLC.
34094 * Originally Released Under LGPL - original licence link has changed is not relivant.
34097 * <script type="text/javascript">
34102 * @class Roo.bootstrap.SplitBar
34103 * @extends Roo.util.Observable
34104 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34108 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34109 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34110 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34111 split.minSize = 100;
34112 split.maxSize = 600;
34113 split.animate = true;
34114 split.on('moved', splitterMoved);
34117 * Create a new SplitBar
34118 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34119 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34120 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34121 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34122 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34123 position of the SplitBar).
34125 Roo.bootstrap.SplitBar = function(cfg){
34130 // dragElement : elm
34131 // resizingElement: el,
34133 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34134 // placement : Roo.bootstrap.SplitBar.LEFT ,
34135 // existingProxy ???
34138 this.el = Roo.get(cfg.dragElement, true);
34139 this.el.dom.unselectable = "on";
34141 this.resizingEl = Roo.get(cfg.resizingElement, true);
34145 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34146 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34149 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34152 * The minimum size of the resizing element. (Defaults to 0)
34158 * The maximum size of the resizing element. (Defaults to 2000)
34161 this.maxSize = 2000;
34164 * Whether to animate the transition to the new size
34167 this.animate = false;
34170 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34173 this.useShim = false;
34178 if(!cfg.existingProxy){
34180 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34182 this.proxy = Roo.get(cfg.existingProxy).dom;
34185 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34188 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34191 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34194 this.dragSpecs = {};
34197 * @private The adapter to use to positon and resize elements
34199 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34200 this.adapter.init(this);
34202 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34204 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34205 this.el.addClass("roo-splitbar-h");
34208 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34209 this.el.addClass("roo-splitbar-v");
34215 * Fires when the splitter is moved (alias for {@link #event-moved})
34216 * @param {Roo.bootstrap.SplitBar} this
34217 * @param {Number} newSize the new width or height
34222 * Fires when the splitter is moved
34223 * @param {Roo.bootstrap.SplitBar} this
34224 * @param {Number} newSize the new width or height
34228 * @event beforeresize
34229 * Fires before the splitter is dragged
34230 * @param {Roo.bootstrap.SplitBar} this
34232 "beforeresize" : true,
34234 "beforeapply" : true
34237 Roo.util.Observable.call(this);
34240 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34241 onStartProxyDrag : function(x, y){
34242 this.fireEvent("beforeresize", this);
34244 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34246 o.enableDisplayMode("block");
34247 // all splitbars share the same overlay
34248 Roo.bootstrap.SplitBar.prototype.overlay = o;
34250 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34251 this.overlay.show();
34252 Roo.get(this.proxy).setDisplayed("block");
34253 var size = this.adapter.getElementSize(this);
34254 this.activeMinSize = this.getMinimumSize();;
34255 this.activeMaxSize = this.getMaximumSize();;
34256 var c1 = size - this.activeMinSize;
34257 var c2 = Math.max(this.activeMaxSize - size, 0);
34258 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34259 this.dd.resetConstraints();
34260 this.dd.setXConstraint(
34261 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34262 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34264 this.dd.setYConstraint(0, 0);
34266 this.dd.resetConstraints();
34267 this.dd.setXConstraint(0, 0);
34268 this.dd.setYConstraint(
34269 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34270 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34273 this.dragSpecs.startSize = size;
34274 this.dragSpecs.startPoint = [x, y];
34275 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34279 * @private Called after the drag operation by the DDProxy
34281 onEndProxyDrag : function(e){
34282 Roo.get(this.proxy).setDisplayed(false);
34283 var endPoint = Roo.lib.Event.getXY(e);
34285 this.overlay.hide();
34288 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34289 newSize = this.dragSpecs.startSize +
34290 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34291 endPoint[0] - this.dragSpecs.startPoint[0] :
34292 this.dragSpecs.startPoint[0] - endPoint[0]
34295 newSize = this.dragSpecs.startSize +
34296 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34297 endPoint[1] - this.dragSpecs.startPoint[1] :
34298 this.dragSpecs.startPoint[1] - endPoint[1]
34301 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34302 if(newSize != this.dragSpecs.startSize){
34303 if(this.fireEvent('beforeapply', this, newSize) !== false){
34304 this.adapter.setElementSize(this, newSize);
34305 this.fireEvent("moved", this, newSize);
34306 this.fireEvent("resize", this, newSize);
34312 * Get the adapter this SplitBar uses
34313 * @return The adapter object
34315 getAdapter : function(){
34316 return this.adapter;
34320 * Set the adapter this SplitBar uses
34321 * @param {Object} adapter A SplitBar adapter object
34323 setAdapter : function(adapter){
34324 this.adapter = adapter;
34325 this.adapter.init(this);
34329 * Gets the minimum size for the resizing element
34330 * @return {Number} The minimum size
34332 getMinimumSize : function(){
34333 return this.minSize;
34337 * Sets the minimum size for the resizing element
34338 * @param {Number} minSize The minimum size
34340 setMinimumSize : function(minSize){
34341 this.minSize = minSize;
34345 * Gets the maximum size for the resizing element
34346 * @return {Number} The maximum size
34348 getMaximumSize : function(){
34349 return this.maxSize;
34353 * Sets the maximum size for the resizing element
34354 * @param {Number} maxSize The maximum size
34356 setMaximumSize : function(maxSize){
34357 this.maxSize = maxSize;
34361 * Sets the initialize size for the resizing element
34362 * @param {Number} size The initial size
34364 setCurrentSize : function(size){
34365 var oldAnimate = this.animate;
34366 this.animate = false;
34367 this.adapter.setElementSize(this, size);
34368 this.animate = oldAnimate;
34372 * Destroy this splitbar.
34373 * @param {Boolean} removeEl True to remove the element
34375 destroy : function(removeEl){
34377 this.shim.remove();
34380 this.proxy.parentNode.removeChild(this.proxy);
34388 * @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.
34390 Roo.bootstrap.SplitBar.createProxy = function(dir){
34391 var proxy = new Roo.Element(document.createElement("div"));
34392 proxy.unselectable();
34393 var cls = 'roo-splitbar-proxy';
34394 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34395 document.body.appendChild(proxy.dom);
34400 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34401 * Default Adapter. It assumes the splitter and resizing element are not positioned
34402 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34404 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34407 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34408 // do nothing for now
34409 init : function(s){
34413 * Called before drag operations to get the current size of the resizing element.
34414 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34416 getElementSize : function(s){
34417 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34418 return s.resizingEl.getWidth();
34420 return s.resizingEl.getHeight();
34425 * Called after drag operations to set the size of the resizing element.
34426 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34427 * @param {Number} newSize The new size to set
34428 * @param {Function} onComplete A function to be invoked when resizing is complete
34430 setElementSize : function(s, newSize, onComplete){
34431 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34433 s.resizingEl.setWidth(newSize);
34435 onComplete(s, newSize);
34438 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34443 s.resizingEl.setHeight(newSize);
34445 onComplete(s, newSize);
34448 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34455 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34456 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34457 * Adapter that moves the splitter element to align with the resized sizing element.
34458 * Used with an absolute positioned SplitBar.
34459 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34460 * document.body, make sure you assign an id to the body element.
34462 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34463 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34464 this.container = Roo.get(container);
34467 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34468 init : function(s){
34469 this.basic.init(s);
34472 getElementSize : function(s){
34473 return this.basic.getElementSize(s);
34476 setElementSize : function(s, newSize, onComplete){
34477 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34480 moveSplitter : function(s){
34481 var yes = Roo.bootstrap.SplitBar;
34482 switch(s.placement){
34484 s.el.setX(s.resizingEl.getRight());
34487 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34490 s.el.setY(s.resizingEl.getBottom());
34493 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34500 * Orientation constant - Create a vertical SplitBar
34504 Roo.bootstrap.SplitBar.VERTICAL = 1;
34507 * Orientation constant - Create a horizontal SplitBar
34511 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34514 * Placement constant - The resizing element is to the left of the splitter element
34518 Roo.bootstrap.SplitBar.LEFT = 1;
34521 * Placement constant - The resizing element is to the right of the splitter element
34525 Roo.bootstrap.SplitBar.RIGHT = 2;
34528 * Placement constant - The resizing element is positioned above the splitter element
34532 Roo.bootstrap.SplitBar.TOP = 3;
34535 * Placement constant - The resizing element is positioned under splitter element
34539 Roo.bootstrap.SplitBar.BOTTOM = 4;
34540 Roo.namespace("Roo.bootstrap.layout");/*
34542 * Ext JS Library 1.1.1
34543 * Copyright(c) 2006-2007, Ext JS, LLC.
34545 * Originally Released Under LGPL - original licence link has changed is not relivant.
34548 * <script type="text/javascript">
34552 * @class Roo.bootstrap.layout.Manager
34553 * @extends Roo.bootstrap.Component
34554 * Base class for layout managers.
34556 Roo.bootstrap.layout.Manager = function(config)
34558 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34564 /** false to disable window resize monitoring @type Boolean */
34565 this.monitorWindowResize = true;
34570 * Fires when a layout is performed.
34571 * @param {Roo.LayoutManager} this
34575 * @event regionresized
34576 * Fires when the user resizes a region.
34577 * @param {Roo.LayoutRegion} region The resized region
34578 * @param {Number} newSize The new size (width for east/west, height for north/south)
34580 "regionresized" : true,
34582 * @event regioncollapsed
34583 * Fires when a region is collapsed.
34584 * @param {Roo.LayoutRegion} region The collapsed region
34586 "regioncollapsed" : true,
34588 * @event regionexpanded
34589 * Fires when a region is expanded.
34590 * @param {Roo.LayoutRegion} region The expanded region
34592 "regionexpanded" : true
34594 this.updating = false;
34597 this.el = Roo.get(config.el);
34603 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34608 monitorWindowResize : true,
34614 onRender : function(ct, position)
34617 this.el = Roo.get(ct);
34620 //this.fireEvent('render',this);
34624 initEvents: function()
34628 // ie scrollbar fix
34629 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34630 document.body.scroll = "no";
34631 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34632 this.el.position('relative');
34634 this.id = this.el.id;
34635 this.el.addClass("roo-layout-container");
34636 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34637 if(this.el.dom != document.body ) {
34638 this.el.on('resize', this.layout,this);
34639 this.el.on('show', this.layout,this);
34645 * Returns true if this layout is currently being updated
34646 * @return {Boolean}
34648 isUpdating : function(){
34649 return this.updating;
34653 * Suspend the LayoutManager from doing auto-layouts while
34654 * making multiple add or remove calls
34656 beginUpdate : function(){
34657 this.updating = true;
34661 * Restore auto-layouts and optionally disable the manager from performing a layout
34662 * @param {Boolean} noLayout true to disable a layout update
34664 endUpdate : function(noLayout){
34665 this.updating = false;
34671 layout: function(){
34675 onRegionResized : function(region, newSize){
34676 this.fireEvent("regionresized", region, newSize);
34680 onRegionCollapsed : function(region){
34681 this.fireEvent("regioncollapsed", region);
34684 onRegionExpanded : function(region){
34685 this.fireEvent("regionexpanded", region);
34689 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34690 * performs box-model adjustments.
34691 * @return {Object} The size as an object {width: (the width), height: (the height)}
34693 getViewSize : function()
34696 if(this.el.dom != document.body){
34697 size = this.el.getSize();
34699 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34701 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34702 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34707 * Returns the Element this layout is bound to.
34708 * @return {Roo.Element}
34710 getEl : function(){
34715 * Returns the specified region.
34716 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34717 * @return {Roo.LayoutRegion}
34719 getRegion : function(target){
34720 return this.regions[target.toLowerCase()];
34723 onWindowResize : function(){
34724 if(this.monitorWindowResize){
34731 * Ext JS Library 1.1.1
34732 * Copyright(c) 2006-2007, Ext JS, LLC.
34734 * Originally Released Under LGPL - original licence link has changed is not relivant.
34737 * <script type="text/javascript">
34740 * @class Roo.bootstrap.layout.Border
34741 * @extends Roo.bootstrap.layout.Manager
34742 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34743 * please see: examples/bootstrap/nested.html<br><br>
34745 <b>The container the layout is rendered into can be either the body element or any other element.
34746 If it is not the body element, the container needs to either be an absolute positioned element,
34747 or you will need to add "position:relative" to the css of the container. You will also need to specify
34748 the container size if it is not the body element.</b>
34751 * Create a new Border
34752 * @param {Object} config Configuration options
34754 Roo.bootstrap.layout.Border = function(config){
34755 config = config || {};
34756 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34760 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34761 if(config[region]){
34762 config[region].region = region;
34763 this.addRegion(config[region]);
34769 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34771 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34773 * Creates and adds a new region if it doesn't already exist.
34774 * @param {String} target The target region key (north, south, east, west or center).
34775 * @param {Object} config The regions config object
34776 * @return {BorderLayoutRegion} The new region
34778 addRegion : function(config)
34780 if(!this.regions[config.region]){
34781 var r = this.factory(config);
34782 this.bindRegion(r);
34784 return this.regions[config.region];
34788 bindRegion : function(r){
34789 this.regions[r.config.region] = r;
34791 r.on("visibilitychange", this.layout, this);
34792 r.on("paneladded", this.layout, this);
34793 r.on("panelremoved", this.layout, this);
34794 r.on("invalidated", this.layout, this);
34795 r.on("resized", this.onRegionResized, this);
34796 r.on("collapsed", this.onRegionCollapsed, this);
34797 r.on("expanded", this.onRegionExpanded, this);
34801 * Performs a layout update.
34803 layout : function()
34805 if(this.updating) {
34809 // render all the rebions if they have not been done alreayd?
34810 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34811 if(this.regions[region] && !this.regions[region].bodyEl){
34812 this.regions[region].onRender(this.el)
34816 var size = this.getViewSize();
34817 var w = size.width;
34818 var h = size.height;
34823 //var x = 0, y = 0;
34825 var rs = this.regions;
34826 var north = rs["north"];
34827 var south = rs["south"];
34828 var west = rs["west"];
34829 var east = rs["east"];
34830 var center = rs["center"];
34831 //if(this.hideOnLayout){ // not supported anymore
34832 //c.el.setStyle("display", "none");
34834 if(north && north.isVisible()){
34835 var b = north.getBox();
34836 var m = north.getMargins();
34837 b.width = w - (m.left+m.right);
34840 centerY = b.height + b.y + m.bottom;
34841 centerH -= centerY;
34842 north.updateBox(this.safeBox(b));
34844 if(south && south.isVisible()){
34845 var b = south.getBox();
34846 var m = south.getMargins();
34847 b.width = w - (m.left+m.right);
34849 var totalHeight = (b.height + m.top + m.bottom);
34850 b.y = h - totalHeight + m.top;
34851 centerH -= totalHeight;
34852 south.updateBox(this.safeBox(b));
34854 if(west && west.isVisible()){
34855 var b = west.getBox();
34856 var m = west.getMargins();
34857 b.height = centerH - (m.top+m.bottom);
34859 b.y = centerY + m.top;
34860 var totalWidth = (b.width + m.left + m.right);
34861 centerX += totalWidth;
34862 centerW -= totalWidth;
34863 west.updateBox(this.safeBox(b));
34865 if(east && east.isVisible()){
34866 var b = east.getBox();
34867 var m = east.getMargins();
34868 b.height = centerH - (m.top+m.bottom);
34869 var totalWidth = (b.width + m.left + m.right);
34870 b.x = w - totalWidth + m.left;
34871 b.y = centerY + m.top;
34872 centerW -= totalWidth;
34873 east.updateBox(this.safeBox(b));
34876 var m = center.getMargins();
34878 x: centerX + m.left,
34879 y: centerY + m.top,
34880 width: centerW - (m.left+m.right),
34881 height: centerH - (m.top+m.bottom)
34883 //if(this.hideOnLayout){
34884 //center.el.setStyle("display", "block");
34886 center.updateBox(this.safeBox(centerBox));
34889 this.fireEvent("layout", this);
34893 safeBox : function(box){
34894 box.width = Math.max(0, box.width);
34895 box.height = Math.max(0, box.height);
34900 * Adds a ContentPanel (or subclass) to this layout.
34901 * @param {String} target The target region key (north, south, east, west or center).
34902 * @param {Roo.ContentPanel} panel The panel to add
34903 * @return {Roo.ContentPanel} The added panel
34905 add : function(target, panel){
34907 target = target.toLowerCase();
34908 return this.regions[target].add(panel);
34912 * Remove a ContentPanel (or subclass) to this layout.
34913 * @param {String} target The target region key (north, south, east, west or center).
34914 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34915 * @return {Roo.ContentPanel} The removed panel
34917 remove : function(target, panel){
34918 target = target.toLowerCase();
34919 return this.regions[target].remove(panel);
34923 * Searches all regions for a panel with the specified id
34924 * @param {String} panelId
34925 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34927 findPanel : function(panelId){
34928 var rs = this.regions;
34929 for(var target in rs){
34930 if(typeof rs[target] != "function"){
34931 var p = rs[target].getPanel(panelId);
34941 * Searches all regions for a panel with the specified id and activates (shows) it.
34942 * @param {String/ContentPanel} panelId The panels id or the panel itself
34943 * @return {Roo.ContentPanel} The shown panel or null
34945 showPanel : function(panelId) {
34946 var rs = this.regions;
34947 for(var target in rs){
34948 var r = rs[target];
34949 if(typeof r != "function"){
34950 if(r.hasPanel(panelId)){
34951 return r.showPanel(panelId);
34959 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34960 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34963 restoreState : function(provider){
34965 provider = Roo.state.Manager;
34967 var sm = new Roo.LayoutStateManager();
34968 sm.init(this, provider);
34974 * Adds a xtype elements to the layout.
34978 xtype : 'ContentPanel',
34985 xtype : 'NestedLayoutPanel',
34991 items : [ ... list of content panels or nested layout panels.. ]
34995 * @param {Object} cfg Xtype definition of item to add.
34997 addxtype : function(cfg)
34999 // basically accepts a pannel...
35000 // can accept a layout region..!?!?
35001 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35004 // theory? children can only be panels??
35006 //if (!cfg.xtype.match(/Panel$/)) {
35011 if (typeof(cfg.region) == 'undefined') {
35012 Roo.log("Failed to add Panel, region was not set");
35016 var region = cfg.region;
35022 xitems = cfg.items;
35029 case 'Content': // ContentPanel (el, cfg)
35030 case 'Scroll': // ContentPanel (el, cfg)
35032 cfg.autoCreate = true;
35033 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35035 // var el = this.el.createChild();
35036 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35039 this.add(region, ret);
35043 case 'TreePanel': // our new panel!
35044 cfg.el = this.el.createChild();
35045 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35046 this.add(region, ret);
35051 // create a new Layout (which is a Border Layout...
35053 var clayout = cfg.layout;
35054 clayout.el = this.el.createChild();
35055 clayout.items = clayout.items || [];
35059 // replace this exitems with the clayout ones..
35060 xitems = clayout.items;
35062 // force background off if it's in center...
35063 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35064 cfg.background = false;
35066 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35069 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35070 //console.log('adding nested layout panel ' + cfg.toSource());
35071 this.add(region, ret);
35072 nb = {}; /// find first...
35077 // needs grid and region
35079 //var el = this.getRegion(region).el.createChild();
35081 *var el = this.el.createChild();
35082 // create the grid first...
35083 cfg.grid.container = el;
35084 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35087 if (region == 'center' && this.active ) {
35088 cfg.background = false;
35091 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35093 this.add(region, ret);
35095 if (cfg.background) {
35096 // render grid on panel activation (if panel background)
35097 ret.on('activate', function(gp) {
35098 if (!gp.grid.rendered) {
35099 // gp.grid.render(el);
35103 // cfg.grid.render(el);
35109 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35110 // it was the old xcomponent building that caused this before.
35111 // espeically if border is the top element in the tree.
35121 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35123 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35124 this.add(region, ret);
35128 throw "Can not add '" + cfg.xtype + "' to Border";
35134 this.beginUpdate();
35138 Roo.each(xitems, function(i) {
35139 region = nb && i.region ? i.region : false;
35141 var add = ret.addxtype(i);
35144 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35145 if (!i.background) {
35146 abn[region] = nb[region] ;
35153 // make the last non-background panel active..
35154 //if (nb) { Roo.log(abn); }
35157 for(var r in abn) {
35158 region = this.getRegion(r);
35160 // tried using nb[r], but it does not work..
35162 region.showPanel(abn[r]);
35173 factory : function(cfg)
35176 var validRegions = Roo.bootstrap.layout.Border.regions;
35178 var target = cfg.region;
35181 var r = Roo.bootstrap.layout;
35185 return new r.North(cfg);
35187 return new r.South(cfg);
35189 return new r.East(cfg);
35191 return new r.West(cfg);
35193 return new r.Center(cfg);
35195 throw 'Layout region "'+target+'" not supported.';
35202 * Ext JS Library 1.1.1
35203 * Copyright(c) 2006-2007, Ext JS, LLC.
35205 * Originally Released Under LGPL - original licence link has changed is not relivant.
35208 * <script type="text/javascript">
35212 * @class Roo.bootstrap.layout.Basic
35213 * @extends Roo.util.Observable
35214 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35215 * and does not have a titlebar, tabs or any other features. All it does is size and position
35216 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35217 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35218 * @cfg {string} region the region that it inhabits..
35219 * @cfg {bool} skipConfig skip config?
35223 Roo.bootstrap.layout.Basic = function(config){
35225 this.mgr = config.mgr;
35227 this.position = config.region;
35229 var skipConfig = config.skipConfig;
35233 * @scope Roo.BasicLayoutRegion
35237 * @event beforeremove
35238 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35239 * @param {Roo.LayoutRegion} this
35240 * @param {Roo.ContentPanel} panel The panel
35241 * @param {Object} e The cancel event object
35243 "beforeremove" : true,
35245 * @event invalidated
35246 * Fires when the layout for this region is changed.
35247 * @param {Roo.LayoutRegion} this
35249 "invalidated" : true,
35251 * @event visibilitychange
35252 * Fires when this region is shown or hidden
35253 * @param {Roo.LayoutRegion} this
35254 * @param {Boolean} visibility true or false
35256 "visibilitychange" : true,
35258 * @event paneladded
35259 * Fires when a panel is added.
35260 * @param {Roo.LayoutRegion} this
35261 * @param {Roo.ContentPanel} panel The panel
35263 "paneladded" : true,
35265 * @event panelremoved
35266 * Fires when a panel is removed.
35267 * @param {Roo.LayoutRegion} this
35268 * @param {Roo.ContentPanel} panel The panel
35270 "panelremoved" : true,
35272 * @event beforecollapse
35273 * Fires when this region before collapse.
35274 * @param {Roo.LayoutRegion} this
35276 "beforecollapse" : true,
35279 * Fires when this region is collapsed.
35280 * @param {Roo.LayoutRegion} this
35282 "collapsed" : true,
35285 * Fires when this region is expanded.
35286 * @param {Roo.LayoutRegion} this
35291 * Fires when this region is slid into view.
35292 * @param {Roo.LayoutRegion} this
35294 "slideshow" : true,
35297 * Fires when this region slides out of view.
35298 * @param {Roo.LayoutRegion} this
35300 "slidehide" : true,
35302 * @event panelactivated
35303 * Fires when a panel is activated.
35304 * @param {Roo.LayoutRegion} this
35305 * @param {Roo.ContentPanel} panel The activated panel
35307 "panelactivated" : true,
35310 * Fires when the user resizes this region.
35311 * @param {Roo.LayoutRegion} this
35312 * @param {Number} newSize The new size (width for east/west, height for north/south)
35316 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35317 this.panels = new Roo.util.MixedCollection();
35318 this.panels.getKey = this.getPanelId.createDelegate(this);
35320 this.activePanel = null;
35321 // ensure listeners are added...
35323 if (config.listeners || config.events) {
35324 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35325 listeners : config.listeners || {},
35326 events : config.events || {}
35330 if(skipConfig !== true){
35331 this.applyConfig(config);
35335 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35337 getPanelId : function(p){
35341 applyConfig : function(config){
35342 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35343 this.config = config;
35348 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35349 * the width, for horizontal (north, south) the height.
35350 * @param {Number} newSize The new width or height
35352 resizeTo : function(newSize){
35353 var el = this.el ? this.el :
35354 (this.activePanel ? this.activePanel.getEl() : null);
35356 switch(this.position){
35359 el.setWidth(newSize);
35360 this.fireEvent("resized", this, newSize);
35364 el.setHeight(newSize);
35365 this.fireEvent("resized", this, newSize);
35371 getBox : function(){
35372 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35375 getMargins : function(){
35376 return this.margins;
35379 updateBox : function(box){
35381 var el = this.activePanel.getEl();
35382 el.dom.style.left = box.x + "px";
35383 el.dom.style.top = box.y + "px";
35384 this.activePanel.setSize(box.width, box.height);
35388 * Returns the container element for this region.
35389 * @return {Roo.Element}
35391 getEl : function(){
35392 return this.activePanel;
35396 * Returns true if this region is currently visible.
35397 * @return {Boolean}
35399 isVisible : function(){
35400 return this.activePanel ? true : false;
35403 setActivePanel : function(panel){
35404 panel = this.getPanel(panel);
35405 if(this.activePanel && this.activePanel != panel){
35406 this.activePanel.setActiveState(false);
35407 this.activePanel.getEl().setLeftTop(-10000,-10000);
35409 this.activePanel = panel;
35410 panel.setActiveState(true);
35412 panel.setSize(this.box.width, this.box.height);
35414 this.fireEvent("panelactivated", this, panel);
35415 this.fireEvent("invalidated");
35419 * Show the specified panel.
35420 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35421 * @return {Roo.ContentPanel} The shown panel or null
35423 showPanel : function(panel){
35424 panel = this.getPanel(panel);
35426 this.setActivePanel(panel);
35432 * Get the active panel for this region.
35433 * @return {Roo.ContentPanel} The active panel or null
35435 getActivePanel : function(){
35436 return this.activePanel;
35440 * Add the passed ContentPanel(s)
35441 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35442 * @return {Roo.ContentPanel} The panel added (if only one was added)
35444 add : function(panel){
35445 if(arguments.length > 1){
35446 for(var i = 0, len = arguments.length; i < len; i++) {
35447 this.add(arguments[i]);
35451 if(this.hasPanel(panel)){
35452 this.showPanel(panel);
35455 var el = panel.getEl();
35456 if(el.dom.parentNode != this.mgr.el.dom){
35457 this.mgr.el.dom.appendChild(el.dom);
35459 if(panel.setRegion){
35460 panel.setRegion(this);
35462 this.panels.add(panel);
35463 el.setStyle("position", "absolute");
35464 if(!panel.background){
35465 this.setActivePanel(panel);
35466 if(this.config.initialSize && this.panels.getCount()==1){
35467 this.resizeTo(this.config.initialSize);
35470 this.fireEvent("paneladded", this, panel);
35475 * Returns true if the panel is in this region.
35476 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35477 * @return {Boolean}
35479 hasPanel : function(panel){
35480 if(typeof panel == "object"){ // must be panel obj
35481 panel = panel.getId();
35483 return this.getPanel(panel) ? true : false;
35487 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35488 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35489 * @param {Boolean} preservePanel Overrides the config preservePanel option
35490 * @return {Roo.ContentPanel} The panel that was removed
35492 remove : function(panel, preservePanel){
35493 panel = this.getPanel(panel);
35498 this.fireEvent("beforeremove", this, panel, e);
35499 if(e.cancel === true){
35502 var panelId = panel.getId();
35503 this.panels.removeKey(panelId);
35508 * Returns the panel specified or null if it's not in this region.
35509 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35510 * @return {Roo.ContentPanel}
35512 getPanel : function(id){
35513 if(typeof id == "object"){ // must be panel obj
35516 return this.panels.get(id);
35520 * Returns this regions position (north/south/east/west/center).
35523 getPosition: function(){
35524 return this.position;
35528 * Ext JS Library 1.1.1
35529 * Copyright(c) 2006-2007, Ext JS, LLC.
35531 * Originally Released Under LGPL - original licence link has changed is not relivant.
35534 * <script type="text/javascript">
35538 * @class Roo.bootstrap.layout.Region
35539 * @extends Roo.bootstrap.layout.Basic
35540 * This class represents a region in a layout manager.
35542 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35543 * @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})
35544 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35545 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35546 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35547 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35548 * @cfg {String} title The title for the region (overrides panel titles)
35549 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35550 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35551 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35552 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35553 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35554 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35555 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35556 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35557 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35558 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35560 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35561 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35562 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35563 * @cfg {Number} width For East/West panels
35564 * @cfg {Number} height For North/South panels
35565 * @cfg {Boolean} split To show the splitter
35566 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35568 * @cfg {string} cls Extra CSS classes to add to region
35570 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35571 * @cfg {string} region the region that it inhabits..
35574 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35575 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35577 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35578 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35579 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35581 Roo.bootstrap.layout.Region = function(config)
35583 this.applyConfig(config);
35585 var mgr = config.mgr;
35586 var pos = config.region;
35587 config.skipConfig = true;
35588 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35591 this.onRender(mgr.el);
35594 this.visible = true;
35595 this.collapsed = false;
35596 this.unrendered_panels = [];
35599 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35601 position: '', // set by wrapper (eg. north/south etc..)
35602 unrendered_panels : null, // unrendered panels.
35603 createBody : function(){
35604 /** This region's body element
35605 * @type Roo.Element */
35606 this.bodyEl = this.el.createChild({
35608 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35612 onRender: function(ctr, pos)
35614 var dh = Roo.DomHelper;
35615 /** This region's container element
35616 * @type Roo.Element */
35617 this.el = dh.append(ctr.dom, {
35619 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35621 /** This region's title element
35622 * @type Roo.Element */
35624 this.titleEl = dh.append(this.el.dom,
35627 unselectable: "on",
35628 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35630 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35631 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35634 this.titleEl.enableDisplayMode();
35635 /** This region's title text element
35636 * @type HTMLElement */
35637 this.titleTextEl = this.titleEl.dom.firstChild;
35638 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35640 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35641 this.closeBtn.enableDisplayMode();
35642 this.closeBtn.on("click", this.closeClicked, this);
35643 this.closeBtn.hide();
35645 this.createBody(this.config);
35646 if(this.config.hideWhenEmpty){
35648 this.on("paneladded", this.validateVisibility, this);
35649 this.on("panelremoved", this.validateVisibility, this);
35651 if(this.autoScroll){
35652 this.bodyEl.setStyle("overflow", "auto");
35654 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35656 //if(c.titlebar !== false){
35657 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35658 this.titleEl.hide();
35660 this.titleEl.show();
35661 if(this.config.title){
35662 this.titleTextEl.innerHTML = this.config.title;
35666 if(this.config.collapsed){
35667 this.collapse(true);
35669 if(this.config.hidden){
35673 if (this.unrendered_panels && this.unrendered_panels.length) {
35674 for (var i =0;i< this.unrendered_panels.length; i++) {
35675 this.add(this.unrendered_panels[i]);
35677 this.unrendered_panels = null;
35683 applyConfig : function(c)
35686 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35687 var dh = Roo.DomHelper;
35688 if(c.titlebar !== false){
35689 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35690 this.collapseBtn.on("click", this.collapse, this);
35691 this.collapseBtn.enableDisplayMode();
35693 if(c.showPin === true || this.showPin){
35694 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35695 this.stickBtn.enableDisplayMode();
35696 this.stickBtn.on("click", this.expand, this);
35697 this.stickBtn.hide();
35702 /** This region's collapsed element
35703 * @type Roo.Element */
35706 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35707 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35710 if(c.floatable !== false){
35711 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35712 this.collapsedEl.on("click", this.collapseClick, this);
35715 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35716 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35717 id: "message", unselectable: "on", style:{"float":"left"}});
35718 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35720 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35721 this.expandBtn.on("click", this.expand, this);
35725 if(this.collapseBtn){
35726 this.collapseBtn.setVisible(c.collapsible == true);
35729 this.cmargins = c.cmargins || this.cmargins ||
35730 (this.position == "west" || this.position == "east" ?
35731 {top: 0, left: 2, right:2, bottom: 0} :
35732 {top: 2, left: 0, right:0, bottom: 2});
35734 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35737 this.bottomTabs = c.tabPosition != "top";
35739 this.autoScroll = c.autoScroll || false;
35744 this.duration = c.duration || .30;
35745 this.slideDuration = c.slideDuration || .45;
35750 * Returns true if this region is currently visible.
35751 * @return {Boolean}
35753 isVisible : function(){
35754 return this.visible;
35758 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35759 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35761 //setCollapsedTitle : function(title){
35762 // title = title || " ";
35763 // if(this.collapsedTitleTextEl){
35764 // this.collapsedTitleTextEl.innerHTML = title;
35768 getBox : function(){
35770 // if(!this.collapsed){
35771 b = this.el.getBox(false, true);
35773 // b = this.collapsedEl.getBox(false, true);
35778 getMargins : function(){
35779 return this.margins;
35780 //return this.collapsed ? this.cmargins : this.margins;
35783 highlight : function(){
35784 this.el.addClass("x-layout-panel-dragover");
35787 unhighlight : function(){
35788 this.el.removeClass("x-layout-panel-dragover");
35791 updateBox : function(box)
35793 if (!this.bodyEl) {
35794 return; // not rendered yet..
35798 if(!this.collapsed){
35799 this.el.dom.style.left = box.x + "px";
35800 this.el.dom.style.top = box.y + "px";
35801 this.updateBody(box.width, box.height);
35803 this.collapsedEl.dom.style.left = box.x + "px";
35804 this.collapsedEl.dom.style.top = box.y + "px";
35805 this.collapsedEl.setSize(box.width, box.height);
35808 this.tabs.autoSizeTabs();
35812 updateBody : function(w, h)
35815 this.el.setWidth(w);
35816 w -= this.el.getBorderWidth("rl");
35817 if(this.config.adjustments){
35818 w += this.config.adjustments[0];
35821 if(h !== null && h > 0){
35822 this.el.setHeight(h);
35823 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35824 h -= this.el.getBorderWidth("tb");
35825 if(this.config.adjustments){
35826 h += this.config.adjustments[1];
35828 this.bodyEl.setHeight(h);
35830 h = this.tabs.syncHeight(h);
35833 if(this.panelSize){
35834 w = w !== null ? w : this.panelSize.width;
35835 h = h !== null ? h : this.panelSize.height;
35837 if(this.activePanel){
35838 var el = this.activePanel.getEl();
35839 w = w !== null ? w : el.getWidth();
35840 h = h !== null ? h : el.getHeight();
35841 this.panelSize = {width: w, height: h};
35842 this.activePanel.setSize(w, h);
35844 if(Roo.isIE && this.tabs){
35845 this.tabs.el.repaint();
35850 * Returns the container element for this region.
35851 * @return {Roo.Element}
35853 getEl : function(){
35858 * Hides this region.
35861 //if(!this.collapsed){
35862 this.el.dom.style.left = "-2000px";
35865 // this.collapsedEl.dom.style.left = "-2000px";
35866 // this.collapsedEl.hide();
35868 this.visible = false;
35869 this.fireEvent("visibilitychange", this, false);
35873 * Shows this region if it was previously hidden.
35876 //if(!this.collapsed){
35879 // this.collapsedEl.show();
35881 this.visible = true;
35882 this.fireEvent("visibilitychange", this, true);
35885 closeClicked : function(){
35886 if(this.activePanel){
35887 this.remove(this.activePanel);
35891 collapseClick : function(e){
35893 e.stopPropagation();
35896 e.stopPropagation();
35902 * Collapses this region.
35903 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35906 collapse : function(skipAnim, skipCheck = false){
35907 if(this.collapsed) {
35911 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35913 this.collapsed = true;
35915 this.split.el.hide();
35917 if(this.config.animate && skipAnim !== true){
35918 this.fireEvent("invalidated", this);
35919 this.animateCollapse();
35921 this.el.setLocation(-20000,-20000);
35923 this.collapsedEl.show();
35924 this.fireEvent("collapsed", this);
35925 this.fireEvent("invalidated", this);
35931 animateCollapse : function(){
35936 * Expands this region if it was previously collapsed.
35937 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35938 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35941 expand : function(e, skipAnim){
35943 e.stopPropagation();
35945 if(!this.collapsed || this.el.hasActiveFx()) {
35949 this.afterSlideIn();
35952 this.collapsed = false;
35953 if(this.config.animate && skipAnim !== true){
35954 this.animateExpand();
35958 this.split.el.show();
35960 this.collapsedEl.setLocation(-2000,-2000);
35961 this.collapsedEl.hide();
35962 this.fireEvent("invalidated", this);
35963 this.fireEvent("expanded", this);
35967 animateExpand : function(){
35971 initTabs : function()
35973 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35975 var ts = new Roo.bootstrap.panel.Tabs({
35976 el: this.bodyEl.dom,
35977 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35978 disableTooltips: this.config.disableTabTips,
35979 toolbar : this.config.toolbar
35982 if(this.config.hideTabs){
35983 ts.stripWrap.setDisplayed(false);
35986 ts.resizeTabs = this.config.resizeTabs === true;
35987 ts.minTabWidth = this.config.minTabWidth || 40;
35988 ts.maxTabWidth = this.config.maxTabWidth || 250;
35989 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35990 ts.monitorResize = false;
35991 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35992 ts.bodyEl.addClass('roo-layout-tabs-body');
35993 this.panels.each(this.initPanelAsTab, this);
35996 initPanelAsTab : function(panel){
35997 var ti = this.tabs.addTab(
36001 this.config.closeOnTab && panel.isClosable(),
36004 if(panel.tabTip !== undefined){
36005 ti.setTooltip(panel.tabTip);
36007 ti.on("activate", function(){
36008 this.setActivePanel(panel);
36011 if(this.config.closeOnTab){
36012 ti.on("beforeclose", function(t, e){
36014 this.remove(panel);
36018 panel.tabItem = ti;
36023 updatePanelTitle : function(panel, title)
36025 if(this.activePanel == panel){
36026 this.updateTitle(title);
36029 var ti = this.tabs.getTab(panel.getEl().id);
36031 if(panel.tabTip !== undefined){
36032 ti.setTooltip(panel.tabTip);
36037 updateTitle : function(title){
36038 if(this.titleTextEl && !this.config.title){
36039 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36043 setActivePanel : function(panel)
36045 panel = this.getPanel(panel);
36046 if(this.activePanel && this.activePanel != panel){
36047 if(this.activePanel.setActiveState(false) === false){
36051 this.activePanel = panel;
36052 panel.setActiveState(true);
36053 if(this.panelSize){
36054 panel.setSize(this.panelSize.width, this.panelSize.height);
36057 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36059 this.updateTitle(panel.getTitle());
36061 this.fireEvent("invalidated", this);
36063 this.fireEvent("panelactivated", this, panel);
36067 * Shows the specified panel.
36068 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36069 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36071 showPanel : function(panel)
36073 panel = this.getPanel(panel);
36076 var tab = this.tabs.getTab(panel.getEl().id);
36077 if(tab.isHidden()){
36078 this.tabs.unhideTab(tab.id);
36082 this.setActivePanel(panel);
36089 * Get the active panel for this region.
36090 * @return {Roo.ContentPanel} The active panel or null
36092 getActivePanel : function(){
36093 return this.activePanel;
36096 validateVisibility : function(){
36097 if(this.panels.getCount() < 1){
36098 this.updateTitle(" ");
36099 this.closeBtn.hide();
36102 if(!this.isVisible()){
36109 * Adds the passed ContentPanel(s) to this region.
36110 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36111 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36113 add : function(panel)
36115 if(arguments.length > 1){
36116 for(var i = 0, len = arguments.length; i < len; i++) {
36117 this.add(arguments[i]);
36122 // if we have not been rendered yet, then we can not really do much of this..
36123 if (!this.bodyEl) {
36124 this.unrendered_panels.push(panel);
36131 if(this.hasPanel(panel)){
36132 this.showPanel(panel);
36135 panel.setRegion(this);
36136 this.panels.add(panel);
36137 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36138 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36139 // and hide them... ???
36140 this.bodyEl.dom.appendChild(panel.getEl().dom);
36141 if(panel.background !== true){
36142 this.setActivePanel(panel);
36144 this.fireEvent("paneladded", this, panel);
36151 this.initPanelAsTab(panel);
36155 if(panel.background !== true){
36156 this.tabs.activate(panel.getEl().id);
36158 this.fireEvent("paneladded", this, panel);
36163 * Hides the tab for the specified panel.
36164 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36166 hidePanel : function(panel){
36167 if(this.tabs && (panel = this.getPanel(panel))){
36168 this.tabs.hideTab(panel.getEl().id);
36173 * Unhides the tab for a previously hidden panel.
36174 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36176 unhidePanel : function(panel){
36177 if(this.tabs && (panel = this.getPanel(panel))){
36178 this.tabs.unhideTab(panel.getEl().id);
36182 clearPanels : function(){
36183 while(this.panels.getCount() > 0){
36184 this.remove(this.panels.first());
36189 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36190 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36191 * @param {Boolean} preservePanel Overrides the config preservePanel option
36192 * @return {Roo.ContentPanel} The panel that was removed
36194 remove : function(panel, preservePanel)
36196 panel = this.getPanel(panel);
36201 this.fireEvent("beforeremove", this, panel, e);
36202 if(e.cancel === true){
36205 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36206 var panelId = panel.getId();
36207 this.panels.removeKey(panelId);
36209 document.body.appendChild(panel.getEl().dom);
36212 this.tabs.removeTab(panel.getEl().id);
36213 }else if (!preservePanel){
36214 this.bodyEl.dom.removeChild(panel.getEl().dom);
36216 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36217 var p = this.panels.first();
36218 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36219 tempEl.appendChild(p.getEl().dom);
36220 this.bodyEl.update("");
36221 this.bodyEl.dom.appendChild(p.getEl().dom);
36223 this.updateTitle(p.getTitle());
36225 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36226 this.setActivePanel(p);
36228 panel.setRegion(null);
36229 if(this.activePanel == panel){
36230 this.activePanel = null;
36232 if(this.config.autoDestroy !== false && preservePanel !== true){
36233 try{panel.destroy();}catch(e){}
36235 this.fireEvent("panelremoved", this, panel);
36240 * Returns the TabPanel component used by this region
36241 * @return {Roo.TabPanel}
36243 getTabs : function(){
36247 createTool : function(parentEl, className){
36248 var btn = Roo.DomHelper.append(parentEl, {
36250 cls: "x-layout-tools-button",
36253 cls: "roo-layout-tools-button-inner " + className,
36257 btn.addClassOnOver("roo-layout-tools-button-over");
36262 * Ext JS Library 1.1.1
36263 * Copyright(c) 2006-2007, Ext JS, LLC.
36265 * Originally Released Under LGPL - original licence link has changed is not relivant.
36268 * <script type="text/javascript">
36274 * @class Roo.SplitLayoutRegion
36275 * @extends Roo.LayoutRegion
36276 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36278 Roo.bootstrap.layout.Split = function(config){
36279 this.cursor = config.cursor;
36280 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36283 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36285 splitTip : "Drag to resize.",
36286 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36287 useSplitTips : false,
36289 applyConfig : function(config){
36290 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36293 onRender : function(ctr,pos) {
36295 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36296 if(!this.config.split){
36301 var splitEl = Roo.DomHelper.append(ctr.dom, {
36303 id: this.el.id + "-split",
36304 cls: "roo-layout-split roo-layout-split-"+this.position,
36307 /** The SplitBar for this region
36308 * @type Roo.SplitBar */
36309 // does not exist yet...
36310 Roo.log([this.position, this.orientation]);
36312 this.split = new Roo.bootstrap.SplitBar({
36313 dragElement : splitEl,
36314 resizingElement: this.el,
36315 orientation : this.orientation
36318 this.split.on("moved", this.onSplitMove, this);
36319 this.split.useShim = this.config.useShim === true;
36320 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36321 if(this.useSplitTips){
36322 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36324 //if(config.collapsible){
36325 // this.split.el.on("dblclick", this.collapse, this);
36328 if(typeof this.config.minSize != "undefined"){
36329 this.split.minSize = this.config.minSize;
36331 if(typeof this.config.maxSize != "undefined"){
36332 this.split.maxSize = this.config.maxSize;
36334 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36335 this.hideSplitter();
36340 getHMaxSize : function(){
36341 var cmax = this.config.maxSize || 10000;
36342 var center = this.mgr.getRegion("center");
36343 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36346 getVMaxSize : function(){
36347 var cmax = this.config.maxSize || 10000;
36348 var center = this.mgr.getRegion("center");
36349 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36352 onSplitMove : function(split, newSize){
36353 this.fireEvent("resized", this, newSize);
36357 * Returns the {@link Roo.SplitBar} for this region.
36358 * @return {Roo.SplitBar}
36360 getSplitBar : function(){
36365 this.hideSplitter();
36366 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36369 hideSplitter : function(){
36371 this.split.el.setLocation(-2000,-2000);
36372 this.split.el.hide();
36378 this.split.el.show();
36380 Roo.bootstrap.layout.Split.superclass.show.call(this);
36383 beforeSlide: function(){
36384 if(Roo.isGecko){// firefox overflow auto bug workaround
36385 this.bodyEl.clip();
36387 this.tabs.bodyEl.clip();
36389 if(this.activePanel){
36390 this.activePanel.getEl().clip();
36392 if(this.activePanel.beforeSlide){
36393 this.activePanel.beforeSlide();
36399 afterSlide : function(){
36400 if(Roo.isGecko){// firefox overflow auto bug workaround
36401 this.bodyEl.unclip();
36403 this.tabs.bodyEl.unclip();
36405 if(this.activePanel){
36406 this.activePanel.getEl().unclip();
36407 if(this.activePanel.afterSlide){
36408 this.activePanel.afterSlide();
36414 initAutoHide : function(){
36415 if(this.autoHide !== false){
36416 if(!this.autoHideHd){
36417 var st = new Roo.util.DelayedTask(this.slideIn, this);
36418 this.autoHideHd = {
36419 "mouseout": function(e){
36420 if(!e.within(this.el, true)){
36424 "mouseover" : function(e){
36430 this.el.on(this.autoHideHd);
36434 clearAutoHide : function(){
36435 if(this.autoHide !== false){
36436 this.el.un("mouseout", this.autoHideHd.mouseout);
36437 this.el.un("mouseover", this.autoHideHd.mouseover);
36441 clearMonitor : function(){
36442 Roo.get(document).un("click", this.slideInIf, this);
36445 // these names are backwards but not changed for compat
36446 slideOut : function(){
36447 if(this.isSlid || this.el.hasActiveFx()){
36450 this.isSlid = true;
36451 if(this.collapseBtn){
36452 this.collapseBtn.hide();
36454 this.closeBtnState = this.closeBtn.getStyle('display');
36455 this.closeBtn.hide();
36457 this.stickBtn.show();
36460 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36461 this.beforeSlide();
36462 this.el.setStyle("z-index", 10001);
36463 this.el.slideIn(this.getSlideAnchor(), {
36464 callback: function(){
36466 this.initAutoHide();
36467 Roo.get(document).on("click", this.slideInIf, this);
36468 this.fireEvent("slideshow", this);
36475 afterSlideIn : function(){
36476 this.clearAutoHide();
36477 this.isSlid = false;
36478 this.clearMonitor();
36479 this.el.setStyle("z-index", "");
36480 if(this.collapseBtn){
36481 this.collapseBtn.show();
36483 this.closeBtn.setStyle('display', this.closeBtnState);
36485 this.stickBtn.hide();
36487 this.fireEvent("slidehide", this);
36490 slideIn : function(cb){
36491 if(!this.isSlid || this.el.hasActiveFx()){
36495 this.isSlid = false;
36496 this.beforeSlide();
36497 this.el.slideOut(this.getSlideAnchor(), {
36498 callback: function(){
36499 this.el.setLeftTop(-10000, -10000);
36501 this.afterSlideIn();
36509 slideInIf : function(e){
36510 if(!e.within(this.el)){
36515 animateCollapse : function(){
36516 this.beforeSlide();
36517 this.el.setStyle("z-index", 20000);
36518 var anchor = this.getSlideAnchor();
36519 this.el.slideOut(anchor, {
36520 callback : function(){
36521 this.el.setStyle("z-index", "");
36522 this.collapsedEl.slideIn(anchor, {duration:.3});
36524 this.el.setLocation(-10000,-10000);
36526 this.fireEvent("collapsed", this);
36533 animateExpand : function(){
36534 this.beforeSlide();
36535 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36536 this.el.setStyle("z-index", 20000);
36537 this.collapsedEl.hide({
36540 this.el.slideIn(this.getSlideAnchor(), {
36541 callback : function(){
36542 this.el.setStyle("z-index", "");
36545 this.split.el.show();
36547 this.fireEvent("invalidated", this);
36548 this.fireEvent("expanded", this);
36576 getAnchor : function(){
36577 return this.anchors[this.position];
36580 getCollapseAnchor : function(){
36581 return this.canchors[this.position];
36584 getSlideAnchor : function(){
36585 return this.sanchors[this.position];
36588 getAlignAdj : function(){
36589 var cm = this.cmargins;
36590 switch(this.position){
36606 getExpandAdj : function(){
36607 var c = this.collapsedEl, cm = this.cmargins;
36608 switch(this.position){
36610 return [-(cm.right+c.getWidth()+cm.left), 0];
36613 return [cm.right+c.getWidth()+cm.left, 0];
36616 return [0, -(cm.top+cm.bottom+c.getHeight())];
36619 return [0, cm.top+cm.bottom+c.getHeight()];
36625 * Ext JS Library 1.1.1
36626 * Copyright(c) 2006-2007, Ext JS, LLC.
36628 * Originally Released Under LGPL - original licence link has changed is not relivant.
36631 * <script type="text/javascript">
36634 * These classes are private internal classes
36636 Roo.bootstrap.layout.Center = function(config){
36637 config.region = "center";
36638 Roo.bootstrap.layout.Region.call(this, config);
36639 this.visible = true;
36640 this.minWidth = config.minWidth || 20;
36641 this.minHeight = config.minHeight || 20;
36644 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36646 // center panel can't be hidden
36650 // center panel can't be hidden
36653 getMinWidth: function(){
36654 return this.minWidth;
36657 getMinHeight: function(){
36658 return this.minHeight;
36671 Roo.bootstrap.layout.North = function(config)
36673 config.region = 'north';
36674 config.cursor = 'n-resize';
36676 Roo.bootstrap.layout.Split.call(this, config);
36680 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36681 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36682 this.split.el.addClass("roo-layout-split-v");
36684 var size = config.initialSize || config.height;
36685 if(typeof size != "undefined"){
36686 this.el.setHeight(size);
36689 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36691 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36695 getBox : function(){
36696 if(this.collapsed){
36697 return this.collapsedEl.getBox();
36699 var box = this.el.getBox();
36701 box.height += this.split.el.getHeight();
36706 updateBox : function(box){
36707 if(this.split && !this.collapsed){
36708 box.height -= this.split.el.getHeight();
36709 this.split.el.setLeft(box.x);
36710 this.split.el.setTop(box.y+box.height);
36711 this.split.el.setWidth(box.width);
36713 if(this.collapsed){
36714 this.updateBody(box.width, null);
36716 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36724 Roo.bootstrap.layout.South = function(config){
36725 config.region = 'south';
36726 config.cursor = 's-resize';
36727 Roo.bootstrap.layout.Split.call(this, config);
36729 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36730 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36731 this.split.el.addClass("roo-layout-split-v");
36733 var size = config.initialSize || config.height;
36734 if(typeof size != "undefined"){
36735 this.el.setHeight(size);
36739 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36740 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36741 getBox : function(){
36742 if(this.collapsed){
36743 return this.collapsedEl.getBox();
36745 var box = this.el.getBox();
36747 var sh = this.split.el.getHeight();
36754 updateBox : function(box){
36755 if(this.split && !this.collapsed){
36756 var sh = this.split.el.getHeight();
36759 this.split.el.setLeft(box.x);
36760 this.split.el.setTop(box.y-sh);
36761 this.split.el.setWidth(box.width);
36763 if(this.collapsed){
36764 this.updateBody(box.width, null);
36766 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36770 Roo.bootstrap.layout.East = function(config){
36771 config.region = "east";
36772 config.cursor = "e-resize";
36773 Roo.bootstrap.layout.Split.call(this, config);
36775 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36776 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36777 this.split.el.addClass("roo-layout-split-h");
36779 var size = config.initialSize || config.width;
36780 if(typeof size != "undefined"){
36781 this.el.setWidth(size);
36784 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36785 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36786 getBox : function(){
36787 if(this.collapsed){
36788 return this.collapsedEl.getBox();
36790 var box = this.el.getBox();
36792 var sw = this.split.el.getWidth();
36799 updateBox : function(box){
36800 if(this.split && !this.collapsed){
36801 var sw = this.split.el.getWidth();
36803 this.split.el.setLeft(box.x);
36804 this.split.el.setTop(box.y);
36805 this.split.el.setHeight(box.height);
36808 if(this.collapsed){
36809 this.updateBody(null, box.height);
36811 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36815 Roo.bootstrap.layout.West = function(config){
36816 config.region = "west";
36817 config.cursor = "w-resize";
36819 Roo.bootstrap.layout.Split.call(this, config);
36821 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36822 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36823 this.split.el.addClass("roo-layout-split-h");
36827 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36828 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36830 onRender: function(ctr, pos)
36832 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36833 var size = this.config.initialSize || this.config.width;
36834 if(typeof size != "undefined"){
36835 this.el.setWidth(size);
36839 getBox : function(){
36840 if(this.collapsed){
36841 return this.collapsedEl.getBox();
36843 var box = this.el.getBox();
36845 box.width += this.split.el.getWidth();
36850 updateBox : function(box){
36851 if(this.split && !this.collapsed){
36852 var sw = this.split.el.getWidth();
36854 this.split.el.setLeft(box.x+box.width);
36855 this.split.el.setTop(box.y);
36856 this.split.el.setHeight(box.height);
36858 if(this.collapsed){
36859 this.updateBody(null, box.height);
36861 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36864 Roo.namespace("Roo.bootstrap.panel");/*
36866 * Ext JS Library 1.1.1
36867 * Copyright(c) 2006-2007, Ext JS, LLC.
36869 * Originally Released Under LGPL - original licence link has changed is not relivant.
36872 * <script type="text/javascript">
36875 * @class Roo.ContentPanel
36876 * @extends Roo.util.Observable
36877 * A basic ContentPanel element.
36878 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36879 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36880 * @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
36881 * @cfg {Boolean} closable True if the panel can be closed/removed
36882 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36883 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36884 * @cfg {Toolbar} toolbar A toolbar for this panel
36885 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36886 * @cfg {String} title The title for this panel
36887 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36888 * @cfg {String} url Calls {@link #setUrl} with this value
36889 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36890 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36891 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36892 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36893 * @cfg {Boolean} badges render the badges
36896 * Create a new ContentPanel.
36897 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36898 * @param {String/Object} config A string to set only the title or a config object
36899 * @param {String} content (optional) Set the HTML content for this panel
36900 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36902 Roo.bootstrap.panel.Content = function( config){
36904 this.tpl = config.tpl || false;
36906 var el = config.el;
36907 var content = config.content;
36909 if(config.autoCreate){ // xtype is available if this is called from factory
36912 this.el = Roo.get(el);
36913 if(!this.el && config && config.autoCreate){
36914 if(typeof config.autoCreate == "object"){
36915 if(!config.autoCreate.id){
36916 config.autoCreate.id = config.id||el;
36918 this.el = Roo.DomHelper.append(document.body,
36919 config.autoCreate, true);
36921 var elcfg = { tag: "div",
36922 cls: "roo-layout-inactive-content",
36926 elcfg.html = config.html;
36930 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36933 this.closable = false;
36934 this.loaded = false;
36935 this.active = false;
36938 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36940 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36942 this.wrapEl = this.el; //this.el.wrap();
36944 if (config.toolbar.items) {
36945 ti = config.toolbar.items ;
36946 delete config.toolbar.items ;
36950 this.toolbar.render(this.wrapEl, 'before');
36951 for(var i =0;i < ti.length;i++) {
36952 // Roo.log(['add child', items[i]]);
36953 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36955 this.toolbar.items = nitems;
36956 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36957 delete config.toolbar;
36961 // xtype created footer. - not sure if will work as we normally have to render first..
36962 if (this.footer && !this.footer.el && this.footer.xtype) {
36963 if (!this.wrapEl) {
36964 this.wrapEl = this.el.wrap();
36967 this.footer.container = this.wrapEl.createChild();
36969 this.footer = Roo.factory(this.footer, Roo);
36974 if(typeof config == "string"){
36975 this.title = config;
36977 Roo.apply(this, config);
36981 this.resizeEl = Roo.get(this.resizeEl, true);
36983 this.resizeEl = this.el;
36985 // handle view.xtype
36993 * Fires when this panel is activated.
36994 * @param {Roo.ContentPanel} this
36998 * @event deactivate
36999 * Fires when this panel is activated.
37000 * @param {Roo.ContentPanel} this
37002 "deactivate" : true,
37006 * Fires when this panel is resized if fitToFrame is true.
37007 * @param {Roo.ContentPanel} this
37008 * @param {Number} width The width after any component adjustments
37009 * @param {Number} height The height after any component adjustments
37015 * Fires when this tab is created
37016 * @param {Roo.ContentPanel} this
37027 if(this.autoScroll){
37028 this.resizeEl.setStyle("overflow", "auto");
37030 // fix randome scrolling
37031 //this.el.on('scroll', function() {
37032 // Roo.log('fix random scolling');
37033 // this.scrollTo('top',0);
37036 content = content || this.content;
37038 this.setContent(content);
37040 if(config && config.url){
37041 this.setUrl(this.url, this.params, this.loadOnce);
37046 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37048 if (this.view && typeof(this.view.xtype) != 'undefined') {
37049 this.view.el = this.el.appendChild(document.createElement("div"));
37050 this.view = Roo.factory(this.view);
37051 this.view.render && this.view.render(false, '');
37055 this.fireEvent('render', this);
37058 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37062 setRegion : function(region){
37063 this.region = region;
37064 this.setActiveClass(region && !this.background);
37068 setActiveClass: function(state)
37071 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37072 this.el.setStyle('position','relative');
37074 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37075 this.el.setStyle('position', 'absolute');
37080 * Returns the toolbar for this Panel if one was configured.
37081 * @return {Roo.Toolbar}
37083 getToolbar : function(){
37084 return this.toolbar;
37087 setActiveState : function(active)
37089 this.active = active;
37090 this.setActiveClass(active);
37092 if(this.fireEvent("deactivate", this) === false){
37097 this.fireEvent("activate", this);
37101 * Updates this panel's element
37102 * @param {String} content The new content
37103 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37105 setContent : function(content, loadScripts){
37106 this.el.update(content, loadScripts);
37109 ignoreResize : function(w, h){
37110 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37113 this.lastSize = {width: w, height: h};
37118 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37119 * @return {Roo.UpdateManager} The UpdateManager
37121 getUpdateManager : function(){
37122 return this.el.getUpdateManager();
37125 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37126 * @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:
37129 url: "your-url.php",
37130 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37131 callback: yourFunction,
37132 scope: yourObject, //(optional scope)
37135 text: "Loading...",
37140 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37141 * 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.
37142 * @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}
37143 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37144 * @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.
37145 * @return {Roo.ContentPanel} this
37148 var um = this.el.getUpdateManager();
37149 um.update.apply(um, arguments);
37155 * 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.
37156 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37157 * @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)
37158 * @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)
37159 * @return {Roo.UpdateManager} The UpdateManager
37161 setUrl : function(url, params, loadOnce){
37162 if(this.refreshDelegate){
37163 this.removeListener("activate", this.refreshDelegate);
37165 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37166 this.on("activate", this.refreshDelegate);
37167 return this.el.getUpdateManager();
37170 _handleRefresh : function(url, params, loadOnce){
37171 if(!loadOnce || !this.loaded){
37172 var updater = this.el.getUpdateManager();
37173 updater.update(url, params, this._setLoaded.createDelegate(this));
37177 _setLoaded : function(){
37178 this.loaded = true;
37182 * Returns this panel's id
37185 getId : function(){
37190 * Returns this panel's element - used by regiosn to add.
37191 * @return {Roo.Element}
37193 getEl : function(){
37194 return this.wrapEl || this.el;
37199 adjustForComponents : function(width, height)
37201 //Roo.log('adjustForComponents ');
37202 if(this.resizeEl != this.el){
37203 width -= this.el.getFrameWidth('lr');
37204 height -= this.el.getFrameWidth('tb');
37207 var te = this.toolbar.getEl();
37208 te.setWidth(width);
37209 height -= te.getHeight();
37212 var te = this.footer.getEl();
37213 te.setWidth(width);
37214 height -= te.getHeight();
37218 if(this.adjustments){
37219 width += this.adjustments[0];
37220 height += this.adjustments[1];
37222 return {"width": width, "height": height};
37225 setSize : function(width, height){
37226 if(this.fitToFrame && !this.ignoreResize(width, height)){
37227 if(this.fitContainer && this.resizeEl != this.el){
37228 this.el.setSize(width, height);
37230 var size = this.adjustForComponents(width, height);
37231 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37232 this.fireEvent('resize', this, size.width, size.height);
37237 * Returns this panel's title
37240 getTitle : function(){
37242 if (typeof(this.title) != 'object') {
37247 for (var k in this.title) {
37248 if (!this.title.hasOwnProperty(k)) {
37252 if (k.indexOf('-') >= 0) {
37253 var s = k.split('-');
37254 for (var i = 0; i<s.length; i++) {
37255 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37258 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37265 * Set this panel's title
37266 * @param {String} title
37268 setTitle : function(title){
37269 this.title = title;
37271 this.region.updatePanelTitle(this, title);
37276 * Returns true is this panel was configured to be closable
37277 * @return {Boolean}
37279 isClosable : function(){
37280 return this.closable;
37283 beforeSlide : function(){
37285 this.resizeEl.clip();
37288 afterSlide : function(){
37290 this.resizeEl.unclip();
37294 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37295 * Will fail silently if the {@link #setUrl} method has not been called.
37296 * This does not activate the panel, just updates its content.
37298 refresh : function(){
37299 if(this.refreshDelegate){
37300 this.loaded = false;
37301 this.refreshDelegate();
37306 * Destroys this panel
37308 destroy : function(){
37309 this.el.removeAllListeners();
37310 var tempEl = document.createElement("span");
37311 tempEl.appendChild(this.el.dom);
37312 tempEl.innerHTML = "";
37318 * form - if the content panel contains a form - this is a reference to it.
37319 * @type {Roo.form.Form}
37323 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37324 * This contains a reference to it.
37330 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37340 * @param {Object} cfg Xtype definition of item to add.
37344 getChildContainer: function () {
37345 return this.getEl();
37350 var ret = new Roo.factory(cfg);
37355 if (cfg.xtype.match(/^Form$/)) {
37358 //if (this.footer) {
37359 // el = this.footer.container.insertSibling(false, 'before');
37361 el = this.el.createChild();
37364 this.form = new Roo.form.Form(cfg);
37367 if ( this.form.allItems.length) {
37368 this.form.render(el.dom);
37372 // should only have one of theses..
37373 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37374 // views.. should not be just added - used named prop 'view''
37376 cfg.el = this.el.appendChild(document.createElement("div"));
37379 var ret = new Roo.factory(cfg);
37381 ret.render && ret.render(false, ''); // render blank..
37391 * @class Roo.bootstrap.panel.Grid
37392 * @extends Roo.bootstrap.panel.Content
37394 * Create a new GridPanel.
37395 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37396 * @param {Object} config A the config object
37402 Roo.bootstrap.panel.Grid = function(config)
37406 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37407 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37409 config.el = this.wrapper;
37410 //this.el = this.wrapper;
37412 if (config.container) {
37413 // ctor'ed from a Border/panel.grid
37416 this.wrapper.setStyle("overflow", "hidden");
37417 this.wrapper.addClass('roo-grid-container');
37422 if(config.toolbar){
37423 var tool_el = this.wrapper.createChild();
37424 this.toolbar = Roo.factory(config.toolbar);
37426 if (config.toolbar.items) {
37427 ti = config.toolbar.items ;
37428 delete config.toolbar.items ;
37432 this.toolbar.render(tool_el);
37433 for(var i =0;i < ti.length;i++) {
37434 // Roo.log(['add child', items[i]]);
37435 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37437 this.toolbar.items = nitems;
37439 delete config.toolbar;
37442 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37443 config.grid.scrollBody = true;;
37444 config.grid.monitorWindowResize = false; // turn off autosizing
37445 config.grid.autoHeight = false;
37446 config.grid.autoWidth = false;
37448 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37450 if (config.background) {
37451 // render grid on panel activation (if panel background)
37452 this.on('activate', function(gp) {
37453 if (!gp.grid.rendered) {
37454 gp.grid.render(this.wrapper);
37455 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37460 this.grid.render(this.wrapper);
37461 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37464 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37465 // ??? needed ??? config.el = this.wrapper;
37470 // xtype created footer. - not sure if will work as we normally have to render first..
37471 if (this.footer && !this.footer.el && this.footer.xtype) {
37473 var ctr = this.grid.getView().getFooterPanel(true);
37474 this.footer.dataSource = this.grid.dataSource;
37475 this.footer = Roo.factory(this.footer, Roo);
37476 this.footer.render(ctr);
37486 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37487 getId : function(){
37488 return this.grid.id;
37492 * Returns the grid for this panel
37493 * @return {Roo.bootstrap.Table}
37495 getGrid : function(){
37499 setSize : function(width, height){
37500 if(!this.ignoreResize(width, height)){
37501 var grid = this.grid;
37502 var size = this.adjustForComponents(width, height);
37503 var gridel = grid.getGridEl();
37504 gridel.setSize(size.width, size.height);
37506 var thd = grid.getGridEl().select('thead',true).first();
37507 var tbd = grid.getGridEl().select('tbody', true).first();
37509 tbd.setSize(width, height - thd.getHeight());
37518 beforeSlide : function(){
37519 this.grid.getView().scroller.clip();
37522 afterSlide : function(){
37523 this.grid.getView().scroller.unclip();
37526 destroy : function(){
37527 this.grid.destroy();
37529 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37534 * @class Roo.bootstrap.panel.Nest
37535 * @extends Roo.bootstrap.panel.Content
37537 * Create a new Panel, that can contain a layout.Border.
37540 * @param {Roo.BorderLayout} layout The layout for this panel
37541 * @param {String/Object} config A string to set only the title or a config object
37543 Roo.bootstrap.panel.Nest = function(config)
37545 // construct with only one argument..
37546 /* FIXME - implement nicer consturctors
37547 if (layout.layout) {
37549 layout = config.layout;
37550 delete config.layout;
37552 if (layout.xtype && !layout.getEl) {
37553 // then layout needs constructing..
37554 layout = Roo.factory(layout, Roo);
37558 config.el = config.layout.getEl();
37560 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37562 config.layout.monitorWindowResize = false; // turn off autosizing
37563 this.layout = config.layout;
37564 this.layout.getEl().addClass("roo-layout-nested-layout");
37571 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37573 setSize : function(width, height){
37574 if(!this.ignoreResize(width, height)){
37575 var size = this.adjustForComponents(width, height);
37576 var el = this.layout.getEl();
37577 if (size.height < 1) {
37578 el.setWidth(size.width);
37580 el.setSize(size.width, size.height);
37582 var touch = el.dom.offsetWidth;
37583 this.layout.layout();
37584 // ie requires a double layout on the first pass
37585 if(Roo.isIE && !this.initialized){
37586 this.initialized = true;
37587 this.layout.layout();
37592 // activate all subpanels if not currently active..
37594 setActiveState : function(active){
37595 this.active = active;
37596 this.setActiveClass(active);
37599 this.fireEvent("deactivate", this);
37603 this.fireEvent("activate", this);
37604 // not sure if this should happen before or after..
37605 if (!this.layout) {
37606 return; // should not happen..
37609 for (var r in this.layout.regions) {
37610 reg = this.layout.getRegion(r);
37611 if (reg.getActivePanel()) {
37612 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37613 reg.setActivePanel(reg.getActivePanel());
37616 if (!reg.panels.length) {
37619 reg.showPanel(reg.getPanel(0));
37628 * Returns the nested BorderLayout for this panel
37629 * @return {Roo.BorderLayout}
37631 getLayout : function(){
37632 return this.layout;
37636 * Adds a xtype elements to the layout of the nested panel
37640 xtype : 'ContentPanel',
37647 xtype : 'NestedLayoutPanel',
37653 items : [ ... list of content panels or nested layout panels.. ]
37657 * @param {Object} cfg Xtype definition of item to add.
37659 addxtype : function(cfg) {
37660 return this.layout.addxtype(cfg);
37665 * Ext JS Library 1.1.1
37666 * Copyright(c) 2006-2007, Ext JS, LLC.
37668 * Originally Released Under LGPL - original licence link has changed is not relivant.
37671 * <script type="text/javascript">
37674 * @class Roo.TabPanel
37675 * @extends Roo.util.Observable
37676 * A lightweight tab container.
37680 // basic tabs 1, built from existing content
37681 var tabs = new Roo.TabPanel("tabs1");
37682 tabs.addTab("script", "View Script");
37683 tabs.addTab("markup", "View Markup");
37684 tabs.activate("script");
37686 // more advanced tabs, built from javascript
37687 var jtabs = new Roo.TabPanel("jtabs");
37688 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37690 // set up the UpdateManager
37691 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37692 var updater = tab2.getUpdateManager();
37693 updater.setDefaultUrl("ajax1.htm");
37694 tab2.on('activate', updater.refresh, updater, true);
37696 // Use setUrl for Ajax loading
37697 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37698 tab3.setUrl("ajax2.htm", null, true);
37701 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37704 jtabs.activate("jtabs-1");
37707 * Create a new TabPanel.
37708 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37709 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37711 Roo.bootstrap.panel.Tabs = function(config){
37713 * The container element for this TabPanel.
37714 * @type Roo.Element
37716 this.el = Roo.get(config.el);
37719 if(typeof config == "boolean"){
37720 this.tabPosition = config ? "bottom" : "top";
37722 Roo.apply(this, config);
37726 if(this.tabPosition == "bottom"){
37727 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37728 this.el.addClass("roo-tabs-bottom");
37730 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37731 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37732 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37734 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37736 if(this.tabPosition != "bottom"){
37737 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37738 * @type Roo.Element
37740 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37741 this.el.addClass("roo-tabs-top");
37745 this.bodyEl.setStyle("position", "relative");
37747 this.active = null;
37748 this.activateDelegate = this.activate.createDelegate(this);
37753 * Fires when the active tab changes
37754 * @param {Roo.TabPanel} this
37755 * @param {Roo.TabPanelItem} activePanel The new active tab
37759 * @event beforetabchange
37760 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37761 * @param {Roo.TabPanel} this
37762 * @param {Object} e Set cancel to true on this object to cancel the tab change
37763 * @param {Roo.TabPanelItem} tab The tab being changed to
37765 "beforetabchange" : true
37768 Roo.EventManager.onWindowResize(this.onResize, this);
37769 this.cpad = this.el.getPadding("lr");
37770 this.hiddenCount = 0;
37773 // toolbar on the tabbar support...
37774 if (this.toolbar) {
37775 alert("no toolbar support yet");
37776 this.toolbar = false;
37778 var tcfg = this.toolbar;
37779 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37780 this.toolbar = new Roo.Toolbar(tcfg);
37781 if (Roo.isSafari) {
37782 var tbl = tcfg.container.child('table', true);
37783 tbl.setAttribute('width', '100%');
37791 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37794 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37796 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37798 tabPosition : "top",
37800 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37802 currentTabWidth : 0,
37804 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37808 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37812 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37814 preferredTabWidth : 175,
37816 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37818 resizeTabs : false,
37820 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37822 monitorResize : true,
37824 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37829 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37830 * @param {String} id The id of the div to use <b>or create</b>
37831 * @param {String} text The text for the tab
37832 * @param {String} content (optional) Content to put in the TabPanelItem body
37833 * @param {Boolean} closable (optional) True to create a close icon on the tab
37834 * @return {Roo.TabPanelItem} The created TabPanelItem
37836 addTab : function(id, text, content, closable, tpl)
37838 var item = new Roo.bootstrap.panel.TabItem({
37842 closable : closable,
37845 this.addTabItem(item);
37847 item.setContent(content);
37853 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37854 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37855 * @return {Roo.TabPanelItem}
37857 getTab : function(id){
37858 return this.items[id];
37862 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37863 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37865 hideTab : function(id){
37866 var t = this.items[id];
37869 this.hiddenCount++;
37870 this.autoSizeTabs();
37875 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37876 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37878 unhideTab : function(id){
37879 var t = this.items[id];
37881 t.setHidden(false);
37882 this.hiddenCount--;
37883 this.autoSizeTabs();
37888 * Adds an existing {@link Roo.TabPanelItem}.
37889 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37891 addTabItem : function(item){
37892 this.items[item.id] = item;
37893 this.items.push(item);
37894 // if(this.resizeTabs){
37895 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37896 // this.autoSizeTabs();
37898 // item.autoSize();
37903 * Removes a {@link Roo.TabPanelItem}.
37904 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37906 removeTab : function(id){
37907 var items = this.items;
37908 var tab = items[id];
37909 if(!tab) { return; }
37910 var index = items.indexOf(tab);
37911 if(this.active == tab && items.length > 1){
37912 var newTab = this.getNextAvailable(index);
37917 this.stripEl.dom.removeChild(tab.pnode.dom);
37918 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37919 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37921 items.splice(index, 1);
37922 delete this.items[tab.id];
37923 tab.fireEvent("close", tab);
37924 tab.purgeListeners();
37925 this.autoSizeTabs();
37928 getNextAvailable : function(start){
37929 var items = this.items;
37931 // look for a next tab that will slide over to
37932 // replace the one being removed
37933 while(index < items.length){
37934 var item = items[++index];
37935 if(item && !item.isHidden()){
37939 // if one isn't found select the previous tab (on the left)
37942 var item = items[--index];
37943 if(item && !item.isHidden()){
37951 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37952 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37954 disableTab : function(id){
37955 var tab = this.items[id];
37956 if(tab && this.active != tab){
37962 * Enables a {@link Roo.TabPanelItem} that is disabled.
37963 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37965 enableTab : function(id){
37966 var tab = this.items[id];
37971 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37972 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37973 * @return {Roo.TabPanelItem} The TabPanelItem.
37975 activate : function(id){
37976 var tab = this.items[id];
37980 if(tab == this.active || tab.disabled){
37984 this.fireEvent("beforetabchange", this, e, tab);
37985 if(e.cancel !== true && !tab.disabled){
37987 this.active.hide();
37989 this.active = this.items[id];
37990 this.active.show();
37991 this.fireEvent("tabchange", this, this.active);
37997 * Gets the active {@link Roo.TabPanelItem}.
37998 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38000 getActiveTab : function(){
38001 return this.active;
38005 * Updates the tab body element to fit the height of the container element
38006 * for overflow scrolling
38007 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38009 syncHeight : function(targetHeight){
38010 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38011 var bm = this.bodyEl.getMargins();
38012 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38013 this.bodyEl.setHeight(newHeight);
38017 onResize : function(){
38018 if(this.monitorResize){
38019 this.autoSizeTabs();
38024 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38026 beginUpdate : function(){
38027 this.updating = true;
38031 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38033 endUpdate : function(){
38034 this.updating = false;
38035 this.autoSizeTabs();
38039 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38041 autoSizeTabs : function(){
38042 var count = this.items.length;
38043 var vcount = count - this.hiddenCount;
38044 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38047 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38048 var availWidth = Math.floor(w / vcount);
38049 var b = this.stripBody;
38050 if(b.getWidth() > w){
38051 var tabs = this.items;
38052 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38053 if(availWidth < this.minTabWidth){
38054 /*if(!this.sleft){ // incomplete scrolling code
38055 this.createScrollButtons();
38058 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38061 if(this.currentTabWidth < this.preferredTabWidth){
38062 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38068 * Returns the number of tabs in this TabPanel.
38071 getCount : function(){
38072 return this.items.length;
38076 * Resizes all the tabs to the passed width
38077 * @param {Number} The new width
38079 setTabWidth : function(width){
38080 this.currentTabWidth = width;
38081 for(var i = 0, len = this.items.length; i < len; i++) {
38082 if(!this.items[i].isHidden()) {
38083 this.items[i].setWidth(width);
38089 * Destroys this TabPanel
38090 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38092 destroy : function(removeEl){
38093 Roo.EventManager.removeResizeListener(this.onResize, this);
38094 for(var i = 0, len = this.items.length; i < len; i++){
38095 this.items[i].purgeListeners();
38097 if(removeEl === true){
38098 this.el.update("");
38103 createStrip : function(container)
38105 var strip = document.createElement("nav");
38106 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38107 container.appendChild(strip);
38111 createStripList : function(strip)
38113 // div wrapper for retard IE
38114 // returns the "tr" element.
38115 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38116 //'<div class="x-tabs-strip-wrap">'+
38117 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38118 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38119 return strip.firstChild; //.firstChild.firstChild.firstChild;
38121 createBody : function(container)
38123 var body = document.createElement("div");
38124 Roo.id(body, "tab-body");
38125 //Roo.fly(body).addClass("x-tabs-body");
38126 Roo.fly(body).addClass("tab-content");
38127 container.appendChild(body);
38130 createItemBody :function(bodyEl, id){
38131 var body = Roo.getDom(id);
38133 body = document.createElement("div");
38136 //Roo.fly(body).addClass("x-tabs-item-body");
38137 Roo.fly(body).addClass("tab-pane");
38138 bodyEl.insertBefore(body, bodyEl.firstChild);
38142 createStripElements : function(stripEl, text, closable, tpl)
38144 var td = document.createElement("li"); // was td..
38147 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38150 stripEl.appendChild(td);
38152 td.className = "x-tabs-closable";
38153 if(!this.closeTpl){
38154 this.closeTpl = new Roo.Template(
38155 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38156 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38157 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38160 var el = this.closeTpl.overwrite(td, {"text": text});
38161 var close = el.getElementsByTagName("div")[0];
38162 var inner = el.getElementsByTagName("em")[0];
38163 return {"el": el, "close": close, "inner": inner};
38166 // not sure what this is..
38167 // if(!this.tabTpl){
38168 //this.tabTpl = new Roo.Template(
38169 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38170 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38172 // this.tabTpl = new Roo.Template(
38173 // '<a href="#">' +
38174 // '<span unselectable="on"' +
38175 // (this.disableTooltips ? '' : ' title="{text}"') +
38176 // ' >{text}</span></a>'
38182 var template = tpl || this.tabTpl || false;
38186 template = new Roo.Template(
38188 '<span unselectable="on"' +
38189 (this.disableTooltips ? '' : ' title="{text}"') +
38190 ' >{text}</span></a>'
38194 switch (typeof(template)) {
38198 template = new Roo.Template(template);
38204 var el = template.overwrite(td, {"text": text});
38206 var inner = el.getElementsByTagName("span")[0];
38208 return {"el": el, "inner": inner};
38216 * @class Roo.TabPanelItem
38217 * @extends Roo.util.Observable
38218 * Represents an individual item (tab plus body) in a TabPanel.
38219 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38220 * @param {String} id The id of this TabPanelItem
38221 * @param {String} text The text for the tab of this TabPanelItem
38222 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38224 Roo.bootstrap.panel.TabItem = function(config){
38226 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38227 * @type Roo.TabPanel
38229 this.tabPanel = config.panel;
38231 * The id for this TabPanelItem
38234 this.id = config.id;
38236 this.disabled = false;
38238 this.text = config.text;
38240 this.loaded = false;
38241 this.closable = config.closable;
38244 * The body element for this TabPanelItem.
38245 * @type Roo.Element
38247 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38248 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38249 this.bodyEl.setStyle("display", "block");
38250 this.bodyEl.setStyle("zoom", "1");
38251 //this.hideAction();
38253 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38255 this.el = Roo.get(els.el);
38256 this.inner = Roo.get(els.inner, true);
38257 this.textEl = Roo.get(this.el.dom.firstChild, true);
38258 this.pnode = Roo.get(els.el.parentNode, true);
38259 // this.el.on("mousedown", this.onTabMouseDown, this);
38260 this.el.on("click", this.onTabClick, this);
38262 if(config.closable){
38263 var c = Roo.get(els.close, true);
38264 c.dom.title = this.closeText;
38265 c.addClassOnOver("close-over");
38266 c.on("click", this.closeClick, this);
38272 * Fires when this tab becomes the active tab.
38273 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38274 * @param {Roo.TabPanelItem} this
38278 * @event beforeclose
38279 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38280 * @param {Roo.TabPanelItem} this
38281 * @param {Object} e Set cancel to true on this object to cancel the close.
38283 "beforeclose": true,
38286 * Fires when this tab is closed.
38287 * @param {Roo.TabPanelItem} this
38291 * @event deactivate
38292 * Fires when this tab is no longer the active tab.
38293 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38294 * @param {Roo.TabPanelItem} this
38296 "deactivate" : true
38298 this.hidden = false;
38300 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38303 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38305 purgeListeners : function(){
38306 Roo.util.Observable.prototype.purgeListeners.call(this);
38307 this.el.removeAllListeners();
38310 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38313 this.pnode.addClass("active");
38316 this.tabPanel.stripWrap.repaint();
38318 this.fireEvent("activate", this.tabPanel, this);
38322 * Returns true if this tab is the active tab.
38323 * @return {Boolean}
38325 isActive : function(){
38326 return this.tabPanel.getActiveTab() == this;
38330 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38333 this.pnode.removeClass("active");
38335 this.fireEvent("deactivate", this.tabPanel, this);
38338 hideAction : function(){
38339 this.bodyEl.hide();
38340 this.bodyEl.setStyle("position", "absolute");
38341 this.bodyEl.setLeft("-20000px");
38342 this.bodyEl.setTop("-20000px");
38345 showAction : function(){
38346 this.bodyEl.setStyle("position", "relative");
38347 this.bodyEl.setTop("");
38348 this.bodyEl.setLeft("");
38349 this.bodyEl.show();
38353 * Set the tooltip for the tab.
38354 * @param {String} tooltip The tab's tooltip
38356 setTooltip : function(text){
38357 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38358 this.textEl.dom.qtip = text;
38359 this.textEl.dom.removeAttribute('title');
38361 this.textEl.dom.title = text;
38365 onTabClick : function(e){
38366 e.preventDefault();
38367 this.tabPanel.activate(this.id);
38370 onTabMouseDown : function(e){
38371 e.preventDefault();
38372 this.tabPanel.activate(this.id);
38375 getWidth : function(){
38376 return this.inner.getWidth();
38379 setWidth : function(width){
38380 var iwidth = width - this.pnode.getPadding("lr");
38381 this.inner.setWidth(iwidth);
38382 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38383 this.pnode.setWidth(width);
38387 * Show or hide the tab
38388 * @param {Boolean} hidden True to hide or false to show.
38390 setHidden : function(hidden){
38391 this.hidden = hidden;
38392 this.pnode.setStyle("display", hidden ? "none" : "");
38396 * Returns true if this tab is "hidden"
38397 * @return {Boolean}
38399 isHidden : function(){
38400 return this.hidden;
38404 * Returns the text for this tab
38407 getText : function(){
38411 autoSize : function(){
38412 //this.el.beginMeasure();
38413 this.textEl.setWidth(1);
38415 * #2804 [new] Tabs in Roojs
38416 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38418 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38419 //this.el.endMeasure();
38423 * Sets the text for the tab (Note: this also sets the tooltip text)
38424 * @param {String} text The tab's text and tooltip
38426 setText : function(text){
38428 this.textEl.update(text);
38429 this.setTooltip(text);
38430 //if(!this.tabPanel.resizeTabs){
38431 // this.autoSize();
38435 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38437 activate : function(){
38438 this.tabPanel.activate(this.id);
38442 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38444 disable : function(){
38445 if(this.tabPanel.active != this){
38446 this.disabled = true;
38447 this.pnode.addClass("disabled");
38452 * Enables this TabPanelItem if it was previously disabled.
38454 enable : function(){
38455 this.disabled = false;
38456 this.pnode.removeClass("disabled");
38460 * Sets the content for this TabPanelItem.
38461 * @param {String} content The content
38462 * @param {Boolean} loadScripts true to look for and load scripts
38464 setContent : function(content, loadScripts){
38465 this.bodyEl.update(content, loadScripts);
38469 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38470 * @return {Roo.UpdateManager} The UpdateManager
38472 getUpdateManager : function(){
38473 return this.bodyEl.getUpdateManager();
38477 * Set a URL to be used to load the content for this TabPanelItem.
38478 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38479 * @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)
38480 * @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)
38481 * @return {Roo.UpdateManager} The UpdateManager
38483 setUrl : function(url, params, loadOnce){
38484 if(this.refreshDelegate){
38485 this.un('activate', this.refreshDelegate);
38487 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38488 this.on("activate", this.refreshDelegate);
38489 return this.bodyEl.getUpdateManager();
38493 _handleRefresh : function(url, params, loadOnce){
38494 if(!loadOnce || !this.loaded){
38495 var updater = this.bodyEl.getUpdateManager();
38496 updater.update(url, params, this._setLoaded.createDelegate(this));
38501 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38502 * Will fail silently if the setUrl method has not been called.
38503 * This does not activate the panel, just updates its content.
38505 refresh : function(){
38506 if(this.refreshDelegate){
38507 this.loaded = false;
38508 this.refreshDelegate();
38513 _setLoaded : function(){
38514 this.loaded = true;
38518 closeClick : function(e){
38521 this.fireEvent("beforeclose", this, o);
38522 if(o.cancel !== true){
38523 this.tabPanel.removeTab(this.id);
38527 * The text displayed in the tooltip for the close icon.
38530 closeText : "Close this tab"
38533 * This script refer to:
38534 * Title: International Telephone Input
38535 * Author: Jack O'Connor
38536 * Code version: v12.1.12
38537 * Availability: https://github.com/jackocnr/intl-tel-input.git
38540 Roo.bootstrap.PhoneInputData = function() {
38543 "Afghanistan (افغانستان)",
38548 "Albania (Shqipëri)",
38553 "Algeria (الجزائر)",
38578 "Antigua and Barbuda",
38588 "Armenia (Հայաստան)",
38604 "Austria (Österreich)",
38609 "Azerbaijan (Azərbaycan)",
38619 "Bahrain (البحرين)",
38624 "Bangladesh (বাংলাদেশ)",
38634 "Belarus (Беларусь)",
38639 "Belgium (België)",
38669 "Bosnia and Herzegovina (Босна и Херцеговина)",
38684 "British Indian Ocean Territory",
38689 "British Virgin Islands",
38699 "Bulgaria (България)",
38709 "Burundi (Uburundi)",
38714 "Cambodia (កម្ពុជា)",
38719 "Cameroon (Cameroun)",
38728 ["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"]
38731 "Cape Verde (Kabu Verdi)",
38736 "Caribbean Netherlands",
38747 "Central African Republic (République centrafricaine)",
38767 "Christmas Island",
38773 "Cocos (Keeling) Islands",
38784 "Comoros (جزر القمر)",
38789 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38794 "Congo (Republic) (Congo-Brazzaville)",
38814 "Croatia (Hrvatska)",
38835 "Czech Republic (Česká republika)",
38840 "Denmark (Danmark)",
38855 "Dominican Republic (República Dominicana)",
38859 ["809", "829", "849"]
38877 "Equatorial Guinea (Guinea Ecuatorial)",
38897 "Falkland Islands (Islas Malvinas)",
38902 "Faroe Islands (Føroyar)",
38923 "French Guiana (Guyane française)",
38928 "French Polynesia (Polynésie française)",
38943 "Georgia (საქართველო)",
38948 "Germany (Deutschland)",
38968 "Greenland (Kalaallit Nunaat)",
39005 "Guinea-Bissau (Guiné Bissau)",
39030 "Hungary (Magyarország)",
39035 "Iceland (Ísland)",
39055 "Iraq (العراق)",
39071 "Israel (ישראל)",
39098 "Jordan (الأردن)",
39103 "Kazakhstan (Казахстан)",
39124 "Kuwait (الكويت)",
39129 "Kyrgyzstan (Кыргызстан)",
39139 "Latvia (Latvija)",
39144 "Lebanon (لبنان)",
39159 "Libya (ليبيا)",
39169 "Lithuania (Lietuva)",
39184 "Macedonia (FYROM) (Македонија)",
39189 "Madagascar (Madagasikara)",
39219 "Marshall Islands",
39229 "Mauritania (موريتانيا)",
39234 "Mauritius (Moris)",
39255 "Moldova (Republica Moldova)",
39265 "Mongolia (Монгол)",
39270 "Montenegro (Crna Gora)",
39280 "Morocco (المغرب)",
39286 "Mozambique (Moçambique)",
39291 "Myanmar (Burma) (မြန်မာ)",
39296 "Namibia (Namibië)",
39311 "Netherlands (Nederland)",
39316 "New Caledonia (Nouvelle-Calédonie)",
39351 "North Korea (조선 민주주의 인민 공화국)",
39356 "Northern Mariana Islands",
39372 "Pakistan (پاکستان)",
39382 "Palestine (فلسطين)",
39392 "Papua New Guinea",
39434 "Réunion (La Réunion)",
39440 "Romania (România)",
39456 "Saint Barthélemy",
39467 "Saint Kitts and Nevis",
39477 "Saint Martin (Saint-Martin (partie française))",
39483 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39488 "Saint Vincent and the Grenadines",
39503 "São Tomé and Príncipe (São Tomé e Príncipe)",
39508 "Saudi Arabia (المملكة العربية السعودية)",
39513 "Senegal (Sénégal)",
39543 "Slovakia (Slovensko)",
39548 "Slovenia (Slovenija)",
39558 "Somalia (Soomaaliya)",
39568 "South Korea (대한민국)",
39573 "South Sudan (جنوب السودان)",
39583 "Sri Lanka (ශ්රී ලංකාව)",
39588 "Sudan (السودان)",
39598 "Svalbard and Jan Mayen",
39609 "Sweden (Sverige)",
39614 "Switzerland (Schweiz)",
39619 "Syria (سوريا)",
39664 "Trinidad and Tobago",
39669 "Tunisia (تونس)",
39674 "Turkey (Türkiye)",
39684 "Turks and Caicos Islands",
39694 "U.S. Virgin Islands",
39704 "Ukraine (Україна)",
39709 "United Arab Emirates (الإمارات العربية المتحدة)",
39731 "Uzbekistan (Oʻzbekiston)",
39741 "Vatican City (Città del Vaticano)",
39752 "Vietnam (Việt Nam)",
39757 "Wallis and Futuna (Wallis-et-Futuna)",
39762 "Western Sahara (الصحراء الغربية)",
39768 "Yemen (اليمن)",
39792 * This script refer to:
39793 * Title: International Telephone Input
39794 * Author: Jack O'Connor
39795 * Code version: v12.1.12
39796 * Availability: https://github.com/jackocnr/intl-tel-input.git
39800 * @class Roo.bootstrap.PhoneInput
39801 * @extends Roo.bootstrap.TriggerField
39802 * An input with International dial-code selection
39804 * @cfg {String} defaultDialCode default '+852'
39805 * @cfg {Array} preferedCountries default []
39808 * Create a new PhoneInput.
39809 * @param {Object} config Configuration options
39812 Roo.bootstrap.PhoneInput = function(config) {
39813 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39816 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39818 listWidth: undefined,
39820 selectedClass: 'active',
39822 invalidClass : "has-warning",
39824 validClass: 'has-success',
39826 allowed: '0123456789',
39829 * @cfg {String} defaultDialCode The default dial code when initializing the input
39831 defaultDialCode: '+852',
39834 * @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
39836 preferedCountries: false,
39838 getAutoCreate : function()
39840 var data = Roo.bootstrap.PhoneInputData();
39841 var align = this.labelAlign || this.parentLabelAlign();
39844 this.allCountries = [];
39845 this.dialCodeMapping = [];
39847 for (var i = 0; i < data.length; i++) {
39849 this.allCountries[i] = {
39853 priority: c[3] || 0,
39854 areaCodes: c[4] || null
39856 this.dialCodeMapping[c[2]] = {
39859 priority: c[3] || 0,
39860 areaCodes: c[4] || null
39872 cls : 'form-control tel-input',
39873 autocomplete: 'new-password'
39876 var hiddenInput = {
39879 cls: 'hidden-tel-input'
39883 hiddenInput.name = this.name;
39886 if (this.disabled) {
39887 input.disabled = true;
39890 var flag_container = {
39907 cls: this.hasFeedback ? 'has-feedback' : '',
39913 cls: 'dial-code-holder',
39920 cls: 'roo-select2-container input-group',
39927 if (this.fieldLabel.length) {
39930 tooltip: 'This field is required'
39936 cls: 'control-label',
39942 html: this.fieldLabel
39945 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39951 if(this.indicatorpos == 'right') {
39952 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39959 if(align == 'left') {
39967 if(this.labelWidth > 12){
39968 label.style = "width: " + this.labelWidth + 'px';
39970 if(this.labelWidth < 13 && this.labelmd == 0){
39971 this.labelmd = this.labelWidth;
39973 if(this.labellg > 0){
39974 label.cls += ' col-lg-' + this.labellg;
39975 input.cls += ' col-lg-' + (12 - this.labellg);
39977 if(this.labelmd > 0){
39978 label.cls += ' col-md-' + this.labelmd;
39979 container.cls += ' col-md-' + (12 - this.labelmd);
39981 if(this.labelsm > 0){
39982 label.cls += ' col-sm-' + this.labelsm;
39983 container.cls += ' col-sm-' + (12 - this.labelsm);
39985 if(this.labelxs > 0){
39986 label.cls += ' col-xs-' + this.labelxs;
39987 container.cls += ' col-xs-' + (12 - this.labelxs);
39997 var settings = this;
39999 ['xs','sm','md','lg'].map(function(size){
40000 if (settings[size]) {
40001 cfg.cls += ' col-' + size + '-' + settings[size];
40005 this.store = new Roo.data.Store({
40006 proxy : new Roo.data.MemoryProxy({}),
40007 reader : new Roo.data.JsonReader({
40018 'name' : 'dialCode',
40022 'name' : 'priority',
40026 'name' : 'areaCodes',
40033 if(!this.preferedCountries) {
40034 this.preferedCountries = [
40041 var p = this.preferedCountries.reverse();
40044 for (var i = 0; i < p.length; i++) {
40045 for (var j = 0; j < this.allCountries.length; j++) {
40046 if(this.allCountries[j].iso2 == p[i]) {
40047 var t = this.allCountries[j];
40048 this.allCountries.splice(j,1);
40049 this.allCountries.unshift(t);
40055 this.store.proxy.data = {
40057 data: this.allCountries
40063 initEvents : function()
40066 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40068 this.indicator = this.indicatorEl();
40069 this.flag = this.flagEl();
40070 this.dialCodeHolder = this.dialCodeHolderEl();
40072 this.trigger = this.el.select('div.flag-box',true).first();
40073 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40078 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40079 _this.list.setWidth(lw);
40082 this.list.on('mouseover', this.onViewOver, this);
40083 this.list.on('mousemove', this.onViewMove, this);
40084 this.inputEl().on("keyup", this.onKeyUp, this);
40086 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40088 this.view = new Roo.View(this.list, this.tpl, {
40089 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40092 this.view.on('click', this.onViewClick, this);
40093 this.setValue(this.defaultDialCode);
40096 onTriggerClick : function(e)
40098 Roo.log('trigger click');
40103 if(this.isExpanded()){
40105 this.hasFocus = false;
40107 this.store.load({});
40108 this.hasFocus = true;
40113 isExpanded : function()
40115 return this.list.isVisible();
40118 collapse : function()
40120 if(!this.isExpanded()){
40124 Roo.get(document).un('mousedown', this.collapseIf, this);
40125 Roo.get(document).un('mousewheel', this.collapseIf, this);
40126 this.fireEvent('collapse', this);
40130 expand : function()
40134 if(this.isExpanded() || !this.hasFocus){
40138 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40139 this.list.setWidth(lw);
40142 this.restrictHeight();
40144 Roo.get(document).on('mousedown', this.collapseIf, this);
40145 Roo.get(document).on('mousewheel', this.collapseIf, this);
40147 this.fireEvent('expand', this);
40150 restrictHeight : function()
40152 this.list.alignTo(this.inputEl(), this.listAlign);
40153 this.list.alignTo(this.inputEl(), this.listAlign);
40156 onViewOver : function(e, t)
40158 if(this.inKeyMode){
40161 var item = this.view.findItemFromChild(t);
40164 var index = this.view.indexOf(item);
40165 this.select(index, false);
40170 onViewClick : function(view, doFocus, el, e)
40172 var index = this.view.getSelectedIndexes()[0];
40174 var r = this.store.getAt(index);
40177 this.onSelect(r, index);
40179 if(doFocus !== false && !this.blockFocus){
40180 this.inputEl().focus();
40184 onViewMove : function(e, t)
40186 this.inKeyMode = false;
40189 select : function(index, scrollIntoView)
40191 this.selectedIndex = index;
40192 this.view.select(index);
40193 if(scrollIntoView !== false){
40194 var el = this.view.getNode(index);
40196 this.list.scrollChildIntoView(el, false);
40201 createList : function()
40203 this.list = Roo.get(document.body).createChild({
40205 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40206 style: 'display:none'
40209 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40212 collapseIf : function(e)
40214 var in_combo = e.within(this.el);
40215 var in_list = e.within(this.list);
40216 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40218 if (in_combo || in_list || is_list) {
40224 onSelect : function(record, index)
40226 if(this.fireEvent('beforeselect', this, record, index) !== false){
40228 this.setFlagClass(record.data.iso2);
40229 this.setDialCode(record.data.dialCode);
40230 this.hasFocus = false;
40232 this.fireEvent('select', this, record, index);
40236 flagEl : function()
40238 var flag = this.el.select('div.flag',true).first();
40245 dialCodeHolderEl : function()
40247 var d = this.el.select('input.dial-code-holder',true).first();
40254 setDialCode : function(v)
40256 this.dialCodeHolder.dom.value = '+'+v;
40259 setFlagClass : function(n)
40261 this.flag.dom.className = 'flag '+n;
40264 getValue : function()
40266 var v = this.inputEl().getValue();
40267 if(this.dialCodeHolder) {
40268 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40273 setValue : function(v)
40275 var d = this.getDialCode(v);
40277 //invalid dial code
40278 if(v.length == 0 || !d || d.length == 0) {
40280 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40281 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40287 this.setFlagClass(this.dialCodeMapping[d].iso2);
40288 this.setDialCode(d);
40289 this.inputEl().dom.value = v.replace('+'+d,'');
40290 this.hiddenEl().dom.value = this.getValue();
40295 getDialCode : function(v)
40299 if (v.length == 0) {
40300 return this.dialCodeHolder.dom.value;
40304 if (v.charAt(0) != "+") {
40307 var numericChars = "";
40308 for (var i = 1; i < v.length; i++) {
40309 var c = v.charAt(i);
40312 if (this.dialCodeMapping[numericChars]) {
40313 dialCode = v.substr(1, i);
40315 if (numericChars.length == 4) {
40325 this.setValue(this.defaultDialCode);
40329 hiddenEl : function()
40331 return this.el.select('input.hidden-tel-input',true).first();
40334 onKeyUp : function(e){
40336 var k = e.getKey();
40337 var c = e.getCharCode();
40340 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40341 this.allowed.indexOf(String.fromCharCode(c)) === -1
40346 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40349 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40353 this.setValue(this.getValue());
40358 * @class Roo.bootstrap.MoneyField
40359 * @extends Roo.bootstrap.ComboBox
40360 * Bootstrap MoneyField class
40363 * Create a new MoneyField.
40364 * @param {Object} config Configuration options
40367 Roo.bootstrap.MoneyField = function(config) {
40369 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40373 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40376 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40378 allowDecimals : true,
40380 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40382 decimalSeparator : ".",
40384 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40386 decimalPrecision : 0,
40388 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40390 allowNegative : true,
40392 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40396 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40398 minValue : Number.NEGATIVE_INFINITY,
40400 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40402 maxValue : Number.MAX_VALUE,
40404 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40406 minText : "The minimum value for this field is {0}",
40408 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40410 maxText : "The maximum value for this field is {0}",
40412 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40413 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40415 nanText : "{0} is not a valid number",
40417 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40421 * @cfg {String} defaults currency of the MoneyField
40422 * value should be in lkey
40424 defaultCurrency : false,
40426 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40428 thousandsDelimiter : false,
40438 getAutoCreate : function()
40440 var align = this.labelAlign || this.parentLabelAlign();
40452 cls : 'form-control roo-money-amount-input',
40453 autocomplete: 'new-password'
40456 var hiddenInput = {
40460 cls: 'hidden-number-input'
40464 hiddenInput.name = this.name;
40467 if (this.disabled) {
40468 input.disabled = true;
40471 var clg = 12 - this.inputlg;
40472 var cmd = 12 - this.inputmd;
40473 var csm = 12 - this.inputsm;
40474 var cxs = 12 - this.inputxs;
40478 cls : 'row roo-money-field',
40482 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40486 cls: 'roo-select2-container input-group',
40490 cls : 'form-control roo-money-currency-input',
40491 autocomplete: 'new-password',
40493 name : this.currencyName
40497 cls : 'input-group-addon',
40511 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40515 cls: this.hasFeedback ? 'has-feedback' : '',
40526 if (this.fieldLabel.length) {
40529 tooltip: 'This field is required'
40535 cls: 'control-label',
40541 html: this.fieldLabel
40544 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40550 if(this.indicatorpos == 'right') {
40551 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40558 if(align == 'left') {
40566 if(this.labelWidth > 12){
40567 label.style = "width: " + this.labelWidth + 'px';
40569 if(this.labelWidth < 13 && this.labelmd == 0){
40570 this.labelmd = this.labelWidth;
40572 if(this.labellg > 0){
40573 label.cls += ' col-lg-' + this.labellg;
40574 input.cls += ' col-lg-' + (12 - this.labellg);
40576 if(this.labelmd > 0){
40577 label.cls += ' col-md-' + this.labelmd;
40578 container.cls += ' col-md-' + (12 - this.labelmd);
40580 if(this.labelsm > 0){
40581 label.cls += ' col-sm-' + this.labelsm;
40582 container.cls += ' col-sm-' + (12 - this.labelsm);
40584 if(this.labelxs > 0){
40585 label.cls += ' col-xs-' + this.labelxs;
40586 container.cls += ' col-xs-' + (12 - this.labelxs);
40597 var settings = this;
40599 ['xs','sm','md','lg'].map(function(size){
40600 if (settings[size]) {
40601 cfg.cls += ' col-' + size + '-' + settings[size];
40608 initEvents : function()
40610 this.indicator = this.indicatorEl();
40612 this.initCurrencyEvent();
40614 this.initNumberEvent();
40617 initCurrencyEvent : function()
40620 throw "can not find store for combo";
40623 this.store = Roo.factory(this.store, Roo.data);
40624 this.store.parent = this;
40628 this.triggerEl = this.el.select('.input-group-addon', true).first();
40630 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40635 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40636 _this.list.setWidth(lw);
40639 this.list.on('mouseover', this.onViewOver, this);
40640 this.list.on('mousemove', this.onViewMove, this);
40641 this.list.on('scroll', this.onViewScroll, this);
40644 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40647 this.view = new Roo.View(this.list, this.tpl, {
40648 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40651 this.view.on('click', this.onViewClick, this);
40653 this.store.on('beforeload', this.onBeforeLoad, this);
40654 this.store.on('load', this.onLoad, this);
40655 this.store.on('loadexception', this.onLoadException, this);
40657 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40658 "up" : function(e){
40659 this.inKeyMode = true;
40663 "down" : function(e){
40664 if(!this.isExpanded()){
40665 this.onTriggerClick();
40667 this.inKeyMode = true;
40672 "enter" : function(e){
40675 if(this.fireEvent("specialkey", this, e)){
40676 this.onViewClick(false);
40682 "esc" : function(e){
40686 "tab" : function(e){
40689 if(this.fireEvent("specialkey", this, e)){
40690 this.onViewClick(false);
40698 doRelay : function(foo, bar, hname){
40699 if(hname == 'down' || this.scope.isExpanded()){
40700 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40708 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40712 initNumberEvent : function(e)
40714 this.inputEl().on("keydown" , this.fireKey, this);
40715 this.inputEl().on("focus", this.onFocus, this);
40716 this.inputEl().on("blur", this.onBlur, this);
40718 this.inputEl().relayEvent('keyup', this);
40720 if(this.indicator){
40721 this.indicator.addClass('invisible');
40724 this.originalValue = this.getValue();
40726 if(this.validationEvent == 'keyup'){
40727 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40728 this.inputEl().on('keyup', this.filterValidation, this);
40730 else if(this.validationEvent !== false){
40731 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40734 if(this.selectOnFocus){
40735 this.on("focus", this.preFocus, this);
40738 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40739 this.inputEl().on("keypress", this.filterKeys, this);
40741 this.inputEl().relayEvent('keypress', this);
40744 var allowed = "0123456789";
40746 if(this.allowDecimals){
40747 allowed += this.decimalSeparator;
40750 if(this.allowNegative){
40754 if(this.thousandsDelimiter) {
40758 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40760 var keyPress = function(e){
40762 var k = e.getKey();
40764 var c = e.getCharCode();
40767 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40768 allowed.indexOf(String.fromCharCode(c)) === -1
40774 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40778 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40783 this.inputEl().on("keypress", keyPress, this);
40787 onTriggerClick : function(e)
40794 this.loadNext = false;
40796 if(this.isExpanded()){
40801 this.hasFocus = true;
40803 if(this.triggerAction == 'all') {
40804 this.doQuery(this.allQuery, true);
40808 this.doQuery(this.getRawValue());
40811 getCurrency : function()
40813 var v = this.currencyEl().getValue();
40818 restrictHeight : function()
40820 this.list.alignTo(this.currencyEl(), this.listAlign);
40821 this.list.alignTo(this.currencyEl(), this.listAlign);
40824 onViewClick : function(view, doFocus, el, e)
40826 var index = this.view.getSelectedIndexes()[0];
40828 var r = this.store.getAt(index);
40831 this.onSelect(r, index);
40835 onSelect : function(record, index){
40837 if(this.fireEvent('beforeselect', this, record, index) !== false){
40839 this.setFromCurrencyData(index > -1 ? record.data : false);
40843 this.fireEvent('select', this, record, index);
40847 setFromCurrencyData : function(o)
40851 this.lastCurrency = o;
40853 if (this.currencyField) {
40854 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40856 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40859 this.lastSelectionText = currency;
40861 //setting default currency
40862 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40863 this.setCurrency(this.defaultCurrency);
40867 this.setCurrency(currency);
40870 setFromData : function(o)
40874 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40876 this.setFromCurrencyData(c);
40881 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40883 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40886 this.setValue(value);
40890 setCurrency : function(v)
40892 this.currencyValue = v;
40895 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40900 setValue : function(v)
40902 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40908 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40910 this.inputEl().dom.value = (v == '') ? '' :
40911 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40913 if(!this.allowZero && v === '0') {
40914 this.hiddenEl().dom.value = '';
40915 this.inputEl().dom.value = '';
40922 getRawValue : function()
40924 var v = this.inputEl().getValue();
40929 getValue : function()
40931 return this.fixPrecision(this.parseValue(this.getRawValue()));
40934 parseValue : function(value)
40936 if(this.thousandsDelimiter) {
40938 r = new RegExp(",", "g");
40939 value = value.replace(r, "");
40942 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40943 return isNaN(value) ? '' : value;
40947 fixPrecision : function(value)
40949 if(this.thousandsDelimiter) {
40951 r = new RegExp(",", "g");
40952 value = value.replace(r, "");
40955 var nan = isNaN(value);
40957 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40958 return nan ? '' : value;
40960 return parseFloat(value).toFixed(this.decimalPrecision);
40963 decimalPrecisionFcn : function(v)
40965 return Math.floor(v);
40968 validateValue : function(value)
40970 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40974 var num = this.parseValue(value);
40977 this.markInvalid(String.format(this.nanText, value));
40981 if(num < this.minValue){
40982 this.markInvalid(String.format(this.minText, this.minValue));
40986 if(num > this.maxValue){
40987 this.markInvalid(String.format(this.maxText, this.maxValue));
40994 validate : function()
40996 if(this.disabled || this.allowBlank){
41001 var currency = this.getCurrency();
41003 if(this.validateValue(this.getRawValue()) && currency.length){
41008 this.markInvalid();
41012 getName: function()
41017 beforeBlur : function()
41023 var v = this.parseValue(this.getRawValue());
41030 onBlur : function()
41034 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41035 //this.el.removeClass(this.focusClass);
41038 this.hasFocus = false;
41040 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41044 var v = this.getValue();
41046 if(String(v) !== String(this.startValue)){
41047 this.fireEvent('change', this, v, this.startValue);
41050 this.fireEvent("blur", this);
41053 inputEl : function()
41055 return this.el.select('.roo-money-amount-input', true).first();
41058 currencyEl : function()
41060 return this.el.select('.roo-money-currency-input', true).first();
41063 hiddenEl : function()
41065 return this.el.select('input.hidden-number-input',true).first();