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;
7094 this.CSS.updateRule(thSelector, "display", "");
7095 this.CSS.updateRule(tdSelector, "display", "");
7098 this.CSS.updateRule(thSelector, "display", "none");
7099 this.CSS.updateRule(tdSelector, "display", "none");
7102 this.onHeaderChange();
7119 * @class Roo.bootstrap.TableCell
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap TableCell class
7122 * @cfg {String} html cell contain text
7123 * @cfg {String} cls cell class
7124 * @cfg {String} tag cell tag (td|th) default td
7125 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7126 * @cfg {String} align Aligns the content in a cell
7127 * @cfg {String} axis Categorizes cells
7128 * @cfg {String} bgcolor Specifies the background color of a cell
7129 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7130 * @cfg {Number} colspan Specifies the number of columns a cell should span
7131 * @cfg {String} headers Specifies one or more header cells a cell is related to
7132 * @cfg {Number} height Sets the height of a cell
7133 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7134 * @cfg {Number} rowspan Sets the number of rows a cell should span
7135 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7136 * @cfg {String} valign Vertical aligns the content in a cell
7137 * @cfg {Number} width Specifies the width of a cell
7140 * Create a new TableCell
7141 * @param {Object} config The config object
7144 Roo.bootstrap.TableCell = function(config){
7145 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7148 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7168 getAutoCreate : function(){
7169 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7189 cfg.align=this.align
7195 cfg.bgcolor=this.bgcolor
7198 cfg.charoff=this.charoff
7201 cfg.colspan=this.colspan
7204 cfg.headers=this.headers
7207 cfg.height=this.height
7210 cfg.nowrap=this.nowrap
7213 cfg.rowspan=this.rowspan
7216 cfg.scope=this.scope
7219 cfg.valign=this.valign
7222 cfg.width=this.width
7241 * @class Roo.bootstrap.TableRow
7242 * @extends Roo.bootstrap.Component
7243 * Bootstrap TableRow class
7244 * @cfg {String} cls row class
7245 * @cfg {String} align Aligns the content in a table row
7246 * @cfg {String} bgcolor Specifies a background color for a table row
7247 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7248 * @cfg {String} valign Vertical aligns the content in a table row
7251 * Create a new TableRow
7252 * @param {Object} config The config object
7255 Roo.bootstrap.TableRow = function(config){
7256 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7259 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7267 getAutoCreate : function(){
7268 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7278 cfg.align = this.align;
7281 cfg.bgcolor = this.bgcolor;
7284 cfg.charoff = this.charoff;
7287 cfg.valign = this.valign;
7305 * @class Roo.bootstrap.TableBody
7306 * @extends Roo.bootstrap.Component
7307 * Bootstrap TableBody class
7308 * @cfg {String} cls element class
7309 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7310 * @cfg {String} align Aligns the content inside the element
7311 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7312 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7315 * Create a new TableBody
7316 * @param {Object} config The config object
7319 Roo.bootstrap.TableBody = function(config){
7320 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7323 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7331 getAutoCreate : function(){
7332 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7346 cfg.align = this.align;
7349 cfg.charoff = this.charoff;
7352 cfg.valign = this.valign;
7359 // initEvents : function()
7366 // this.store = Roo.factory(this.store, Roo.data);
7367 // this.store.on('load', this.onLoad, this);
7369 // this.store.load();
7373 // onLoad: function ()
7375 // this.fireEvent('load', this);
7385 * Ext JS Library 1.1.1
7386 * Copyright(c) 2006-2007, Ext JS, LLC.
7388 * Originally Released Under LGPL - original licence link has changed is not relivant.
7391 * <script type="text/javascript">
7394 // as we use this in bootstrap.
7395 Roo.namespace('Roo.form');
7397 * @class Roo.form.Action
7398 * Internal Class used to handle form actions
7400 * @param {Roo.form.BasicForm} el The form element or its id
7401 * @param {Object} config Configuration options
7406 // define the action interface
7407 Roo.form.Action = function(form, options){
7409 this.options = options || {};
7412 * Client Validation Failed
7415 Roo.form.Action.CLIENT_INVALID = 'client';
7417 * Server Validation Failed
7420 Roo.form.Action.SERVER_INVALID = 'server';
7422 * Connect to Server Failed
7425 Roo.form.Action.CONNECT_FAILURE = 'connect';
7427 * Reading Data from Server Failed
7430 Roo.form.Action.LOAD_FAILURE = 'load';
7432 Roo.form.Action.prototype = {
7434 failureType : undefined,
7435 response : undefined,
7439 run : function(options){
7444 success : function(response){
7449 handleResponse : function(response){
7453 // default connection failure
7454 failure : function(response){
7456 this.response = response;
7457 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7458 this.form.afterAction(this, false);
7461 processResponse : function(response){
7462 this.response = response;
7463 if(!response.responseText){
7466 this.result = this.handleResponse(response);
7470 // utility functions used internally
7471 getUrl : function(appendParams){
7472 var url = this.options.url || this.form.url || this.form.el.dom.action;
7474 var p = this.getParams();
7476 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7482 getMethod : function(){
7483 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7486 getParams : function(){
7487 var bp = this.form.baseParams;
7488 var p = this.options.params;
7490 if(typeof p == "object"){
7491 p = Roo.urlEncode(Roo.applyIf(p, bp));
7492 }else if(typeof p == 'string' && bp){
7493 p += '&' + Roo.urlEncode(bp);
7496 p = Roo.urlEncode(bp);
7501 createCallback : function(){
7503 success: this.success,
7504 failure: this.failure,
7506 timeout: (this.form.timeout*1000),
7507 upload: this.form.fileUpload ? this.success : undefined
7512 Roo.form.Action.Submit = function(form, options){
7513 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7516 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7519 haveProgress : false,
7520 uploadComplete : false,
7522 // uploadProgress indicator.
7523 uploadProgress : function()
7525 if (!this.form.progressUrl) {
7529 if (!this.haveProgress) {
7530 Roo.MessageBox.progress("Uploading", "Uploading");
7532 if (this.uploadComplete) {
7533 Roo.MessageBox.hide();
7537 this.haveProgress = true;
7539 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7541 var c = new Roo.data.Connection();
7543 url : this.form.progressUrl,
7548 success : function(req){
7549 //console.log(data);
7553 rdata = Roo.decode(req.responseText)
7555 Roo.log("Invalid data from server..");
7559 if (!rdata || !rdata.success) {
7561 Roo.MessageBox.alert(Roo.encode(rdata));
7564 var data = rdata.data;
7566 if (this.uploadComplete) {
7567 Roo.MessageBox.hide();
7572 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7573 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7576 this.uploadProgress.defer(2000,this);
7579 failure: function(data) {
7580 Roo.log('progress url failed ');
7591 // run get Values on the form, so it syncs any secondary forms.
7592 this.form.getValues();
7594 var o = this.options;
7595 var method = this.getMethod();
7596 var isPost = method == 'POST';
7597 if(o.clientValidation === false || this.form.isValid()){
7599 if (this.form.progressUrl) {
7600 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7601 (new Date() * 1) + '' + Math.random());
7606 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7607 form:this.form.el.dom,
7608 url:this.getUrl(!isPost),
7610 params:isPost ? this.getParams() : null,
7611 isUpload: this.form.fileUpload
7614 this.uploadProgress();
7616 }else if (o.clientValidation !== false){ // client validation failed
7617 this.failureType = Roo.form.Action.CLIENT_INVALID;
7618 this.form.afterAction(this, false);
7622 success : function(response)
7624 this.uploadComplete= true;
7625 if (this.haveProgress) {
7626 Roo.MessageBox.hide();
7630 var result = this.processResponse(response);
7631 if(result === true || result.success){
7632 this.form.afterAction(this, true);
7636 this.form.markInvalid(result.errors);
7637 this.failureType = Roo.form.Action.SERVER_INVALID;
7639 this.form.afterAction(this, false);
7641 failure : function(response)
7643 this.uploadComplete= true;
7644 if (this.haveProgress) {
7645 Roo.MessageBox.hide();
7648 this.response = response;
7649 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7650 this.form.afterAction(this, false);
7653 handleResponse : function(response){
7654 if(this.form.errorReader){
7655 var rs = this.form.errorReader.read(response);
7658 for(var i = 0, len = rs.records.length; i < len; i++) {
7659 var r = rs.records[i];
7663 if(errors.length < 1){
7667 success : rs.success,
7673 ret = Roo.decode(response.responseText);
7677 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7687 Roo.form.Action.Load = function(form, options){
7688 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7689 this.reader = this.form.reader;
7692 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7697 Roo.Ajax.request(Roo.apply(
7698 this.createCallback(), {
7699 method:this.getMethod(),
7700 url:this.getUrl(false),
7701 params:this.getParams()
7705 success : function(response){
7707 var result = this.processResponse(response);
7708 if(result === true || !result.success || !result.data){
7709 this.failureType = Roo.form.Action.LOAD_FAILURE;
7710 this.form.afterAction(this, false);
7713 this.form.clearInvalid();
7714 this.form.setValues(result.data);
7715 this.form.afterAction(this, true);
7718 handleResponse : function(response){
7719 if(this.form.reader){
7720 var rs = this.form.reader.read(response);
7721 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7723 success : rs.success,
7727 return Roo.decode(response.responseText);
7731 Roo.form.Action.ACTION_TYPES = {
7732 'load' : Roo.form.Action.Load,
7733 'submit' : Roo.form.Action.Submit
7742 * @class Roo.bootstrap.Form
7743 * @extends Roo.bootstrap.Component
7744 * Bootstrap Form class
7745 * @cfg {String} method GET | POST (default POST)
7746 * @cfg {String} labelAlign top | left (default top)
7747 * @cfg {String} align left | right - for navbars
7748 * @cfg {Boolean} loadMask load mask when submit (default true)
7753 * @param {Object} config The config object
7757 Roo.bootstrap.Form = function(config){
7759 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7761 Roo.bootstrap.Form.popover.apply();
7765 * @event clientvalidation
7766 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7767 * @param {Form} this
7768 * @param {Boolean} valid true if the form has passed client-side validation
7770 clientvalidation: true,
7772 * @event beforeaction
7773 * Fires before any action is performed. Return false to cancel the action.
7774 * @param {Form} this
7775 * @param {Action} action The action to be performed
7779 * @event actionfailed
7780 * Fires when an action fails.
7781 * @param {Form} this
7782 * @param {Action} action The action that failed
7784 actionfailed : true,
7786 * @event actioncomplete
7787 * Fires when an action is completed.
7788 * @param {Form} this
7789 * @param {Action} action The action that completed
7791 actioncomplete : true
7795 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7798 * @cfg {String} method
7799 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7804 * The URL to use for form actions if one isn't supplied in the action options.
7807 * @cfg {Boolean} fileUpload
7808 * Set to true if this form is a file upload.
7812 * @cfg {Object} baseParams
7813 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7817 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7821 * @cfg {Sting} align (left|right) for navbar forms
7826 activeAction : null,
7829 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7830 * element by passing it or its id or mask the form itself by passing in true.
7833 waitMsgTarget : false,
7838 * @cfg {Boolean} errorMask (true|false) default false
7843 * @cfg {Number} maskOffset Default 100
7848 * @cfg {Boolean} maskBody
7852 getAutoCreate : function(){
7856 method : this.method || 'POST',
7857 id : this.id || Roo.id(),
7860 if (this.parent().xtype.match(/^Nav/)) {
7861 cfg.cls = 'navbar-form navbar-' + this.align;
7865 if (this.labelAlign == 'left' ) {
7866 cfg.cls += ' form-horizontal';
7872 initEvents : function()
7874 this.el.on('submit', this.onSubmit, this);
7875 // this was added as random key presses on the form where triggering form submit.
7876 this.el.on('keypress', function(e) {
7877 if (e.getCharCode() != 13) {
7880 // we might need to allow it for textareas.. and some other items.
7881 // check e.getTarget().
7883 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7887 Roo.log("keypress blocked");
7895 onSubmit : function(e){
7900 * Returns true if client-side validation on the form is successful.
7903 isValid : function(){
7904 var items = this.getItems();
7908 items.each(function(f){
7914 Roo.log('invalid field: ' + f.name);
7918 if(!target && f.el.isVisible(true)){
7924 if(this.errorMask && !valid){
7925 Roo.bootstrap.Form.popover.mask(this, target);
7932 * Returns true if any fields in this form have changed since their original load.
7935 isDirty : function(){
7937 var items = this.getItems();
7938 items.each(function(f){
7948 * Performs a predefined action (submit or load) or custom actions you define on this form.
7949 * @param {String} actionName The name of the action type
7950 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7951 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7952 * accept other config options):
7954 Property Type Description
7955 ---------------- --------------- ----------------------------------------------------------------------------------
7956 url String The url for the action (defaults to the form's url)
7957 method String The form method to use (defaults to the form's method, or POST if not defined)
7958 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7959 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7960 validate the form on the client (defaults to false)
7962 * @return {BasicForm} this
7964 doAction : function(action, options){
7965 if(typeof action == 'string'){
7966 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7968 if(this.fireEvent('beforeaction', this, action) !== false){
7969 this.beforeAction(action);
7970 action.run.defer(100, action);
7976 beforeAction : function(action){
7977 var o = action.options;
7982 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7984 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7987 // not really supported yet.. ??
7989 //if(this.waitMsgTarget === true){
7990 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7991 //}else if(this.waitMsgTarget){
7992 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7993 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7995 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8001 afterAction : function(action, success){
8002 this.activeAction = null;
8003 var o = action.options;
8008 Roo.get(document.body).unmask();
8014 //if(this.waitMsgTarget === true){
8015 // this.el.unmask();
8016 //}else if(this.waitMsgTarget){
8017 // this.waitMsgTarget.unmask();
8019 // Roo.MessageBox.updateProgress(1);
8020 // Roo.MessageBox.hide();
8027 Roo.callback(o.success, o.scope, [this, action]);
8028 this.fireEvent('actioncomplete', this, action);
8032 // failure condition..
8033 // we have a scenario where updates need confirming.
8034 // eg. if a locking scenario exists..
8035 // we look for { errors : { needs_confirm : true }} in the response.
8037 (typeof(action.result) != 'undefined') &&
8038 (typeof(action.result.errors) != 'undefined') &&
8039 (typeof(action.result.errors.needs_confirm) != 'undefined')
8042 Roo.log("not supported yet");
8045 Roo.MessageBox.confirm(
8046 "Change requires confirmation",
8047 action.result.errorMsg,
8052 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8062 Roo.callback(o.failure, o.scope, [this, action]);
8063 // show an error message if no failed handler is set..
8064 if (!this.hasListener('actionfailed')) {
8065 Roo.log("need to add dialog support");
8067 Roo.MessageBox.alert("Error",
8068 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8069 action.result.errorMsg :
8070 "Saving Failed, please check your entries or try again"
8075 this.fireEvent('actionfailed', this, action);
8080 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8081 * @param {String} id The value to search for
8084 findField : function(id){
8085 var items = this.getItems();
8086 var field = items.get(id);
8088 items.each(function(f){
8089 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8096 return field || null;
8099 * Mark fields in this form invalid in bulk.
8100 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8101 * @return {BasicForm} this
8103 markInvalid : function(errors){
8104 if(errors instanceof Array){
8105 for(var i = 0, len = errors.length; i < len; i++){
8106 var fieldError = errors[i];
8107 var f = this.findField(fieldError.id);
8109 f.markInvalid(fieldError.msg);
8115 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8116 field.markInvalid(errors[id]);
8120 //Roo.each(this.childForms || [], function (f) {
8121 // f.markInvalid(errors);
8128 * Set values for fields in this form in bulk.
8129 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8130 * @return {BasicForm} this
8132 setValues : function(values){
8133 if(values instanceof Array){ // array of objects
8134 for(var i = 0, len = values.length; i < len; i++){
8136 var f = this.findField(v.id);
8138 f.setValue(v.value);
8139 if(this.trackResetOnLoad){
8140 f.originalValue = f.getValue();
8144 }else{ // object hash
8147 if(typeof values[id] != 'function' && (field = this.findField(id))){
8149 if (field.setFromData &&
8151 field.displayField &&
8152 // combos' with local stores can
8153 // be queried via setValue()
8154 // to set their value..
8155 (field.store && !field.store.isLocal)
8159 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8160 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8161 field.setFromData(sd);
8163 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8165 field.setFromData(values);
8168 field.setValue(values[id]);
8172 if(this.trackResetOnLoad){
8173 field.originalValue = field.getValue();
8179 //Roo.each(this.childForms || [], function (f) {
8180 // f.setValues(values);
8187 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8188 * they are returned as an array.
8189 * @param {Boolean} asString
8192 getValues : function(asString){
8193 //if (this.childForms) {
8194 // copy values from the child forms
8195 // Roo.each(this.childForms, function (f) {
8196 // this.setValues(f.getValues());
8202 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8203 if(asString === true){
8206 return Roo.urlDecode(fs);
8210 * Returns the fields in this form as an object with key/value pairs.
8211 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8214 getFieldValues : function(with_hidden)
8216 var items = this.getItems();
8218 items.each(function(f){
8224 var v = f.getValue();
8226 if (f.inputType =='radio') {
8227 if (typeof(ret[f.getName()]) == 'undefined') {
8228 ret[f.getName()] = ''; // empty..
8231 if (!f.el.dom.checked) {
8239 if(f.xtype == 'MoneyField'){
8240 ret[f.currencyName] = f.getCurrency();
8243 // not sure if this supported any more..
8244 if ((typeof(v) == 'object') && f.getRawValue) {
8245 v = f.getRawValue() ; // dates..
8247 // combo boxes where name != hiddenName...
8248 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8249 ret[f.name] = f.getRawValue();
8251 ret[f.getName()] = v;
8258 * Clears all invalid messages in this form.
8259 * @return {BasicForm} this
8261 clearInvalid : function(){
8262 var items = this.getItems();
8264 items.each(function(f){
8273 * @return {BasicForm} this
8276 var items = this.getItems();
8277 items.each(function(f){
8281 Roo.each(this.childForms || [], function (f) {
8289 getItems : function()
8291 var r=new Roo.util.MixedCollection(false, function(o){
8292 return o.id || (o.id = Roo.id());
8294 var iter = function(el) {
8301 Roo.each(el.items,function(e) {
8310 hideFields : function(items)
8312 Roo.each(items, function(i){
8314 var f = this.findField(i);
8325 showFields : function(items)
8327 Roo.each(items, function(i){
8329 var f = this.findField(i);
8342 Roo.apply(Roo.bootstrap.Form, {
8369 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8370 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8371 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8372 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8375 this.maskEl.top.enableDisplayMode("block");
8376 this.maskEl.left.enableDisplayMode("block");
8377 this.maskEl.bottom.enableDisplayMode("block");
8378 this.maskEl.right.enableDisplayMode("block");
8380 this.toolTip = new Roo.bootstrap.Tooltip({
8381 cls : 'roo-form-error-popover',
8383 'left' : ['r-l', [-2,0], 'right'],
8384 'right' : ['l-r', [2,0], 'left'],
8385 'bottom' : ['tl-bl', [0,2], 'top'],
8386 'top' : [ 'bl-tl', [0,-2], 'bottom']
8390 this.toolTip.render(Roo.get(document.body));
8392 this.toolTip.el.enableDisplayMode("block");
8394 Roo.get(document.body).on('click', function(){
8398 Roo.get(document.body).on('touchstart', function(){
8402 this.isApplied = true
8405 mask : function(form, target)
8409 this.target = target;
8411 if(!this.form.errorMask || !target.el){
8415 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8417 Roo.log(scrollable);
8419 var ot = this.target.el.calcOffsetsTo(scrollable);
8421 var scrollTo = ot[1] - this.form.maskOffset;
8423 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8425 scrollable.scrollTo('top', scrollTo);
8427 var box = this.target.el.getBox();
8429 var zIndex = Roo.bootstrap.Modal.zIndex++;
8432 this.maskEl.top.setStyle('position', 'absolute');
8433 this.maskEl.top.setStyle('z-index', zIndex);
8434 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8435 this.maskEl.top.setLeft(0);
8436 this.maskEl.top.setTop(0);
8437 this.maskEl.top.show();
8439 this.maskEl.left.setStyle('position', 'absolute');
8440 this.maskEl.left.setStyle('z-index', zIndex);
8441 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8442 this.maskEl.left.setLeft(0);
8443 this.maskEl.left.setTop(box.y - this.padding);
8444 this.maskEl.left.show();
8446 this.maskEl.bottom.setStyle('position', 'absolute');
8447 this.maskEl.bottom.setStyle('z-index', zIndex);
8448 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8449 this.maskEl.bottom.setLeft(0);
8450 this.maskEl.bottom.setTop(box.bottom + this.padding);
8451 this.maskEl.bottom.show();
8453 this.maskEl.right.setStyle('position', 'absolute');
8454 this.maskEl.right.setStyle('z-index', zIndex);
8455 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8456 this.maskEl.right.setLeft(box.right + this.padding);
8457 this.maskEl.right.setTop(box.y - this.padding);
8458 this.maskEl.right.show();
8460 this.toolTip.bindEl = this.target.el;
8462 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8464 var tip = this.target.blankText;
8466 if(this.target.getValue() !== '' ) {
8468 if (this.target.invalidText.length) {
8469 tip = this.target.invalidText;
8470 } else if (this.target.regexText.length){
8471 tip = this.target.regexText;
8475 this.toolTip.show(tip);
8477 this.intervalID = window.setInterval(function() {
8478 Roo.bootstrap.Form.popover.unmask();
8481 window.onwheel = function(){ return false;};
8483 (function(){ this.isMasked = true; }).defer(500, this);
8489 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8493 this.maskEl.top.setStyle('position', 'absolute');
8494 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8495 this.maskEl.top.hide();
8497 this.maskEl.left.setStyle('position', 'absolute');
8498 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8499 this.maskEl.left.hide();
8501 this.maskEl.bottom.setStyle('position', 'absolute');
8502 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8503 this.maskEl.bottom.hide();
8505 this.maskEl.right.setStyle('position', 'absolute');
8506 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8507 this.maskEl.right.hide();
8509 this.toolTip.hide();
8511 this.toolTip.el.hide();
8513 window.onwheel = function(){ return true;};
8515 if(this.intervalID){
8516 window.clearInterval(this.intervalID);
8517 this.intervalID = false;
8520 this.isMasked = false;
8530 * Ext JS Library 1.1.1
8531 * Copyright(c) 2006-2007, Ext JS, LLC.
8533 * Originally Released Under LGPL - original licence link has changed is not relivant.
8536 * <script type="text/javascript">
8539 * @class Roo.form.VTypes
8540 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8543 Roo.form.VTypes = function(){
8544 // closure these in so they are only created once.
8545 var alpha = /^[a-zA-Z_]+$/;
8546 var alphanum = /^[a-zA-Z0-9_]+$/;
8547 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8548 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8550 // All these messages and functions are configurable
8553 * The function used to validate email addresses
8554 * @param {String} value The email address
8556 'email' : function(v){
8557 return email.test(v);
8560 * The error text to display when the email validation function returns false
8563 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8565 * The keystroke filter mask to be applied on email input
8568 'emailMask' : /[a-z0-9_\.\-@]/i,
8571 * The function used to validate URLs
8572 * @param {String} value The URL
8574 'url' : function(v){
8578 * The error text to display when the url validation function returns false
8581 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8584 * The function used to validate alpha values
8585 * @param {String} value The value
8587 'alpha' : function(v){
8588 return alpha.test(v);
8591 * The error text to display when the alpha validation function returns false
8594 'alphaText' : 'This field should only contain letters and _',
8596 * The keystroke filter mask to be applied on alpha input
8599 'alphaMask' : /[a-z_]/i,
8602 * The function used to validate alphanumeric values
8603 * @param {String} value The value
8605 'alphanum' : function(v){
8606 return alphanum.test(v);
8609 * The error text to display when the alphanumeric validation function returns false
8612 'alphanumText' : 'This field should only contain letters, numbers and _',
8614 * The keystroke filter mask to be applied on alphanumeric input
8617 'alphanumMask' : /[a-z0-9_]/i
8627 * @class Roo.bootstrap.Input
8628 * @extends Roo.bootstrap.Component
8629 * Bootstrap Input class
8630 * @cfg {Boolean} disabled is it disabled
8631 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8632 * @cfg {String} name name of the input
8633 * @cfg {string} fieldLabel - the label associated
8634 * @cfg {string} placeholder - placeholder to put in text.
8635 * @cfg {string} before - input group add on before
8636 * @cfg {string} after - input group add on after
8637 * @cfg {string} size - (lg|sm) or leave empty..
8638 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8639 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8640 * @cfg {Number} md colspan out of 12 for computer-sized screens
8641 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8642 * @cfg {string} value default value of the input
8643 * @cfg {Number} labelWidth set the width of label
8644 * @cfg {Number} labellg set the width of label (1-12)
8645 * @cfg {Number} labelmd set the width of label (1-12)
8646 * @cfg {Number} labelsm set the width of label (1-12)
8647 * @cfg {Number} labelxs set the width of label (1-12)
8648 * @cfg {String} labelAlign (top|left)
8649 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8650 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8651 * @cfg {String} indicatorpos (left|right) default left
8652 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8653 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8655 * @cfg {String} align (left|center|right) Default left
8656 * @cfg {Boolean} forceFeedback (true|false) Default false
8659 * Create a new Input
8660 * @param {Object} config The config object
8663 Roo.bootstrap.Input = function(config){
8665 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8670 * Fires when this field receives input focus.
8671 * @param {Roo.form.Field} this
8676 * Fires when this field loses input focus.
8677 * @param {Roo.form.Field} this
8682 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8683 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8684 * @param {Roo.form.Field} this
8685 * @param {Roo.EventObject} e The event object
8690 * Fires just before the field blurs if the field value has changed.
8691 * @param {Roo.form.Field} this
8692 * @param {Mixed} newValue The new value
8693 * @param {Mixed} oldValue The original value
8698 * Fires after the field has been marked as invalid.
8699 * @param {Roo.form.Field} this
8700 * @param {String} msg The validation message
8705 * Fires after the field has been validated with no errors.
8706 * @param {Roo.form.Field} this
8711 * Fires after the key up
8712 * @param {Roo.form.Field} this
8713 * @param {Roo.EventObject} e The event Object
8719 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8721 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8722 automatic validation (defaults to "keyup").
8724 validationEvent : "keyup",
8726 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8728 validateOnBlur : true,
8730 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8732 validationDelay : 250,
8734 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8736 focusClass : "x-form-focus", // not needed???
8740 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8742 invalidClass : "has-warning",
8745 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8747 validClass : "has-success",
8750 * @cfg {Boolean} hasFeedback (true|false) default true
8755 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8757 invalidFeedbackClass : "glyphicon-warning-sign",
8760 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8762 validFeedbackClass : "glyphicon-ok",
8765 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8767 selectOnFocus : false,
8770 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8774 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8779 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8781 disableKeyFilter : false,
8784 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8788 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8792 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8794 blankText : "Please complete this mandatory field",
8797 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8801 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8803 maxLength : Number.MAX_VALUE,
8805 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8807 minLengthText : "The minimum length for this field is {0}",
8809 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8811 maxLengthText : "The maximum length for this field is {0}",
8815 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8816 * If available, this function will be called only after the basic validators all return true, and will be passed the
8817 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8821 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8822 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8823 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8827 * @cfg {String} regexText -- Depricated - use Invalid Text
8832 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8838 autocomplete: false,
8857 formatedValue : false,
8858 forceFeedback : false,
8860 indicatorpos : 'left',
8870 parentLabelAlign : function()
8873 while (parent.parent()) {
8874 parent = parent.parent();
8875 if (typeof(parent.labelAlign) !='undefined') {
8876 return parent.labelAlign;
8883 getAutoCreate : function()
8885 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8891 if(this.inputType != 'hidden'){
8892 cfg.cls = 'form-group' //input-group
8898 type : this.inputType,
8900 cls : 'form-control',
8901 placeholder : this.placeholder || '',
8902 autocomplete : this.autocomplete || 'new-password'
8905 if(this.capture.length){
8906 input.capture = this.capture;
8909 if(this.accept.length){
8910 input.accept = this.accept + "/*";
8914 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8917 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8918 input.maxLength = this.maxLength;
8921 if (this.disabled) {
8922 input.disabled=true;
8925 if (this.readOnly) {
8926 input.readonly=true;
8930 input.name = this.name;
8934 input.cls += ' input-' + this.size;
8938 ['xs','sm','md','lg'].map(function(size){
8939 if (settings[size]) {
8940 cfg.cls += ' col-' + size + '-' + settings[size];
8944 var inputblock = input;
8948 cls: 'glyphicon form-control-feedback'
8951 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8954 cls : 'has-feedback',
8962 if (this.before || this.after) {
8965 cls : 'input-group',
8969 if (this.before && typeof(this.before) == 'string') {
8971 inputblock.cn.push({
8973 cls : 'roo-input-before input-group-addon',
8977 if (this.before && typeof(this.before) == 'object') {
8978 this.before = Roo.factory(this.before);
8980 inputblock.cn.push({
8982 cls : 'roo-input-before input-group-' +
8983 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8987 inputblock.cn.push(input);
8989 if (this.after && typeof(this.after) == 'string') {
8990 inputblock.cn.push({
8992 cls : 'roo-input-after input-group-addon',
8996 if (this.after && typeof(this.after) == 'object') {
8997 this.after = Roo.factory(this.after);
8999 inputblock.cn.push({
9001 cls : 'roo-input-after input-group-' +
9002 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9006 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9007 inputblock.cls += ' has-feedback';
9008 inputblock.cn.push(feedback);
9012 if (align ==='left' && this.fieldLabel.length) {
9014 cfg.cls += ' roo-form-group-label-left';
9019 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9020 tooltip : 'This field is required'
9025 cls : 'control-label',
9026 html : this.fieldLabel
9037 var labelCfg = cfg.cn[1];
9038 var contentCfg = cfg.cn[2];
9040 if(this.indicatorpos == 'right'){
9045 cls : 'control-label',
9049 html : this.fieldLabel
9053 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9054 tooltip : 'This field is required'
9067 labelCfg = cfg.cn[0];
9068 contentCfg = cfg.cn[1];
9072 if(this.labelWidth > 12){
9073 labelCfg.style = "width: " + this.labelWidth + 'px';
9076 if(this.labelWidth < 13 && this.labelmd == 0){
9077 this.labelmd = this.labelWidth;
9080 if(this.labellg > 0){
9081 labelCfg.cls += ' col-lg-' + this.labellg;
9082 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9085 if(this.labelmd > 0){
9086 labelCfg.cls += ' col-md-' + this.labelmd;
9087 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9090 if(this.labelsm > 0){
9091 labelCfg.cls += ' col-sm-' + this.labelsm;
9092 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9095 if(this.labelxs > 0){
9096 labelCfg.cls += ' col-xs-' + this.labelxs;
9097 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9101 } else if ( this.fieldLabel.length) {
9106 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9107 tooltip : 'This field is required'
9111 //cls : 'input-group-addon',
9112 html : this.fieldLabel
9120 if(this.indicatorpos == 'right'){
9125 //cls : 'input-group-addon',
9126 html : this.fieldLabel
9131 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9132 tooltip : 'This field is required'
9152 if (this.parentType === 'Navbar' && this.parent().bar) {
9153 cfg.cls += ' navbar-form';
9156 if (this.parentType === 'NavGroup') {
9157 cfg.cls += ' navbar-form';
9165 * return the real input element.
9167 inputEl: function ()
9169 return this.el.select('input.form-control',true).first();
9172 tooltipEl : function()
9174 return this.inputEl();
9177 indicatorEl : function()
9179 var indicator = this.el.select('i.roo-required-indicator',true).first();
9189 setDisabled : function(v)
9191 var i = this.inputEl().dom;
9193 i.removeAttribute('disabled');
9197 i.setAttribute('disabled','true');
9199 initEvents : function()
9202 this.inputEl().on("keydown" , this.fireKey, this);
9203 this.inputEl().on("focus", this.onFocus, this);
9204 this.inputEl().on("blur", this.onBlur, this);
9206 this.inputEl().relayEvent('keyup', this);
9208 this.indicator = this.indicatorEl();
9211 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9214 // reference to original value for reset
9215 this.originalValue = this.getValue();
9216 //Roo.form.TextField.superclass.initEvents.call(this);
9217 if(this.validationEvent == 'keyup'){
9218 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9219 this.inputEl().on('keyup', this.filterValidation, this);
9221 else if(this.validationEvent !== false){
9222 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9225 if(this.selectOnFocus){
9226 this.on("focus", this.preFocus, this);
9229 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9230 this.inputEl().on("keypress", this.filterKeys, this);
9232 this.inputEl().relayEvent('keypress', this);
9235 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9236 this.el.on("click", this.autoSize, this);
9239 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9240 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9243 if (typeof(this.before) == 'object') {
9244 this.before.render(this.el.select('.roo-input-before',true).first());
9246 if (typeof(this.after) == 'object') {
9247 this.after.render(this.el.select('.roo-input-after',true).first());
9250 this.inputEl().on('change', this.onChange, this);
9253 filterValidation : function(e){
9254 if(!e.isNavKeyPress()){
9255 this.validationTask.delay(this.validationDelay);
9259 * Validates the field value
9260 * @return {Boolean} True if the value is valid, else false
9262 validate : function(){
9263 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9264 if(this.disabled || this.validateValue(this.getRawValue())){
9275 * Validates a value according to the field's validation rules and marks the field as invalid
9276 * if the validation fails
9277 * @param {Mixed} value The value to validate
9278 * @return {Boolean} True if the value is valid, else false
9280 validateValue : function(value)
9282 if(this.getVisibilityEl().hasClass('hidden')){
9286 if(value.length < 1) { // if it's blank
9287 if(this.allowBlank){
9293 if(value.length < this.minLength){
9296 if(value.length > this.maxLength){
9300 var vt = Roo.form.VTypes;
9301 if(!vt[this.vtype](value, this)){
9305 if(typeof this.validator == "function"){
9306 var msg = this.validator(value);
9310 if (typeof(msg) == 'string') {
9311 this.invalidText = msg;
9315 if(this.regex && !this.regex.test(value)){
9323 fireKey : function(e){
9324 //Roo.log('field ' + e.getKey());
9325 if(e.isNavKeyPress()){
9326 this.fireEvent("specialkey", this, e);
9329 focus : function (selectText){
9331 this.inputEl().focus();
9332 if(selectText === true){
9333 this.inputEl().dom.select();
9339 onFocus : function(){
9340 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9341 // this.el.addClass(this.focusClass);
9344 this.hasFocus = true;
9345 this.startValue = this.getValue();
9346 this.fireEvent("focus", this);
9350 beforeBlur : Roo.emptyFn,
9354 onBlur : function(){
9356 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9357 //this.el.removeClass(this.focusClass);
9359 this.hasFocus = false;
9360 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9363 var v = this.getValue();
9364 if(String(v) !== String(this.startValue)){
9365 this.fireEvent('change', this, v, this.startValue);
9367 this.fireEvent("blur", this);
9370 onChange : function(e)
9372 var v = this.getValue();
9373 if(String(v) !== String(this.startValue)){
9374 this.fireEvent('change', this, v, this.startValue);
9380 * Resets the current field value to the originally loaded value and clears any validation messages
9383 this.setValue(this.originalValue);
9387 * Returns the name of the field
9388 * @return {Mixed} name The name field
9390 getName: function(){
9394 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9395 * @return {Mixed} value The field value
9397 getValue : function(){
9399 var v = this.inputEl().getValue();
9404 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9405 * @return {Mixed} value The field value
9407 getRawValue : function(){
9408 var v = this.inputEl().getValue();
9414 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9415 * @param {Mixed} value The value to set
9417 setRawValue : function(v){
9418 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9421 selectText : function(start, end){
9422 var v = this.getRawValue();
9424 start = start === undefined ? 0 : start;
9425 end = end === undefined ? v.length : end;
9426 var d = this.inputEl().dom;
9427 if(d.setSelectionRange){
9428 d.setSelectionRange(start, end);
9429 }else if(d.createTextRange){
9430 var range = d.createTextRange();
9431 range.moveStart("character", start);
9432 range.moveEnd("character", v.length-end);
9439 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9440 * @param {Mixed} value The value to set
9442 setValue : function(v){
9445 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9451 processValue : function(value){
9452 if(this.stripCharsRe){
9453 var newValue = value.replace(this.stripCharsRe, '');
9454 if(newValue !== value){
9455 this.setRawValue(newValue);
9462 preFocus : function(){
9464 if(this.selectOnFocus){
9465 this.inputEl().dom.select();
9468 filterKeys : function(e){
9470 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9473 var c = e.getCharCode(), cc = String.fromCharCode(c);
9474 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9477 if(!this.maskRe.test(cc)){
9482 * Clear any invalid styles/messages for this field
9484 clearInvalid : function(){
9486 if(!this.el || this.preventMark){ // not rendered
9491 this.el.removeClass(this.invalidClass);
9493 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9495 var feedback = this.el.select('.form-control-feedback', true).first();
9498 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9504 this.indicator.removeClass('visible');
9505 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9508 this.fireEvent('valid', this);
9512 * Mark this field as valid
9514 markValid : function()
9516 if(!this.el || this.preventMark){ // not rendered...
9520 this.el.removeClass([this.invalidClass, this.validClass]);
9522 var feedback = this.el.select('.form-control-feedback', true).first();
9525 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9529 this.indicator.removeClass('visible');
9530 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9537 if(this.allowBlank && !this.getRawValue().length){
9541 this.el.addClass(this.validClass);
9543 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9545 var feedback = this.el.select('.form-control-feedback', true).first();
9548 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9549 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9554 this.fireEvent('valid', this);
9558 * Mark this field as invalid
9559 * @param {String} msg The validation message
9561 markInvalid : function(msg)
9563 if(!this.el || this.preventMark){ // not rendered
9567 this.el.removeClass([this.invalidClass, this.validClass]);
9569 var feedback = this.el.select('.form-control-feedback', true).first();
9572 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9579 if(this.allowBlank && !this.getRawValue().length){
9584 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9585 this.indicator.addClass('visible');
9588 this.el.addClass(this.invalidClass);
9590 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9592 var feedback = this.el.select('.form-control-feedback', true).first();
9595 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9597 if(this.getValue().length || this.forceFeedback){
9598 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9605 this.fireEvent('invalid', this, msg);
9608 SafariOnKeyDown : function(event)
9610 // this is a workaround for a password hang bug on chrome/ webkit.
9611 if (this.inputEl().dom.type != 'password') {
9615 var isSelectAll = false;
9617 if(this.inputEl().dom.selectionEnd > 0){
9618 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9620 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9621 event.preventDefault();
9626 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9628 event.preventDefault();
9629 // this is very hacky as keydown always get's upper case.
9631 var cc = String.fromCharCode(event.getCharCode());
9632 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9636 adjustWidth : function(tag, w){
9637 tag = tag.toLowerCase();
9638 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9639 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9643 if(tag == 'textarea'){
9646 }else if(Roo.isOpera){
9650 if(tag == 'textarea'){
9658 setFieldLabel : function(v)
9665 var ar = this.el.select('label > span',true);
9667 if (ar.elements.length) {
9668 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9669 this.fieldLabel = v;
9673 var br = this.el.select('label',true);
9675 if(br.elements.length) {
9676 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9677 this.fieldLabel = v;
9681 Roo.log('Cannot Found any of label > span || label in input');
9685 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9686 this.fieldLabel = v;
9701 * @class Roo.bootstrap.TextArea
9702 * @extends Roo.bootstrap.Input
9703 * Bootstrap TextArea class
9704 * @cfg {Number} cols Specifies the visible width of a text area
9705 * @cfg {Number} rows Specifies the visible number of lines in a text area
9706 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9707 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9708 * @cfg {string} html text
9711 * Create a new TextArea
9712 * @param {Object} config The config object
9715 Roo.bootstrap.TextArea = function(config){
9716 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9720 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9730 getAutoCreate : function(){
9732 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9738 if(this.inputType != 'hidden'){
9739 cfg.cls = 'form-group' //input-group
9747 value : this.value || '',
9748 html: this.html || '',
9749 cls : 'form-control',
9750 placeholder : this.placeholder || ''
9754 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9755 input.maxLength = this.maxLength;
9759 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9763 input.cols = this.cols;
9766 if (this.readOnly) {
9767 input.readonly = true;
9771 input.name = this.name;
9775 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9779 ['xs','sm','md','lg'].map(function(size){
9780 if (settings[size]) {
9781 cfg.cls += ' col-' + size + '-' + settings[size];
9785 var inputblock = input;
9787 if(this.hasFeedback && !this.allowBlank){
9791 cls: 'glyphicon form-control-feedback'
9795 cls : 'has-feedback',
9804 if (this.before || this.after) {
9807 cls : 'input-group',
9811 inputblock.cn.push({
9813 cls : 'input-group-addon',
9818 inputblock.cn.push(input);
9820 if(this.hasFeedback && !this.allowBlank){
9821 inputblock.cls += ' has-feedback';
9822 inputblock.cn.push(feedback);
9826 inputblock.cn.push({
9828 cls : 'input-group-addon',
9835 if (align ==='left' && this.fieldLabel.length) {
9840 cls : 'control-label',
9841 html : this.fieldLabel
9852 if(this.labelWidth > 12){
9853 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9856 if(this.labelWidth < 13 && this.labelmd == 0){
9857 this.labelmd = this.labelWidth;
9860 if(this.labellg > 0){
9861 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9862 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9865 if(this.labelmd > 0){
9866 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9867 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9870 if(this.labelsm > 0){
9871 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9872 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9875 if(this.labelxs > 0){
9876 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9877 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9880 } else if ( this.fieldLabel.length) {
9885 //cls : 'input-group-addon',
9886 html : this.fieldLabel
9904 if (this.disabled) {
9905 input.disabled=true;
9912 * return the real textarea element.
9914 inputEl: function ()
9916 return this.el.select('textarea.form-control',true).first();
9920 * Clear any invalid styles/messages for this field
9922 clearInvalid : function()
9925 if(!this.el || this.preventMark){ // not rendered
9929 var label = this.el.select('label', true).first();
9930 var icon = this.el.select('i.fa-star', true).first();
9936 this.el.removeClass(this.invalidClass);
9938 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9940 var feedback = this.el.select('.form-control-feedback', true).first();
9943 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9948 this.fireEvent('valid', this);
9952 * Mark this field as valid
9954 markValid : function()
9956 if(!this.el || this.preventMark){ // not rendered
9960 this.el.removeClass([this.invalidClass, this.validClass]);
9962 var feedback = this.el.select('.form-control-feedback', true).first();
9965 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9968 if(this.disabled || this.allowBlank){
9972 var label = this.el.select('label', true).first();
9973 var icon = this.el.select('i.fa-star', true).first();
9979 this.el.addClass(this.validClass);
9981 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9983 var feedback = this.el.select('.form-control-feedback', true).first();
9986 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9987 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9992 this.fireEvent('valid', this);
9996 * Mark this field as invalid
9997 * @param {String} msg The validation message
9999 markInvalid : function(msg)
10001 if(!this.el || this.preventMark){ // not rendered
10005 this.el.removeClass([this.invalidClass, this.validClass]);
10007 var feedback = this.el.select('.form-control-feedback', true).first();
10010 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10013 if(this.disabled || this.allowBlank){
10017 var label = this.el.select('label', true).first();
10018 var icon = this.el.select('i.fa-star', true).first();
10020 if(!this.getValue().length && label && !icon){
10021 this.el.createChild({
10023 cls : 'text-danger fa fa-lg fa-star',
10024 tooltip : 'This field is required',
10025 style : 'margin-right:5px;'
10029 this.el.addClass(this.invalidClass);
10031 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10033 var feedback = this.el.select('.form-control-feedback', true).first();
10036 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10038 if(this.getValue().length || this.forceFeedback){
10039 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10046 this.fireEvent('invalid', this, msg);
10054 * trigger field - base class for combo..
10059 * @class Roo.bootstrap.TriggerField
10060 * @extends Roo.bootstrap.Input
10061 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10062 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10063 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10064 * for which you can provide a custom implementation. For example:
10066 var trigger = new Roo.bootstrap.TriggerField();
10067 trigger.onTriggerClick = myTriggerFn;
10068 trigger.applyTo('my-field');
10071 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10072 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10073 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10074 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10075 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10078 * Create a new TriggerField.
10079 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10080 * to the base TextField)
10082 Roo.bootstrap.TriggerField = function(config){
10083 this.mimicing = false;
10084 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10087 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10089 * @cfg {String} triggerClass A CSS class to apply to the trigger
10092 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10097 * @cfg {Boolean} removable (true|false) special filter default false
10101 /** @cfg {Boolean} grow @hide */
10102 /** @cfg {Number} growMin @hide */
10103 /** @cfg {Number} growMax @hide */
10109 autoSize: Roo.emptyFn,
10113 deferHeight : true,
10116 actionMode : 'wrap',
10121 getAutoCreate : function(){
10123 var align = this.labelAlign || this.parentLabelAlign();
10128 cls: 'form-group' //input-group
10135 type : this.inputType,
10136 cls : 'form-control',
10137 autocomplete: 'new-password',
10138 placeholder : this.placeholder || ''
10142 input.name = this.name;
10145 input.cls += ' input-' + this.size;
10148 if (this.disabled) {
10149 input.disabled=true;
10152 var inputblock = input;
10154 if(this.hasFeedback && !this.allowBlank){
10158 cls: 'glyphicon form-control-feedback'
10161 if(this.removable && !this.editable && !this.tickable){
10163 cls : 'has-feedback',
10169 cls : 'roo-combo-removable-btn close'
10176 cls : 'has-feedback',
10185 if(this.removable && !this.editable && !this.tickable){
10187 cls : 'roo-removable',
10193 cls : 'roo-combo-removable-btn close'
10200 if (this.before || this.after) {
10203 cls : 'input-group',
10207 inputblock.cn.push({
10209 cls : 'input-group-addon',
10214 inputblock.cn.push(input);
10216 if(this.hasFeedback && !this.allowBlank){
10217 inputblock.cls += ' has-feedback';
10218 inputblock.cn.push(feedback);
10222 inputblock.cn.push({
10224 cls : 'input-group-addon',
10237 cls: 'form-hidden-field'
10251 cls: 'form-hidden-field'
10255 cls: 'roo-select2-choices',
10259 cls: 'roo-select2-search-field',
10272 cls: 'roo-select2-container input-group',
10277 // cls: 'typeahead typeahead-long dropdown-menu',
10278 // style: 'display:none'
10283 if(!this.multiple && this.showToggleBtn){
10289 if (this.caret != false) {
10292 cls: 'fa fa-' + this.caret
10299 cls : 'input-group-addon btn dropdown-toggle',
10304 cls: 'combobox-clear',
10318 combobox.cls += ' roo-select2-container-multi';
10321 if (align ==='left' && this.fieldLabel.length) {
10323 cfg.cls += ' roo-form-group-label-left';
10328 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10329 tooltip : 'This field is required'
10334 cls : 'control-label',
10335 html : this.fieldLabel
10347 var labelCfg = cfg.cn[1];
10348 var contentCfg = cfg.cn[2];
10350 if(this.indicatorpos == 'right'){
10355 cls : 'control-label',
10359 html : this.fieldLabel
10363 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10364 tooltip : 'This field is required'
10377 labelCfg = cfg.cn[0];
10378 contentCfg = cfg.cn[1];
10381 if(this.labelWidth > 12){
10382 labelCfg.style = "width: " + this.labelWidth + 'px';
10385 if(this.labelWidth < 13 && this.labelmd == 0){
10386 this.labelmd = this.labelWidth;
10389 if(this.labellg > 0){
10390 labelCfg.cls += ' col-lg-' + this.labellg;
10391 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10394 if(this.labelmd > 0){
10395 labelCfg.cls += ' col-md-' + this.labelmd;
10396 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10399 if(this.labelsm > 0){
10400 labelCfg.cls += ' col-sm-' + this.labelsm;
10401 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10404 if(this.labelxs > 0){
10405 labelCfg.cls += ' col-xs-' + this.labelxs;
10406 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10409 } else if ( this.fieldLabel.length) {
10410 // Roo.log(" label");
10414 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10415 tooltip : 'This field is required'
10419 //cls : 'input-group-addon',
10420 html : this.fieldLabel
10428 if(this.indicatorpos == 'right'){
10436 html : this.fieldLabel
10440 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10441 tooltip : 'This field is required'
10454 // Roo.log(" no label && no align");
10461 ['xs','sm','md','lg'].map(function(size){
10462 if (settings[size]) {
10463 cfg.cls += ' col-' + size + '-' + settings[size];
10474 onResize : function(w, h){
10475 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10476 // if(typeof w == 'number'){
10477 // var x = w - this.trigger.getWidth();
10478 // this.inputEl().setWidth(this.adjustWidth('input', x));
10479 // this.trigger.setStyle('left', x+'px');
10484 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10487 getResizeEl : function(){
10488 return this.inputEl();
10492 getPositionEl : function(){
10493 return this.inputEl();
10497 alignErrorIcon : function(){
10498 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10502 initEvents : function(){
10506 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10507 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10508 if(!this.multiple && this.showToggleBtn){
10509 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10510 if(this.hideTrigger){
10511 this.trigger.setDisplayed(false);
10513 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10517 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10520 if(this.removable && !this.editable && !this.tickable){
10521 var close = this.closeTriggerEl();
10524 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10525 close.on('click', this.removeBtnClick, this, close);
10529 //this.trigger.addClassOnOver('x-form-trigger-over');
10530 //this.trigger.addClassOnClick('x-form-trigger-click');
10533 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10537 closeTriggerEl : function()
10539 var close = this.el.select('.roo-combo-removable-btn', true).first();
10540 return close ? close : false;
10543 removeBtnClick : function(e, h, el)
10545 e.preventDefault();
10547 if(this.fireEvent("remove", this) !== false){
10549 this.fireEvent("afterremove", this)
10553 createList : function()
10555 this.list = Roo.get(document.body).createChild({
10557 cls: 'typeahead typeahead-long dropdown-menu',
10558 style: 'display:none'
10561 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10566 initTrigger : function(){
10571 onDestroy : function(){
10573 this.trigger.removeAllListeners();
10574 // this.trigger.remove();
10577 // this.wrap.remove();
10579 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10583 onFocus : function(){
10584 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10586 if(!this.mimicing){
10587 this.wrap.addClass('x-trigger-wrap-focus');
10588 this.mimicing = true;
10589 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10590 if(this.monitorTab){
10591 this.el.on("keydown", this.checkTab, this);
10598 checkTab : function(e){
10599 if(e.getKey() == e.TAB){
10600 this.triggerBlur();
10605 onBlur : function(){
10610 mimicBlur : function(e, t){
10612 if(!this.wrap.contains(t) && this.validateBlur()){
10613 this.triggerBlur();
10619 triggerBlur : function(){
10620 this.mimicing = false;
10621 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10622 if(this.monitorTab){
10623 this.el.un("keydown", this.checkTab, this);
10625 //this.wrap.removeClass('x-trigger-wrap-focus');
10626 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10630 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10631 validateBlur : function(e, t){
10636 onDisable : function(){
10637 this.inputEl().dom.disabled = true;
10638 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10640 // this.wrap.addClass('x-item-disabled');
10645 onEnable : function(){
10646 this.inputEl().dom.disabled = false;
10647 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10649 // this.el.removeClass('x-item-disabled');
10654 onShow : function(){
10655 var ae = this.getActionEl();
10658 ae.dom.style.display = '';
10659 ae.dom.style.visibility = 'visible';
10665 onHide : function(){
10666 var ae = this.getActionEl();
10667 ae.dom.style.display = 'none';
10671 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10672 * by an implementing function.
10674 * @param {EventObject} e
10676 onTriggerClick : Roo.emptyFn
10680 * Ext JS Library 1.1.1
10681 * Copyright(c) 2006-2007, Ext JS, LLC.
10683 * Originally Released Under LGPL - original licence link has changed is not relivant.
10686 * <script type="text/javascript">
10691 * @class Roo.data.SortTypes
10693 * Defines the default sorting (casting?) comparison functions used when sorting data.
10695 Roo.data.SortTypes = {
10697 * Default sort that does nothing
10698 * @param {Mixed} s The value being converted
10699 * @return {Mixed} The comparison value
10701 none : function(s){
10706 * The regular expression used to strip tags
10710 stripTagsRE : /<\/?[^>]+>/gi,
10713 * Strips all HTML tags to sort on text only
10714 * @param {Mixed} s The value being converted
10715 * @return {String} The comparison value
10717 asText : function(s){
10718 return String(s).replace(this.stripTagsRE, "");
10722 * Strips all HTML tags to sort on text only - Case insensitive
10723 * @param {Mixed} s The value being converted
10724 * @return {String} The comparison value
10726 asUCText : function(s){
10727 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10731 * Case insensitive string
10732 * @param {Mixed} s The value being converted
10733 * @return {String} The comparison value
10735 asUCString : function(s) {
10736 return String(s).toUpperCase();
10741 * @param {Mixed} s The value being converted
10742 * @return {Number} The comparison value
10744 asDate : function(s) {
10748 if(s instanceof Date){
10749 return s.getTime();
10751 return Date.parse(String(s));
10756 * @param {Mixed} s The value being converted
10757 * @return {Float} The comparison value
10759 asFloat : function(s) {
10760 var val = parseFloat(String(s).replace(/,/g, ""));
10769 * @param {Mixed} s The value being converted
10770 * @return {Number} The comparison value
10772 asInt : function(s) {
10773 var val = parseInt(String(s).replace(/,/g, ""));
10781 * Ext JS Library 1.1.1
10782 * Copyright(c) 2006-2007, Ext JS, LLC.
10784 * Originally Released Under LGPL - original licence link has changed is not relivant.
10787 * <script type="text/javascript">
10791 * @class Roo.data.Record
10792 * Instances of this class encapsulate both record <em>definition</em> information, and record
10793 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10794 * to access Records cached in an {@link Roo.data.Store} object.<br>
10796 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10797 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10800 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10802 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10803 * {@link #create}. The parameters are the same.
10804 * @param {Array} data An associative Array of data values keyed by the field name.
10805 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10806 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10807 * not specified an integer id is generated.
10809 Roo.data.Record = function(data, id){
10810 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10815 * Generate a constructor for a specific record layout.
10816 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10817 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10818 * Each field definition object may contain the following properties: <ul>
10819 * <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,
10820 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10821 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10822 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10823 * is being used, then this is a string containing the javascript expression to reference the data relative to
10824 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10825 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10826 * this may be omitted.</p></li>
10827 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10828 * <ul><li>auto (Default, implies no conversion)</li>
10833 * <li>date</li></ul></p></li>
10834 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10835 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10836 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10837 * by the Reader into an object that will be stored in the Record. It is passed the
10838 * following parameters:<ul>
10839 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10841 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10843 * <br>usage:<br><pre><code>
10844 var TopicRecord = Roo.data.Record.create(
10845 {name: 'title', mapping: 'topic_title'},
10846 {name: 'author', mapping: 'username'},
10847 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10848 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10849 {name: 'lastPoster', mapping: 'user2'},
10850 {name: 'excerpt', mapping: 'post_text'}
10853 var myNewRecord = new TopicRecord({
10854 title: 'Do my job please',
10857 lastPost: new Date(),
10858 lastPoster: 'Animal',
10859 excerpt: 'No way dude!'
10861 myStore.add(myNewRecord);
10866 Roo.data.Record.create = function(o){
10867 var f = function(){
10868 f.superclass.constructor.apply(this, arguments);
10870 Roo.extend(f, Roo.data.Record);
10871 var p = f.prototype;
10872 p.fields = new Roo.util.MixedCollection(false, function(field){
10875 for(var i = 0, len = o.length; i < len; i++){
10876 p.fields.add(new Roo.data.Field(o[i]));
10878 f.getField = function(name){
10879 return p.fields.get(name);
10884 Roo.data.Record.AUTO_ID = 1000;
10885 Roo.data.Record.EDIT = 'edit';
10886 Roo.data.Record.REJECT = 'reject';
10887 Roo.data.Record.COMMIT = 'commit';
10889 Roo.data.Record.prototype = {
10891 * Readonly flag - true if this record has been modified.
10900 join : function(store){
10901 this.store = store;
10905 * Set the named field to the specified value.
10906 * @param {String} name The name of the field to set.
10907 * @param {Object} value The value to set the field to.
10909 set : function(name, value){
10910 if(this.data[name] == value){
10914 if(!this.modified){
10915 this.modified = {};
10917 if(typeof this.modified[name] == 'undefined'){
10918 this.modified[name] = this.data[name];
10920 this.data[name] = value;
10921 if(!this.editing && this.store){
10922 this.store.afterEdit(this);
10927 * Get the value of the named field.
10928 * @param {String} name The name of the field to get the value of.
10929 * @return {Object} The value of the field.
10931 get : function(name){
10932 return this.data[name];
10936 beginEdit : function(){
10937 this.editing = true;
10938 this.modified = {};
10942 cancelEdit : function(){
10943 this.editing = false;
10944 delete this.modified;
10948 endEdit : function(){
10949 this.editing = false;
10950 if(this.dirty && this.store){
10951 this.store.afterEdit(this);
10956 * Usually called by the {@link Roo.data.Store} which owns the Record.
10957 * Rejects all changes made to the Record since either creation, or the last commit operation.
10958 * Modified fields are reverted to their original values.
10960 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10961 * of reject operations.
10963 reject : function(){
10964 var m = this.modified;
10966 if(typeof m[n] != "function"){
10967 this.data[n] = m[n];
10970 this.dirty = false;
10971 delete this.modified;
10972 this.editing = false;
10974 this.store.afterReject(this);
10979 * Usually called by the {@link Roo.data.Store} which owns the Record.
10980 * Commits all changes made to the Record since either creation, or the last commit operation.
10982 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10983 * of commit operations.
10985 commit : function(){
10986 this.dirty = false;
10987 delete this.modified;
10988 this.editing = false;
10990 this.store.afterCommit(this);
10995 hasError : function(){
10996 return this.error != null;
11000 clearError : function(){
11005 * Creates a copy of this record.
11006 * @param {String} id (optional) A new record id if you don't want to use this record's id
11009 copy : function(newId) {
11010 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11014 * Ext JS Library 1.1.1
11015 * Copyright(c) 2006-2007, Ext JS, LLC.
11017 * Originally Released Under LGPL - original licence link has changed is not relivant.
11020 * <script type="text/javascript">
11026 * @class Roo.data.Store
11027 * @extends Roo.util.Observable
11028 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11029 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11031 * 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
11032 * has no knowledge of the format of the data returned by the Proxy.<br>
11034 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11035 * instances from the data object. These records are cached and made available through accessor functions.
11037 * Creates a new Store.
11038 * @param {Object} config A config object containing the objects needed for the Store to access data,
11039 * and read the data into Records.
11041 Roo.data.Store = function(config){
11042 this.data = new Roo.util.MixedCollection(false);
11043 this.data.getKey = function(o){
11046 this.baseParams = {};
11048 this.paramNames = {
11053 "multisort" : "_multisort"
11056 if(config && config.data){
11057 this.inlineData = config.data;
11058 delete config.data;
11061 Roo.apply(this, config);
11063 if(this.reader){ // reader passed
11064 this.reader = Roo.factory(this.reader, Roo.data);
11065 this.reader.xmodule = this.xmodule || false;
11066 if(!this.recordType){
11067 this.recordType = this.reader.recordType;
11069 if(this.reader.onMetaChange){
11070 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11074 if(this.recordType){
11075 this.fields = this.recordType.prototype.fields;
11077 this.modified = [];
11081 * @event datachanged
11082 * Fires when the data cache has changed, and a widget which is using this Store
11083 * as a Record cache should refresh its view.
11084 * @param {Store} this
11086 datachanged : true,
11088 * @event metachange
11089 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11090 * @param {Store} this
11091 * @param {Object} meta The JSON metadata
11096 * Fires when Records have been added to the Store
11097 * @param {Store} this
11098 * @param {Roo.data.Record[]} records The array of Records added
11099 * @param {Number} index The index at which the record(s) were added
11104 * Fires when a Record has been removed from the Store
11105 * @param {Store} this
11106 * @param {Roo.data.Record} record The Record that was removed
11107 * @param {Number} index The index at which the record was removed
11112 * Fires when a Record has been updated
11113 * @param {Store} this
11114 * @param {Roo.data.Record} record The Record that was updated
11115 * @param {String} operation The update operation being performed. Value may be one of:
11117 Roo.data.Record.EDIT
11118 Roo.data.Record.REJECT
11119 Roo.data.Record.COMMIT
11125 * Fires when the data cache has been cleared.
11126 * @param {Store} this
11130 * @event beforeload
11131 * Fires before a request is made for a new data object. If the beforeload handler returns false
11132 * the load action will be canceled.
11133 * @param {Store} this
11134 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11138 * @event beforeloadadd
11139 * Fires after a new set of Records has been loaded.
11140 * @param {Store} this
11141 * @param {Roo.data.Record[]} records The Records that were loaded
11142 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11144 beforeloadadd : true,
11147 * Fires after a new set of Records has been loaded, before they are added to the store.
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)
11151 * @params {Object} return from reader
11155 * @event loadexception
11156 * Fires if an exception occurs in the Proxy during loading.
11157 * Called with the signature of the Proxy's "loadexception" event.
11158 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11161 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11162 * @param {Object} load options
11163 * @param {Object} jsonData from your request (normally this contains the Exception)
11165 loadexception : true
11169 this.proxy = Roo.factory(this.proxy, Roo.data);
11170 this.proxy.xmodule = this.xmodule || false;
11171 this.relayEvents(this.proxy, ["loadexception"]);
11173 this.sortToggle = {};
11174 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11176 Roo.data.Store.superclass.constructor.call(this);
11178 if(this.inlineData){
11179 this.loadData(this.inlineData);
11180 delete this.inlineData;
11184 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11186 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11187 * without a remote query - used by combo/forms at present.
11191 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11194 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11197 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11198 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11201 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11202 * on any HTTP request
11205 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11208 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11212 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11213 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11215 remoteSort : false,
11218 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11219 * loaded or when a record is removed. (defaults to false).
11221 pruneModifiedRecords : false,
11224 lastOptions : null,
11227 * Add Records to the Store and fires the add event.
11228 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11230 add : function(records){
11231 records = [].concat(records);
11232 for(var i = 0, len = records.length; i < len; i++){
11233 records[i].join(this);
11235 var index = this.data.length;
11236 this.data.addAll(records);
11237 this.fireEvent("add", this, records, index);
11241 * Remove a Record from the Store and fires the remove event.
11242 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11244 remove : function(record){
11245 var index = this.data.indexOf(record);
11246 this.data.removeAt(index);
11248 if(this.pruneModifiedRecords){
11249 this.modified.remove(record);
11251 this.fireEvent("remove", this, record, index);
11255 * Remove all Records from the Store and fires the clear event.
11257 removeAll : function(){
11259 if(this.pruneModifiedRecords){
11260 this.modified = [];
11262 this.fireEvent("clear", this);
11266 * Inserts Records to the Store at the given index and fires the add event.
11267 * @param {Number} index The start index at which to insert the passed Records.
11268 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11270 insert : function(index, records){
11271 records = [].concat(records);
11272 for(var i = 0, len = records.length; i < len; i++){
11273 this.data.insert(index, records[i]);
11274 records[i].join(this);
11276 this.fireEvent("add", this, records, index);
11280 * Get the index within the cache of the passed Record.
11281 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11282 * @return {Number} The index of the passed Record. Returns -1 if not found.
11284 indexOf : function(record){
11285 return this.data.indexOf(record);
11289 * Get the index within the cache of the Record with the passed id.
11290 * @param {String} id The id of the Record to find.
11291 * @return {Number} The index of the Record. Returns -1 if not found.
11293 indexOfId : function(id){
11294 return this.data.indexOfKey(id);
11298 * Get the Record with the specified id.
11299 * @param {String} id The id of the Record to find.
11300 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11302 getById : function(id){
11303 return this.data.key(id);
11307 * Get the Record at the specified index.
11308 * @param {Number} index The index of the Record to find.
11309 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11311 getAt : function(index){
11312 return this.data.itemAt(index);
11316 * Returns a range of Records between specified indices.
11317 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11318 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11319 * @return {Roo.data.Record[]} An array of Records
11321 getRange : function(start, end){
11322 return this.data.getRange(start, end);
11326 storeOptions : function(o){
11327 o = Roo.apply({}, o);
11330 this.lastOptions = o;
11334 * Loads the Record cache from the configured Proxy using the configured Reader.
11336 * If using remote paging, then the first load call must specify the <em>start</em>
11337 * and <em>limit</em> properties in the options.params property to establish the initial
11338 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11340 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11341 * and this call will return before the new data has been loaded. Perform any post-processing
11342 * in a callback function, or in a "load" event handler.</strong>
11344 * @param {Object} options An object containing properties which control loading options:<ul>
11345 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11346 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11347 * passed the following arguments:<ul>
11348 * <li>r : Roo.data.Record[]</li>
11349 * <li>options: Options object from the load call</li>
11350 * <li>success: Boolean success indicator</li></ul></li>
11351 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11352 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11355 load : function(options){
11356 options = options || {};
11357 if(this.fireEvent("beforeload", this, options) !== false){
11358 this.storeOptions(options);
11359 var p = Roo.apply(options.params || {}, this.baseParams);
11360 // if meta was not loaded from remote source.. try requesting it.
11361 if (!this.reader.metaFromRemote) {
11362 p._requestMeta = 1;
11364 if(this.sortInfo && this.remoteSort){
11365 var pn = this.paramNames;
11366 p[pn["sort"]] = this.sortInfo.field;
11367 p[pn["dir"]] = this.sortInfo.direction;
11369 if (this.multiSort) {
11370 var pn = this.paramNames;
11371 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11374 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11379 * Reloads the Record cache from the configured Proxy using the configured Reader and
11380 * the options from the last load operation performed.
11381 * @param {Object} options (optional) An object containing properties which may override the options
11382 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11383 * the most recently used options are reused).
11385 reload : function(options){
11386 this.load(Roo.applyIf(options||{}, this.lastOptions));
11390 // Called as a callback by the Reader during a load operation.
11391 loadRecords : function(o, options, success){
11392 if(!o || success === false){
11393 if(success !== false){
11394 this.fireEvent("load", this, [], options, o);
11396 if(options.callback){
11397 options.callback.call(options.scope || this, [], options, false);
11401 // if data returned failure - throw an exception.
11402 if (o.success === false) {
11403 // show a message if no listener is registered.
11404 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11405 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11407 // loadmask wil be hooked into this..
11408 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11411 var r = o.records, t = o.totalRecords || r.length;
11413 this.fireEvent("beforeloadadd", this, r, options, o);
11415 if(!options || options.add !== true){
11416 if(this.pruneModifiedRecords){
11417 this.modified = [];
11419 for(var i = 0, len = r.length; i < len; i++){
11423 this.data = this.snapshot;
11424 delete this.snapshot;
11427 this.data.addAll(r);
11428 this.totalLength = t;
11430 this.fireEvent("datachanged", this);
11432 this.totalLength = Math.max(t, this.data.length+r.length);
11436 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11438 var e = new Roo.data.Record({});
11440 e.set(this.parent.displayField, this.parent.emptyTitle);
11441 e.set(this.parent.valueField, '');
11446 this.fireEvent("load", this, r, options, o);
11447 if(options.callback){
11448 options.callback.call(options.scope || this, r, options, true);
11454 * Loads data from a passed data block. A Reader which understands the format of the data
11455 * must have been configured in the constructor.
11456 * @param {Object} data The data block from which to read the Records. The format of the data expected
11457 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11458 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11460 loadData : function(o, append){
11461 var r = this.reader.readRecords(o);
11462 this.loadRecords(r, {add: append}, true);
11466 * Gets the number of cached records.
11468 * <em>If using paging, this may not be the total size of the dataset. If the data object
11469 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11470 * the data set size</em>
11472 getCount : function(){
11473 return this.data.length || 0;
11477 * Gets the total number of records in the dataset as returned by the server.
11479 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11480 * the dataset size</em>
11482 getTotalCount : function(){
11483 return this.totalLength || 0;
11487 * Returns the sort state of the Store as an object with two properties:
11489 field {String} The name of the field by which the Records are sorted
11490 direction {String} The sort order, "ASC" or "DESC"
11493 getSortState : function(){
11494 return this.sortInfo;
11498 applySort : function(){
11499 if(this.sortInfo && !this.remoteSort){
11500 var s = this.sortInfo, f = s.field;
11501 var st = this.fields.get(f).sortType;
11502 var fn = function(r1, r2){
11503 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11504 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11506 this.data.sort(s.direction, fn);
11507 if(this.snapshot && this.snapshot != this.data){
11508 this.snapshot.sort(s.direction, fn);
11514 * Sets the default sort column and order to be used by the next load operation.
11515 * @param {String} fieldName The name of the field to sort by.
11516 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11518 setDefaultSort : function(field, dir){
11519 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11523 * Sort the Records.
11524 * If remote sorting is used, the sort is performed on the server, and the cache is
11525 * reloaded. If local sorting is used, the cache is sorted internally.
11526 * @param {String} fieldName The name of the field to sort by.
11527 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11529 sort : function(fieldName, dir){
11530 var f = this.fields.get(fieldName);
11532 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11534 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11535 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11540 this.sortToggle[f.name] = dir;
11541 this.sortInfo = {field: f.name, direction: dir};
11542 if(!this.remoteSort){
11544 this.fireEvent("datachanged", this);
11546 this.load(this.lastOptions);
11551 * Calls the specified function for each of the Records in the cache.
11552 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11553 * Returning <em>false</em> aborts and exits the iteration.
11554 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11556 each : function(fn, scope){
11557 this.data.each(fn, scope);
11561 * Gets all records modified since the last commit. Modified records are persisted across load operations
11562 * (e.g., during paging).
11563 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11565 getModifiedRecords : function(){
11566 return this.modified;
11570 createFilterFn : function(property, value, anyMatch){
11571 if(!value.exec){ // not a regex
11572 value = String(value);
11573 if(value.length == 0){
11576 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11578 return function(r){
11579 return value.test(r.data[property]);
11584 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11585 * @param {String} property A field on your records
11586 * @param {Number} start The record index to start at (defaults to 0)
11587 * @param {Number} end The last record index to include (defaults to length - 1)
11588 * @return {Number} The sum
11590 sum : function(property, start, end){
11591 var rs = this.data.items, v = 0;
11592 start = start || 0;
11593 end = (end || end === 0) ? end : rs.length-1;
11595 for(var i = start; i <= end; i++){
11596 v += (rs[i].data[property] || 0);
11602 * Filter the records by a specified property.
11603 * @param {String} field A field on your records
11604 * @param {String/RegExp} value Either a string that the field
11605 * should start with or a RegExp to test against the field
11606 * @param {Boolean} anyMatch True to match any part not just the beginning
11608 filter : function(property, value, anyMatch){
11609 var fn = this.createFilterFn(property, value, anyMatch);
11610 return fn ? this.filterBy(fn) : this.clearFilter();
11614 * Filter by a function. The specified function will be called with each
11615 * record in this data source. If the function returns true the record is included,
11616 * otherwise it is filtered.
11617 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11618 * @param {Object} scope (optional) The scope of the function (defaults to this)
11620 filterBy : function(fn, scope){
11621 this.snapshot = this.snapshot || this.data;
11622 this.data = this.queryBy(fn, scope||this);
11623 this.fireEvent("datachanged", this);
11627 * Query the records by a specified property.
11628 * @param {String} field A field on your records
11629 * @param {String/RegExp} value Either a string that the field
11630 * should start with or a RegExp to test against the field
11631 * @param {Boolean} anyMatch True to match any part not just the beginning
11632 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11634 query : function(property, value, anyMatch){
11635 var fn = this.createFilterFn(property, value, anyMatch);
11636 return fn ? this.queryBy(fn) : this.data.clone();
11640 * Query by a function. The specified function will be called with each
11641 * record in this data source. If the function returns true the record is included
11643 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11644 * @param {Object} scope (optional) The scope of the function (defaults to this)
11645 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11647 queryBy : function(fn, scope){
11648 var data = this.snapshot || this.data;
11649 return data.filterBy(fn, scope||this);
11653 * Collects unique values for a particular dataIndex from this store.
11654 * @param {String} dataIndex The property to collect
11655 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11656 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11657 * @return {Array} An array of the unique values
11659 collect : function(dataIndex, allowNull, bypassFilter){
11660 var d = (bypassFilter === true && this.snapshot) ?
11661 this.snapshot.items : this.data.items;
11662 var v, sv, r = [], l = {};
11663 for(var i = 0, len = d.length; i < len; i++){
11664 v = d[i].data[dataIndex];
11666 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11675 * Revert to a view of the Record cache with no filtering applied.
11676 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11678 clearFilter : function(suppressEvent){
11679 if(this.snapshot && this.snapshot != this.data){
11680 this.data = this.snapshot;
11681 delete this.snapshot;
11682 if(suppressEvent !== true){
11683 this.fireEvent("datachanged", this);
11689 afterEdit : function(record){
11690 if(this.modified.indexOf(record) == -1){
11691 this.modified.push(record);
11693 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11697 afterReject : function(record){
11698 this.modified.remove(record);
11699 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11703 afterCommit : function(record){
11704 this.modified.remove(record);
11705 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11709 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11710 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11712 commitChanges : function(){
11713 var m = this.modified.slice(0);
11714 this.modified = [];
11715 for(var i = 0, len = m.length; i < len; i++){
11721 * Cancel outstanding changes on all changed records.
11723 rejectChanges : function(){
11724 var m = this.modified.slice(0);
11725 this.modified = [];
11726 for(var i = 0, len = m.length; i < len; i++){
11731 onMetaChange : function(meta, rtype, o){
11732 this.recordType = rtype;
11733 this.fields = rtype.prototype.fields;
11734 delete this.snapshot;
11735 this.sortInfo = meta.sortInfo || this.sortInfo;
11736 this.modified = [];
11737 this.fireEvent('metachange', this, this.reader.meta);
11740 moveIndex : function(data, type)
11742 var index = this.indexOf(data);
11744 var newIndex = index + type;
11748 this.insert(newIndex, data);
11753 * Ext JS Library 1.1.1
11754 * Copyright(c) 2006-2007, Ext JS, LLC.
11756 * Originally Released Under LGPL - original licence link has changed is not relivant.
11759 * <script type="text/javascript">
11763 * @class Roo.data.SimpleStore
11764 * @extends Roo.data.Store
11765 * Small helper class to make creating Stores from Array data easier.
11766 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11767 * @cfg {Array} fields An array of field definition objects, or field name strings.
11768 * @cfg {Array} data The multi-dimensional array of data
11770 * @param {Object} config
11772 Roo.data.SimpleStore = function(config){
11773 Roo.data.SimpleStore.superclass.constructor.call(this, {
11775 reader: new Roo.data.ArrayReader({
11778 Roo.data.Record.create(config.fields)
11780 proxy : new Roo.data.MemoryProxy(config.data)
11784 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11786 * Ext JS Library 1.1.1
11787 * Copyright(c) 2006-2007, Ext JS, LLC.
11789 * Originally Released Under LGPL - original licence link has changed is not relivant.
11792 * <script type="text/javascript">
11797 * @extends Roo.data.Store
11798 * @class Roo.data.JsonStore
11799 * Small helper class to make creating Stores for JSON data easier. <br/>
11801 var store = new Roo.data.JsonStore({
11802 url: 'get-images.php',
11804 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11807 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11808 * JsonReader and HttpProxy (unless inline data is provided).</b>
11809 * @cfg {Array} fields An array of field definition objects, or field name strings.
11811 * @param {Object} config
11813 Roo.data.JsonStore = function(c){
11814 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11815 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11816 reader: new Roo.data.JsonReader(c, c.fields)
11819 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11821 * Ext JS Library 1.1.1
11822 * Copyright(c) 2006-2007, Ext JS, LLC.
11824 * Originally Released Under LGPL - original licence link has changed is not relivant.
11827 * <script type="text/javascript">
11831 Roo.data.Field = function(config){
11832 if(typeof config == "string"){
11833 config = {name: config};
11835 Roo.apply(this, config);
11838 this.type = "auto";
11841 var st = Roo.data.SortTypes;
11842 // named sortTypes are supported, here we look them up
11843 if(typeof this.sortType == "string"){
11844 this.sortType = st[this.sortType];
11847 // set default sortType for strings and dates
11848 if(!this.sortType){
11851 this.sortType = st.asUCString;
11854 this.sortType = st.asDate;
11857 this.sortType = st.none;
11862 var stripRe = /[\$,%]/g;
11864 // prebuilt conversion function for this field, instead of
11865 // switching every time we're reading a value
11867 var cv, dateFormat = this.dateFormat;
11872 cv = function(v){ return v; };
11875 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11879 return v !== undefined && v !== null && v !== '' ?
11880 parseInt(String(v).replace(stripRe, ""), 10) : '';
11885 return v !== undefined && v !== null && v !== '' ?
11886 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11891 cv = function(v){ return v === true || v === "true" || v == 1; };
11898 if(v instanceof Date){
11902 if(dateFormat == "timestamp"){
11903 return new Date(v*1000);
11905 return Date.parseDate(v, dateFormat);
11907 var parsed = Date.parse(v);
11908 return parsed ? new Date(parsed) : null;
11917 Roo.data.Field.prototype = {
11925 * Ext JS Library 1.1.1
11926 * Copyright(c) 2006-2007, Ext JS, LLC.
11928 * Originally Released Under LGPL - original licence link has changed is not relivant.
11931 * <script type="text/javascript">
11934 // Base class for reading structured data from a data source. This class is intended to be
11935 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11938 * @class Roo.data.DataReader
11939 * Base class for reading structured data from a data source. This class is intended to be
11940 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11943 Roo.data.DataReader = function(meta, recordType){
11947 this.recordType = recordType instanceof Array ?
11948 Roo.data.Record.create(recordType) : recordType;
11951 Roo.data.DataReader.prototype = {
11953 * Create an empty record
11954 * @param {Object} data (optional) - overlay some values
11955 * @return {Roo.data.Record} record created.
11957 newRow : function(d) {
11959 this.recordType.prototype.fields.each(function(c) {
11961 case 'int' : da[c.name] = 0; break;
11962 case 'date' : da[c.name] = new Date(); break;
11963 case 'float' : da[c.name] = 0.0; break;
11964 case 'boolean' : da[c.name] = false; break;
11965 default : da[c.name] = ""; break;
11969 return new this.recordType(Roo.apply(da, d));
11974 * Ext JS Library 1.1.1
11975 * Copyright(c) 2006-2007, Ext JS, LLC.
11977 * Originally Released Under LGPL - original licence link has changed is not relivant.
11980 * <script type="text/javascript">
11984 * @class Roo.data.DataProxy
11985 * @extends Roo.data.Observable
11986 * This class is an abstract base class for implementations which provide retrieval of
11987 * unformatted data objects.<br>
11989 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11990 * (of the appropriate type which knows how to parse the data object) to provide a block of
11991 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11993 * Custom implementations must implement the load method as described in
11994 * {@link Roo.data.HttpProxy#load}.
11996 Roo.data.DataProxy = function(){
11999 * @event beforeload
12000 * Fires before a network request is made to retrieve a data object.
12001 * @param {Object} This DataProxy object.
12002 * @param {Object} params The params parameter to the load function.
12007 * Fires before the load method's callback is called.
12008 * @param {Object} This DataProxy object.
12009 * @param {Object} o The data object.
12010 * @param {Object} arg The callback argument object passed to the load function.
12014 * @event loadexception
12015 * Fires if an Exception occurs during data retrieval.
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.
12019 * @param {Object} e The Exception.
12021 loadexception : true
12023 Roo.data.DataProxy.superclass.constructor.call(this);
12026 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12029 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12033 * Ext JS Library 1.1.1
12034 * Copyright(c) 2006-2007, Ext JS, LLC.
12036 * Originally Released Under LGPL - original licence link has changed is not relivant.
12039 * <script type="text/javascript">
12042 * @class Roo.data.MemoryProxy
12043 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12044 * to the Reader when its load method is called.
12046 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12048 Roo.data.MemoryProxy = function(data){
12052 Roo.data.MemoryProxy.superclass.constructor.call(this);
12056 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12059 * Load data from the requested source (in this case an in-memory
12060 * data object passed to the constructor), read the data object into
12061 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12062 * process that block using the passed callback.
12063 * @param {Object} params This parameter is not used by the MemoryProxy class.
12064 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12065 * object into a block of Roo.data.Records.
12066 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12067 * The function must be passed <ul>
12068 * <li>The Record block object</li>
12069 * <li>The "arg" argument from the load function</li>
12070 * <li>A boolean success indicator</li>
12072 * @param {Object} scope The scope in which to call the callback
12073 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12075 load : function(params, reader, callback, scope, arg){
12076 params = params || {};
12079 result = reader.readRecords(this.data);
12081 this.fireEvent("loadexception", this, arg, null, e);
12082 callback.call(scope, null, arg, false);
12085 callback.call(scope, result, arg, true);
12089 update : function(params, records){
12094 * Ext JS Library 1.1.1
12095 * Copyright(c) 2006-2007, Ext JS, LLC.
12097 * Originally Released Under LGPL - original licence link has changed is not relivant.
12100 * <script type="text/javascript">
12103 * @class Roo.data.HttpProxy
12104 * @extends Roo.data.DataProxy
12105 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12106 * configured to reference a certain URL.<br><br>
12108 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12109 * from which the running page was served.<br><br>
12111 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12113 * Be aware that to enable the browser to parse an XML document, the server must set
12114 * the Content-Type header in the HTTP response to "text/xml".
12116 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12117 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12118 * will be used to make the request.
12120 Roo.data.HttpProxy = function(conn){
12121 Roo.data.HttpProxy.superclass.constructor.call(this);
12122 // is conn a conn config or a real conn?
12124 this.useAjax = !conn || !conn.events;
12128 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12129 // thse are take from connection...
12132 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12135 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12136 * extra parameters to each request made by this object. (defaults to undefined)
12139 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12140 * to each request made by this object. (defaults to undefined)
12143 * @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)
12146 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12149 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12155 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12159 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12160 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12161 * a finer-grained basis than the DataProxy events.
12163 getConnection : function(){
12164 return this.useAjax ? Roo.Ajax : this.conn;
12168 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12169 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12170 * process that block using the passed callback.
12171 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12172 * for the request to the remote server.
12173 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12174 * object into a block of Roo.data.Records.
12175 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12176 * The function must be passed <ul>
12177 * <li>The Record block object</li>
12178 * <li>The "arg" argument from the load function</li>
12179 * <li>A boolean success indicator</li>
12181 * @param {Object} scope The scope in which to call the callback
12182 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12184 load : function(params, reader, callback, scope, arg){
12185 if(this.fireEvent("beforeload", this, params) !== false){
12187 params : params || {},
12189 callback : callback,
12194 callback : this.loadResponse,
12198 Roo.applyIf(o, this.conn);
12199 if(this.activeRequest){
12200 Roo.Ajax.abort(this.activeRequest);
12202 this.activeRequest = Roo.Ajax.request(o);
12204 this.conn.request(o);
12207 callback.call(scope||this, null, arg, false);
12212 loadResponse : function(o, success, response){
12213 delete this.activeRequest;
12215 this.fireEvent("loadexception", this, o, response);
12216 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12221 result = o.reader.read(response);
12223 this.fireEvent("loadexception", this, o, response, e);
12224 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12228 this.fireEvent("load", this, o, o.request.arg);
12229 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12233 update : function(dataSet){
12238 updateResponse : function(dataSet){
12243 * Ext JS Library 1.1.1
12244 * Copyright(c) 2006-2007, Ext JS, LLC.
12246 * Originally Released Under LGPL - original licence link has changed is not relivant.
12249 * <script type="text/javascript">
12253 * @class Roo.data.ScriptTagProxy
12254 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12255 * other than the originating domain of the running page.<br><br>
12257 * <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
12258 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12260 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12261 * source code that is used as the source inside a <script> tag.<br><br>
12263 * In order for the browser to process the returned data, the server must wrap the data object
12264 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12265 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12266 * depending on whether the callback name was passed:
12269 boolean scriptTag = false;
12270 String cb = request.getParameter("callback");
12273 response.setContentType("text/javascript");
12275 response.setContentType("application/x-json");
12277 Writer out = response.getWriter();
12279 out.write(cb + "(");
12281 out.print(dataBlock.toJsonString());
12288 * @param {Object} config A configuration object.
12290 Roo.data.ScriptTagProxy = function(config){
12291 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12292 Roo.apply(this, config);
12293 this.head = document.getElementsByTagName("head")[0];
12296 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12298 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12300 * @cfg {String} url The URL from which to request the data object.
12303 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12307 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12308 * the server the name of the callback function set up by the load call to process the returned data object.
12309 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12310 * javascript output which calls this named function passing the data object as its only parameter.
12312 callbackParam : "callback",
12314 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12315 * name to the request.
12320 * Load data from the configured URL, read the data object into
12321 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12322 * process that block using the passed callback.
12323 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12324 * for the request to the remote server.
12325 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12326 * object into a block of Roo.data.Records.
12327 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12328 * The function must be passed <ul>
12329 * <li>The Record block object</li>
12330 * <li>The "arg" argument from the load function</li>
12331 * <li>A boolean success indicator</li>
12333 * @param {Object} scope The scope in which to call the callback
12334 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12336 load : function(params, reader, callback, scope, arg){
12337 if(this.fireEvent("beforeload", this, params) !== false){
12339 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12341 var url = this.url;
12342 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12344 url += "&_dc=" + (new Date().getTime());
12346 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12349 cb : "stcCallback"+transId,
12350 scriptId : "stcScript"+transId,
12354 callback : callback,
12360 window[trans.cb] = function(o){
12361 conn.handleResponse(o, trans);
12364 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12366 if(this.autoAbort !== false){
12370 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12372 var script = document.createElement("script");
12373 script.setAttribute("src", url);
12374 script.setAttribute("type", "text/javascript");
12375 script.setAttribute("id", trans.scriptId);
12376 this.head.appendChild(script);
12378 this.trans = trans;
12380 callback.call(scope||this, null, arg, false);
12385 isLoading : function(){
12386 return this.trans ? true : false;
12390 * Abort the current server request.
12392 abort : function(){
12393 if(this.isLoading()){
12394 this.destroyTrans(this.trans);
12399 destroyTrans : function(trans, isLoaded){
12400 this.head.removeChild(document.getElementById(trans.scriptId));
12401 clearTimeout(trans.timeoutId);
12403 window[trans.cb] = undefined;
12405 delete window[trans.cb];
12408 // if hasn't been loaded, wait for load to remove it to prevent script error
12409 window[trans.cb] = function(){
12410 window[trans.cb] = undefined;
12412 delete window[trans.cb];
12419 handleResponse : function(o, trans){
12420 this.trans = false;
12421 this.destroyTrans(trans, true);
12424 result = trans.reader.readRecords(o);
12426 this.fireEvent("loadexception", this, o, trans.arg, e);
12427 trans.callback.call(trans.scope||window, null, trans.arg, false);
12430 this.fireEvent("load", this, o, trans.arg);
12431 trans.callback.call(trans.scope||window, result, trans.arg, true);
12435 handleFailure : function(trans){
12436 this.trans = false;
12437 this.destroyTrans(trans, false);
12438 this.fireEvent("loadexception", this, null, trans.arg);
12439 trans.callback.call(trans.scope||window, null, trans.arg, false);
12443 * Ext JS Library 1.1.1
12444 * Copyright(c) 2006-2007, Ext JS, LLC.
12446 * Originally Released Under LGPL - original licence link has changed is not relivant.
12449 * <script type="text/javascript">
12453 * @class Roo.data.JsonReader
12454 * @extends Roo.data.DataReader
12455 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12456 * based on mappings in a provided Roo.data.Record constructor.
12458 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12459 * in the reply previously.
12464 var RecordDef = Roo.data.Record.create([
12465 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12466 {name: 'occupation'} // This field will use "occupation" as the mapping.
12468 var myReader = new Roo.data.JsonReader({
12469 totalProperty: "results", // The property which contains the total dataset size (optional)
12470 root: "rows", // The property which contains an Array of row objects
12471 id: "id" // The property within each row object that provides an ID for the record (optional)
12475 * This would consume a JSON file like this:
12477 { 'results': 2, 'rows': [
12478 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12479 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12482 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12483 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12484 * paged from the remote server.
12485 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12486 * @cfg {String} root name of the property which contains the Array of row objects.
12487 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12488 * @cfg {Array} fields Array of field definition objects
12490 * Create a new JsonReader
12491 * @param {Object} meta Metadata configuration options
12492 * @param {Object} recordType Either an Array of field definition objects,
12493 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12495 Roo.data.JsonReader = function(meta, recordType){
12498 // set some defaults:
12499 Roo.applyIf(meta, {
12500 totalProperty: 'total',
12501 successProperty : 'success',
12506 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12508 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12511 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12512 * Used by Store query builder to append _requestMeta to params.
12515 metaFromRemote : false,
12517 * This method is only used by a DataProxy which has retrieved data from a remote server.
12518 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12519 * @return {Object} data A data block which is used by an Roo.data.Store object as
12520 * a cache of Roo.data.Records.
12522 read : function(response){
12523 var json = response.responseText;
12525 var o = /* eval:var:o */ eval("("+json+")");
12527 throw {message: "JsonReader.read: Json object not found"};
12533 this.metaFromRemote = true;
12534 this.meta = o.metaData;
12535 this.recordType = Roo.data.Record.create(o.metaData.fields);
12536 this.onMetaChange(this.meta, this.recordType, o);
12538 return this.readRecords(o);
12541 // private function a store will implement
12542 onMetaChange : function(meta, recordType, o){
12549 simpleAccess: function(obj, subsc) {
12556 getJsonAccessor: function(){
12558 return function(expr) {
12560 return(re.test(expr))
12561 ? new Function("obj", "return obj." + expr)
12566 return Roo.emptyFn;
12571 * Create a data block containing Roo.data.Records from an XML document.
12572 * @param {Object} o An object which contains an Array of row objects in the property specified
12573 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12574 * which contains the total size of the dataset.
12575 * @return {Object} data A data block which is used by an Roo.data.Store object as
12576 * a cache of Roo.data.Records.
12578 readRecords : function(o){
12580 * After any data loads, the raw JSON data is available for further custom processing.
12584 var s = this.meta, Record = this.recordType,
12585 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12587 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12589 if(s.totalProperty) {
12590 this.getTotal = this.getJsonAccessor(s.totalProperty);
12592 if(s.successProperty) {
12593 this.getSuccess = this.getJsonAccessor(s.successProperty);
12595 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12597 var g = this.getJsonAccessor(s.id);
12598 this.getId = function(rec) {
12600 return (r === undefined || r === "") ? null : r;
12603 this.getId = function(){return null;};
12606 for(var jj = 0; jj < fl; jj++){
12608 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12609 this.ef[jj] = this.getJsonAccessor(map);
12613 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12614 if(s.totalProperty){
12615 var vt = parseInt(this.getTotal(o), 10);
12620 if(s.successProperty){
12621 var vs = this.getSuccess(o);
12622 if(vs === false || vs === 'false'){
12627 for(var i = 0; i < c; i++){
12630 var id = this.getId(n);
12631 for(var j = 0; j < fl; j++){
12633 var v = this.ef[j](n);
12635 Roo.log('missing convert for ' + f.name);
12639 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12641 var record = new Record(values, id);
12643 records[i] = record;
12649 totalRecords : totalRecords
12654 * Ext JS Library 1.1.1
12655 * Copyright(c) 2006-2007, Ext JS, LLC.
12657 * Originally Released Under LGPL - original licence link has changed is not relivant.
12660 * <script type="text/javascript">
12664 * @class Roo.data.ArrayReader
12665 * @extends Roo.data.DataReader
12666 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12667 * Each element of that Array represents a row of data fields. The
12668 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12669 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12673 var RecordDef = Roo.data.Record.create([
12674 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12675 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12677 var myReader = new Roo.data.ArrayReader({
12678 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12682 * This would consume an Array like this:
12684 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12686 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12688 * Create a new JsonReader
12689 * @param {Object} meta Metadata configuration options.
12690 * @param {Object} recordType Either an Array of field definition objects
12691 * as specified to {@link Roo.data.Record#create},
12692 * or an {@link Roo.data.Record} object
12693 * created using {@link Roo.data.Record#create}.
12695 Roo.data.ArrayReader = function(meta, recordType){
12696 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12699 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12701 * Create a data block containing Roo.data.Records from an XML document.
12702 * @param {Object} o An Array of row objects which represents the dataset.
12703 * @return {Object} data A data block which is used by an Roo.data.Store object as
12704 * a cache of Roo.data.Records.
12706 readRecords : function(o){
12707 var sid = this.meta ? this.meta.id : null;
12708 var recordType = this.recordType, fields = recordType.prototype.fields;
12711 for(var i = 0; i < root.length; i++){
12714 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12715 for(var j = 0, jlen = fields.length; j < jlen; j++){
12716 var f = fields.items[j];
12717 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12718 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12720 values[f.name] = v;
12722 var record = new recordType(values, id);
12724 records[records.length] = record;
12728 totalRecords : records.length
12737 * @class Roo.bootstrap.ComboBox
12738 * @extends Roo.bootstrap.TriggerField
12739 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12740 * @cfg {Boolean} append (true|false) default false
12741 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12742 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12743 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12744 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12745 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12746 * @cfg {Boolean} animate default true
12747 * @cfg {Boolean} emptyResultText only for touch device
12748 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12749 * @cfg {String} emptyTitle default ''
12751 * Create a new ComboBox.
12752 * @param {Object} config Configuration options
12754 Roo.bootstrap.ComboBox = function(config){
12755 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12759 * Fires when the dropdown list is expanded
12760 * @param {Roo.bootstrap.ComboBox} combo This combo box
12765 * Fires when the dropdown list is collapsed
12766 * @param {Roo.bootstrap.ComboBox} combo This combo box
12770 * @event beforeselect
12771 * Fires before a list item is selected. Return false to cancel the selection.
12772 * @param {Roo.bootstrap.ComboBox} combo This combo box
12773 * @param {Roo.data.Record} record The data record returned from the underlying store
12774 * @param {Number} index The index of the selected item in the dropdown list
12776 'beforeselect' : true,
12779 * Fires when a list item is selected
12780 * @param {Roo.bootstrap.ComboBox} combo This combo box
12781 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12782 * @param {Number} index The index of the selected item in the dropdown list
12786 * @event beforequery
12787 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12788 * The event object passed has these properties:
12789 * @param {Roo.bootstrap.ComboBox} combo This combo box
12790 * @param {String} query The query
12791 * @param {Boolean} forceAll true to force "all" query
12792 * @param {Boolean} cancel true to cancel the query
12793 * @param {Object} e The query event object
12795 'beforequery': true,
12798 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12799 * @param {Roo.bootstrap.ComboBox} combo This combo box
12804 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12805 * @param {Roo.bootstrap.ComboBox} combo This combo box
12806 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12811 * Fires when the remove value from the combobox array
12812 * @param {Roo.bootstrap.ComboBox} combo This combo box
12816 * @event afterremove
12817 * Fires when the remove value from the combobox array
12818 * @param {Roo.bootstrap.ComboBox} combo This combo box
12820 'afterremove' : true,
12822 * @event specialfilter
12823 * Fires when specialfilter
12824 * @param {Roo.bootstrap.ComboBox} combo This combo box
12826 'specialfilter' : true,
12829 * Fires when tick the element
12830 * @param {Roo.bootstrap.ComboBox} combo This combo box
12834 * @event touchviewdisplay
12835 * Fires when touch view require special display (default is using displayField)
12836 * @param {Roo.bootstrap.ComboBox} combo This combo box
12837 * @param {Object} cfg set html .
12839 'touchviewdisplay' : true
12844 this.tickItems = [];
12846 this.selectedIndex = -1;
12847 if(this.mode == 'local'){
12848 if(config.queryDelay === undefined){
12849 this.queryDelay = 10;
12851 if(config.minChars === undefined){
12857 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12860 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12861 * rendering into an Roo.Editor, defaults to false)
12864 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12865 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12868 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12871 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12872 * the dropdown list (defaults to undefined, with no header element)
12876 * @cfg {String/Roo.Template} tpl The template to use to render the output
12880 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12882 listWidth: undefined,
12884 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12885 * mode = 'remote' or 'text' if mode = 'local')
12887 displayField: undefined,
12890 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12891 * mode = 'remote' or 'value' if mode = 'local').
12892 * Note: use of a valueField requires the user make a selection
12893 * in order for a value to be mapped.
12895 valueField: undefined,
12897 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12902 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12903 * field's data value (defaults to the underlying DOM element's name)
12905 hiddenName: undefined,
12907 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12911 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12913 selectedClass: 'active',
12916 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12920 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12921 * anchor positions (defaults to 'tl-bl')
12923 listAlign: 'tl-bl?',
12925 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12929 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12930 * query specified by the allQuery config option (defaults to 'query')
12932 triggerAction: 'query',
12934 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12935 * (defaults to 4, does not apply if editable = false)
12939 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12940 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12944 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12945 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12949 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12950 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12954 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12955 * when editable = true (defaults to false)
12957 selectOnFocus:false,
12959 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12961 queryParam: 'query',
12963 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12964 * when mode = 'remote' (defaults to 'Loading...')
12966 loadingText: 'Loading...',
12968 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12972 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12976 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12977 * traditional select (defaults to true)
12981 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12985 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12989 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12990 * listWidth has a higher value)
12994 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12995 * allow the user to set arbitrary text into the field (defaults to false)
12997 forceSelection:false,
12999 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13000 * if typeAhead = true (defaults to 250)
13002 typeAheadDelay : 250,
13004 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13005 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13007 valueNotFoundText : undefined,
13009 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13011 blockFocus : false,
13014 * @cfg {Boolean} disableClear Disable showing of clear button.
13016 disableClear : false,
13018 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13020 alwaysQuery : false,
13023 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13028 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13030 invalidClass : "has-warning",
13033 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13035 validClass : "has-success",
13038 * @cfg {Boolean} specialFilter (true|false) special filter default false
13040 specialFilter : false,
13043 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13045 mobileTouchView : true,
13048 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13050 useNativeIOS : false,
13053 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13055 mobile_restrict_height : false,
13057 ios_options : false,
13069 btnPosition : 'right',
13070 triggerList : true,
13071 showToggleBtn : true,
13073 emptyResultText: 'Empty',
13074 triggerText : 'Select',
13077 // element that contains real text value.. (when hidden is used..)
13079 getAutoCreate : function()
13084 * Render classic select for iso
13087 if(Roo.isIOS && this.useNativeIOS){
13088 cfg = this.getAutoCreateNativeIOS();
13096 if(Roo.isTouch && this.mobileTouchView){
13097 cfg = this.getAutoCreateTouchView();
13104 if(!this.tickable){
13105 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13110 * ComboBox with tickable selections
13113 var align = this.labelAlign || this.parentLabelAlign();
13116 cls : 'form-group roo-combobox-tickable' //input-group
13119 var btn_text_select = '';
13120 var btn_text_done = '';
13121 var btn_text_cancel = '';
13123 if (this.btn_text_show) {
13124 btn_text_select = 'Select';
13125 btn_text_done = 'Done';
13126 btn_text_cancel = 'Cancel';
13131 cls : 'tickable-buttons',
13136 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13137 //html : this.triggerText
13138 html: btn_text_select
13144 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13146 html: btn_text_done
13152 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13154 html: btn_text_cancel
13160 buttons.cn.unshift({
13162 cls: 'roo-select2-search-field-input'
13168 Roo.each(buttons.cn, function(c){
13170 c.cls += ' btn-' + _this.size;
13173 if (_this.disabled) {
13184 cls: 'form-hidden-field'
13188 cls: 'roo-select2-choices',
13192 cls: 'roo-select2-search-field',
13203 cls: 'roo-select2-container input-group roo-select2-container-multi',
13208 // cls: 'typeahead typeahead-long dropdown-menu',
13209 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13214 if(this.hasFeedback && !this.allowBlank){
13218 cls: 'glyphicon form-control-feedback'
13221 combobox.cn.push(feedback);
13225 if (align ==='left' && this.fieldLabel.length) {
13227 cfg.cls += ' roo-form-group-label-left';
13232 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13233 tooltip : 'This field is required'
13238 cls : 'control-label',
13239 html : this.fieldLabel
13251 var labelCfg = cfg.cn[1];
13252 var contentCfg = cfg.cn[2];
13255 if(this.indicatorpos == 'right'){
13261 cls : 'control-label',
13265 html : this.fieldLabel
13269 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13270 tooltip : 'This field is required'
13285 labelCfg = cfg.cn[0];
13286 contentCfg = cfg.cn[1];
13290 if(this.labelWidth > 12){
13291 labelCfg.style = "width: " + this.labelWidth + 'px';
13294 if(this.labelWidth < 13 && this.labelmd == 0){
13295 this.labelmd = this.labelWidth;
13298 if(this.labellg > 0){
13299 labelCfg.cls += ' col-lg-' + this.labellg;
13300 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13303 if(this.labelmd > 0){
13304 labelCfg.cls += ' col-md-' + this.labelmd;
13305 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13308 if(this.labelsm > 0){
13309 labelCfg.cls += ' col-sm-' + this.labelsm;
13310 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13313 if(this.labelxs > 0){
13314 labelCfg.cls += ' col-xs-' + this.labelxs;
13315 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13319 } else if ( this.fieldLabel.length) {
13320 // Roo.log(" label");
13324 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13325 tooltip : 'This field is required'
13329 //cls : 'input-group-addon',
13330 html : this.fieldLabel
13335 if(this.indicatorpos == 'right'){
13339 //cls : 'input-group-addon',
13340 html : this.fieldLabel
13344 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13345 tooltip : 'This field is required'
13354 // Roo.log(" no label && no align");
13361 ['xs','sm','md','lg'].map(function(size){
13362 if (settings[size]) {
13363 cfg.cls += ' col-' + size + '-' + settings[size];
13371 _initEventsCalled : false,
13374 initEvents: function()
13376 if (this._initEventsCalled) { // as we call render... prevent looping...
13379 this._initEventsCalled = true;
13382 throw "can not find store for combo";
13385 this.indicator = this.indicatorEl();
13387 this.store = Roo.factory(this.store, Roo.data);
13388 this.store.parent = this;
13390 // if we are building from html. then this element is so complex, that we can not really
13391 // use the rendered HTML.
13392 // so we have to trash and replace the previous code.
13393 if (Roo.XComponent.build_from_html) {
13394 // remove this element....
13395 var e = this.el.dom, k=0;
13396 while (e ) { e = e.previousSibling; ++k;}
13401 this.rendered = false;
13403 this.render(this.parent().getChildContainer(true), k);
13406 if(Roo.isIOS && this.useNativeIOS){
13407 this.initIOSView();
13415 if(Roo.isTouch && this.mobileTouchView){
13416 this.initTouchView();
13421 this.initTickableEvents();
13425 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13427 if(this.hiddenName){
13429 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13431 this.hiddenField.dom.value =
13432 this.hiddenValue !== undefined ? this.hiddenValue :
13433 this.value !== undefined ? this.value : '';
13435 // prevent input submission
13436 this.el.dom.removeAttribute('name');
13437 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13442 // this.el.dom.setAttribute('autocomplete', 'off');
13445 var cls = 'x-combo-list';
13447 //this.list = new Roo.Layer({
13448 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13454 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13455 _this.list.setWidth(lw);
13458 this.list.on('mouseover', this.onViewOver, this);
13459 this.list.on('mousemove', this.onViewMove, this);
13460 this.list.on('scroll', this.onViewScroll, this);
13463 this.list.swallowEvent('mousewheel');
13464 this.assetHeight = 0;
13467 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13468 this.assetHeight += this.header.getHeight();
13471 this.innerList = this.list.createChild({cls:cls+'-inner'});
13472 this.innerList.on('mouseover', this.onViewOver, this);
13473 this.innerList.on('mousemove', this.onViewMove, this);
13474 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13476 if(this.allowBlank && !this.pageSize && !this.disableClear){
13477 this.footer = this.list.createChild({cls:cls+'-ft'});
13478 this.pageTb = new Roo.Toolbar(this.footer);
13482 this.footer = this.list.createChild({cls:cls+'-ft'});
13483 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13484 {pageSize: this.pageSize});
13488 if (this.pageTb && this.allowBlank && !this.disableClear) {
13490 this.pageTb.add(new Roo.Toolbar.Fill(), {
13491 cls: 'x-btn-icon x-btn-clear',
13493 handler: function()
13496 _this.clearValue();
13497 _this.onSelect(false, -1);
13502 this.assetHeight += this.footer.getHeight();
13507 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13510 this.view = new Roo.View(this.list, this.tpl, {
13511 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13513 //this.view.wrapEl.setDisplayed(false);
13514 this.view.on('click', this.onViewClick, this);
13517 this.store.on('beforeload', this.onBeforeLoad, this);
13518 this.store.on('load', this.onLoad, this);
13519 this.store.on('loadexception', this.onLoadException, this);
13521 if(this.resizable){
13522 this.resizer = new Roo.Resizable(this.list, {
13523 pinned:true, handles:'se'
13525 this.resizer.on('resize', function(r, w, h){
13526 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13527 this.listWidth = w;
13528 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13529 this.restrictHeight();
13531 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13534 if(!this.editable){
13535 this.editable = true;
13536 this.setEditable(false);
13541 if (typeof(this.events.add.listeners) != 'undefined') {
13543 this.addicon = this.wrap.createChild(
13544 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13546 this.addicon.on('click', function(e) {
13547 this.fireEvent('add', this);
13550 if (typeof(this.events.edit.listeners) != 'undefined') {
13552 this.editicon = this.wrap.createChild(
13553 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13554 if (this.addicon) {
13555 this.editicon.setStyle('margin-left', '40px');
13557 this.editicon.on('click', function(e) {
13559 // we fire even if inothing is selected..
13560 this.fireEvent('edit', this, this.lastData );
13566 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13567 "up" : function(e){
13568 this.inKeyMode = true;
13572 "down" : function(e){
13573 if(!this.isExpanded()){
13574 this.onTriggerClick();
13576 this.inKeyMode = true;
13581 "enter" : function(e){
13582 // this.onViewClick();
13586 if(this.fireEvent("specialkey", this, e)){
13587 this.onViewClick(false);
13593 "esc" : function(e){
13597 "tab" : function(e){
13600 if(this.fireEvent("specialkey", this, e)){
13601 this.onViewClick(false);
13609 doRelay : function(foo, bar, hname){
13610 if(hname == 'down' || this.scope.isExpanded()){
13611 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13620 this.queryDelay = Math.max(this.queryDelay || 10,
13621 this.mode == 'local' ? 10 : 250);
13624 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13626 if(this.typeAhead){
13627 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13629 if(this.editable !== false){
13630 this.inputEl().on("keyup", this.onKeyUp, this);
13632 if(this.forceSelection){
13633 this.inputEl().on('blur', this.doForce, this);
13637 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13638 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13642 initTickableEvents: function()
13646 if(this.hiddenName){
13648 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13650 this.hiddenField.dom.value =
13651 this.hiddenValue !== undefined ? this.hiddenValue :
13652 this.value !== undefined ? this.value : '';
13654 // prevent input submission
13655 this.el.dom.removeAttribute('name');
13656 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13661 // this.list = this.el.select('ul.dropdown-menu',true).first();
13663 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13664 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13665 if(this.triggerList){
13666 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13669 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13670 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13672 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13673 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13675 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13676 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13678 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13679 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13680 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13683 this.cancelBtn.hide();
13688 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13689 _this.list.setWidth(lw);
13692 this.list.on('mouseover', this.onViewOver, this);
13693 this.list.on('mousemove', this.onViewMove, this);
13695 this.list.on('scroll', this.onViewScroll, this);
13698 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13699 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13702 this.view = new Roo.View(this.list, this.tpl, {
13707 selectedClass: this.selectedClass
13710 //this.view.wrapEl.setDisplayed(false);
13711 this.view.on('click', this.onViewClick, this);
13715 this.store.on('beforeload', this.onBeforeLoad, this);
13716 this.store.on('load', this.onLoad, this);
13717 this.store.on('loadexception', this.onLoadException, this);
13720 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13721 "up" : function(e){
13722 this.inKeyMode = true;
13726 "down" : function(e){
13727 this.inKeyMode = true;
13731 "enter" : function(e){
13732 if(this.fireEvent("specialkey", this, e)){
13733 this.onViewClick(false);
13739 "esc" : function(e){
13740 this.onTickableFooterButtonClick(e, false, false);
13743 "tab" : function(e){
13744 this.fireEvent("specialkey", this, e);
13746 this.onTickableFooterButtonClick(e, false, false);
13753 doRelay : function(e, fn, key){
13754 if(this.scope.isExpanded()){
13755 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13764 this.queryDelay = Math.max(this.queryDelay || 10,
13765 this.mode == 'local' ? 10 : 250);
13768 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13770 if(this.typeAhead){
13771 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13774 if(this.editable !== false){
13775 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13778 this.indicator = this.indicatorEl();
13780 if(this.indicator){
13781 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13782 this.indicator.hide();
13787 onDestroy : function(){
13789 this.view.setStore(null);
13790 this.view.el.removeAllListeners();
13791 this.view.el.remove();
13792 this.view.purgeListeners();
13795 this.list.dom.innerHTML = '';
13799 this.store.un('beforeload', this.onBeforeLoad, this);
13800 this.store.un('load', this.onLoad, this);
13801 this.store.un('loadexception', this.onLoadException, this);
13803 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13807 fireKey : function(e){
13808 if(e.isNavKeyPress() && !this.list.isVisible()){
13809 this.fireEvent("specialkey", this, e);
13814 onResize: function(w, h){
13815 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13817 // if(typeof w != 'number'){
13818 // // we do not handle it!?!?
13821 // var tw = this.trigger.getWidth();
13822 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13823 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13825 // this.inputEl().setWidth( this.adjustWidth('input', x));
13827 // //this.trigger.setStyle('left', x+'px');
13829 // if(this.list && this.listWidth === undefined){
13830 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13831 // this.list.setWidth(lw);
13832 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13840 * Allow or prevent the user from directly editing the field text. If false is passed,
13841 * the user will only be able to select from the items defined in the dropdown list. This method
13842 * is the runtime equivalent of setting the 'editable' config option at config time.
13843 * @param {Boolean} value True to allow the user to directly edit the field text
13845 setEditable : function(value){
13846 if(value == this.editable){
13849 this.editable = value;
13851 this.inputEl().dom.setAttribute('readOnly', true);
13852 this.inputEl().on('mousedown', this.onTriggerClick, this);
13853 this.inputEl().addClass('x-combo-noedit');
13855 this.inputEl().dom.setAttribute('readOnly', false);
13856 this.inputEl().un('mousedown', this.onTriggerClick, this);
13857 this.inputEl().removeClass('x-combo-noedit');
13863 onBeforeLoad : function(combo,opts){
13864 if(!this.hasFocus){
13868 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13870 this.restrictHeight();
13871 this.selectedIndex = -1;
13875 onLoad : function(){
13877 this.hasQuery = false;
13879 if(!this.hasFocus){
13883 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13884 this.loading.hide();
13887 if(this.store.getCount() > 0){
13890 this.restrictHeight();
13891 if(this.lastQuery == this.allQuery){
13892 if(this.editable && !this.tickable){
13893 this.inputEl().dom.select();
13897 !this.selectByValue(this.value, true) &&
13900 !this.store.lastOptions ||
13901 typeof(this.store.lastOptions.add) == 'undefined' ||
13902 this.store.lastOptions.add != true
13905 this.select(0, true);
13908 if(this.autoFocus){
13911 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13912 this.taTask.delay(this.typeAheadDelay);
13916 this.onEmptyResults();
13922 onLoadException : function()
13924 this.hasQuery = false;
13926 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13927 this.loading.hide();
13930 if(this.tickable && this.editable){
13935 // only causes errors at present
13936 //Roo.log(this.store.reader.jsonData);
13937 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13939 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13945 onTypeAhead : function(){
13946 if(this.store.getCount() > 0){
13947 var r = this.store.getAt(0);
13948 var newValue = r.data[this.displayField];
13949 var len = newValue.length;
13950 var selStart = this.getRawValue().length;
13952 if(selStart != len){
13953 this.setRawValue(newValue);
13954 this.selectText(selStart, newValue.length);
13960 onSelect : function(record, index){
13962 if(this.fireEvent('beforeselect', this, record, index) !== false){
13964 this.setFromData(index > -1 ? record.data : false);
13967 this.fireEvent('select', this, record, index);
13972 * Returns the currently selected field value or empty string if no value is set.
13973 * @return {String} value The selected value
13975 getValue : function()
13977 if(Roo.isIOS && this.useNativeIOS){
13978 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13982 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13985 if(this.valueField){
13986 return typeof this.value != 'undefined' ? this.value : '';
13988 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13992 getRawValue : function()
13994 if(Roo.isIOS && this.useNativeIOS){
13995 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13998 var v = this.inputEl().getValue();
14004 * Clears any text/value currently set in the field
14006 clearValue : function(){
14008 if(this.hiddenField){
14009 this.hiddenField.dom.value = '';
14012 this.setRawValue('');
14013 this.lastSelectionText = '';
14014 this.lastData = false;
14016 var close = this.closeTriggerEl();
14027 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14028 * will be displayed in the field. If the value does not match the data value of an existing item,
14029 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14030 * Otherwise the field will be blank (although the value will still be set).
14031 * @param {String} value The value to match
14033 setValue : function(v)
14035 if(Roo.isIOS && this.useNativeIOS){
14036 this.setIOSValue(v);
14046 if(this.valueField){
14047 var r = this.findRecord(this.valueField, v);
14049 text = r.data[this.displayField];
14050 }else if(this.valueNotFoundText !== undefined){
14051 text = this.valueNotFoundText;
14054 this.lastSelectionText = text;
14055 if(this.hiddenField){
14056 this.hiddenField.dom.value = v;
14058 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14061 var close = this.closeTriggerEl();
14064 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14070 * @property {Object} the last set data for the element
14075 * Sets the value of the field based on a object which is related to the record format for the store.
14076 * @param {Object} value the value to set as. or false on reset?
14078 setFromData : function(o){
14085 var dv = ''; // display value
14086 var vv = ''; // value value..
14088 if (this.displayField) {
14089 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14091 // this is an error condition!!!
14092 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14095 if(this.valueField){
14096 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14099 var close = this.closeTriggerEl();
14102 if(dv.length || vv * 1 > 0){
14104 this.blockFocus=true;
14110 if(this.hiddenField){
14111 this.hiddenField.dom.value = vv;
14113 this.lastSelectionText = dv;
14114 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14118 // no hidden field.. - we store the value in 'value', but still display
14119 // display field!!!!
14120 this.lastSelectionText = dv;
14121 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14128 reset : function(){
14129 // overridden so that last data is reset..
14136 this.setValue(this.originalValue);
14137 //this.clearInvalid();
14138 this.lastData = false;
14140 this.view.clearSelections();
14146 findRecord : function(prop, value){
14148 if(this.store.getCount() > 0){
14149 this.store.each(function(r){
14150 if(r.data[prop] == value){
14160 getName: function()
14162 // returns hidden if it's set..
14163 if (!this.rendered) {return ''};
14164 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14168 onViewMove : function(e, t){
14169 this.inKeyMode = false;
14173 onViewOver : function(e, t){
14174 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14177 var item = this.view.findItemFromChild(t);
14180 var index = this.view.indexOf(item);
14181 this.select(index, false);
14186 onViewClick : function(view, doFocus, el, e)
14188 var index = this.view.getSelectedIndexes()[0];
14190 var r = this.store.getAt(index);
14194 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14201 Roo.each(this.tickItems, function(v,k){
14203 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14205 _this.tickItems.splice(k, 1);
14207 if(typeof(e) == 'undefined' && view == false){
14208 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14220 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14221 this.tickItems.push(r.data);
14224 if(typeof(e) == 'undefined' && view == false){
14225 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14232 this.onSelect(r, index);
14234 if(doFocus !== false && !this.blockFocus){
14235 this.inputEl().focus();
14240 restrictHeight : function(){
14241 //this.innerList.dom.style.height = '';
14242 //var inner = this.innerList.dom;
14243 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14244 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14245 //this.list.beginUpdate();
14246 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14247 this.list.alignTo(this.inputEl(), this.listAlign);
14248 this.list.alignTo(this.inputEl(), this.listAlign);
14249 //this.list.endUpdate();
14253 onEmptyResults : function(){
14255 if(this.tickable && this.editable){
14256 this.hasFocus = false;
14257 this.restrictHeight();
14265 * Returns true if the dropdown list is expanded, else false.
14267 isExpanded : function(){
14268 return this.list.isVisible();
14272 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14273 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14274 * @param {String} value The data value of the item to select
14275 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14276 * selected item if it is not currently in view (defaults to true)
14277 * @return {Boolean} True if the value matched an item in the list, else false
14279 selectByValue : function(v, scrollIntoView){
14280 if(v !== undefined && v !== null){
14281 var r = this.findRecord(this.valueField || this.displayField, v);
14283 this.select(this.store.indexOf(r), scrollIntoView);
14291 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14292 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14293 * @param {Number} index The zero-based index of the list item to select
14294 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14295 * selected item if it is not currently in view (defaults to true)
14297 select : function(index, scrollIntoView){
14298 this.selectedIndex = index;
14299 this.view.select(index);
14300 if(scrollIntoView !== false){
14301 var el = this.view.getNode(index);
14303 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14306 this.list.scrollChildIntoView(el, false);
14312 selectNext : function(){
14313 var ct = this.store.getCount();
14315 if(this.selectedIndex == -1){
14317 }else if(this.selectedIndex < ct-1){
14318 this.select(this.selectedIndex+1);
14324 selectPrev : function(){
14325 var ct = this.store.getCount();
14327 if(this.selectedIndex == -1){
14329 }else if(this.selectedIndex != 0){
14330 this.select(this.selectedIndex-1);
14336 onKeyUp : function(e){
14337 if(this.editable !== false && !e.isSpecialKey()){
14338 this.lastKey = e.getKey();
14339 this.dqTask.delay(this.queryDelay);
14344 validateBlur : function(){
14345 return !this.list || !this.list.isVisible();
14349 initQuery : function(){
14351 var v = this.getRawValue();
14353 if(this.tickable && this.editable){
14354 v = this.tickableInputEl().getValue();
14361 doForce : function(){
14362 if(this.inputEl().dom.value.length > 0){
14363 this.inputEl().dom.value =
14364 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14370 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14371 * query allowing the query action to be canceled if needed.
14372 * @param {String} query The SQL query to execute
14373 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14374 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14375 * saved in the current store (defaults to false)
14377 doQuery : function(q, forceAll){
14379 if(q === undefined || q === null){
14384 forceAll: forceAll,
14388 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14393 forceAll = qe.forceAll;
14394 if(forceAll === true || (q.length >= this.minChars)){
14396 this.hasQuery = true;
14398 if(this.lastQuery != q || this.alwaysQuery){
14399 this.lastQuery = q;
14400 if(this.mode == 'local'){
14401 this.selectedIndex = -1;
14403 this.store.clearFilter();
14406 if(this.specialFilter){
14407 this.fireEvent('specialfilter', this);
14412 this.store.filter(this.displayField, q);
14415 this.store.fireEvent("datachanged", this.store);
14422 this.store.baseParams[this.queryParam] = q;
14424 var options = {params : this.getParams(q)};
14427 options.add = true;
14428 options.params.start = this.page * this.pageSize;
14431 this.store.load(options);
14434 * this code will make the page width larger, at the beginning, the list not align correctly,
14435 * we should expand the list on onLoad
14436 * so command out it
14441 this.selectedIndex = -1;
14446 this.loadNext = false;
14450 getParams : function(q){
14452 //p[this.queryParam] = q;
14456 p.limit = this.pageSize;
14462 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14464 collapse : function(){
14465 if(!this.isExpanded()){
14471 this.hasFocus = false;
14475 this.cancelBtn.hide();
14476 this.trigger.show();
14479 this.tickableInputEl().dom.value = '';
14480 this.tickableInputEl().blur();
14485 Roo.get(document).un('mousedown', this.collapseIf, this);
14486 Roo.get(document).un('mousewheel', this.collapseIf, this);
14487 if (!this.editable) {
14488 Roo.get(document).un('keydown', this.listKeyPress, this);
14490 this.fireEvent('collapse', this);
14496 collapseIf : function(e){
14497 var in_combo = e.within(this.el);
14498 var in_list = e.within(this.list);
14499 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14501 if (in_combo || in_list || is_list) {
14502 //e.stopPropagation();
14507 this.onTickableFooterButtonClick(e, false, false);
14515 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14517 expand : function(){
14519 if(this.isExpanded() || !this.hasFocus){
14523 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14524 this.list.setWidth(lw);
14530 this.restrictHeight();
14534 this.tickItems = Roo.apply([], this.item);
14537 this.cancelBtn.show();
14538 this.trigger.hide();
14541 this.tickableInputEl().focus();
14546 Roo.get(document).on('mousedown', this.collapseIf, this);
14547 Roo.get(document).on('mousewheel', this.collapseIf, this);
14548 if (!this.editable) {
14549 Roo.get(document).on('keydown', this.listKeyPress, this);
14552 this.fireEvent('expand', this);
14556 // Implements the default empty TriggerField.onTriggerClick function
14557 onTriggerClick : function(e)
14559 Roo.log('trigger click');
14561 if(this.disabled || !this.triggerList){
14566 this.loadNext = false;
14568 if(this.isExpanded()){
14570 if (!this.blockFocus) {
14571 this.inputEl().focus();
14575 this.hasFocus = true;
14576 if(this.triggerAction == 'all') {
14577 this.doQuery(this.allQuery, true);
14579 this.doQuery(this.getRawValue());
14581 if (!this.blockFocus) {
14582 this.inputEl().focus();
14587 onTickableTriggerClick : function(e)
14594 this.loadNext = false;
14595 this.hasFocus = true;
14597 if(this.triggerAction == 'all') {
14598 this.doQuery(this.allQuery, true);
14600 this.doQuery(this.getRawValue());
14604 onSearchFieldClick : function(e)
14606 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14607 this.onTickableFooterButtonClick(e, false, false);
14611 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14616 this.loadNext = false;
14617 this.hasFocus = true;
14619 if(this.triggerAction == 'all') {
14620 this.doQuery(this.allQuery, true);
14622 this.doQuery(this.getRawValue());
14626 listKeyPress : function(e)
14628 //Roo.log('listkeypress');
14629 // scroll to first matching element based on key pres..
14630 if (e.isSpecialKey()) {
14633 var k = String.fromCharCode(e.getKey()).toUpperCase();
14636 var csel = this.view.getSelectedNodes();
14637 var cselitem = false;
14639 var ix = this.view.indexOf(csel[0]);
14640 cselitem = this.store.getAt(ix);
14641 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14647 this.store.each(function(v) {
14649 // start at existing selection.
14650 if (cselitem.id == v.id) {
14656 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14657 match = this.store.indexOf(v);
14663 if (match === false) {
14664 return true; // no more action?
14667 this.view.select(match);
14668 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14669 sn.scrollIntoView(sn.dom.parentNode, false);
14672 onViewScroll : function(e, t){
14674 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){
14678 this.hasQuery = true;
14680 this.loading = this.list.select('.loading', true).first();
14682 if(this.loading === null){
14683 this.list.createChild({
14685 cls: 'loading roo-select2-more-results roo-select2-active',
14686 html: 'Loading more results...'
14689 this.loading = this.list.select('.loading', true).first();
14691 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14693 this.loading.hide();
14696 this.loading.show();
14701 this.loadNext = true;
14703 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14708 addItem : function(o)
14710 var dv = ''; // display value
14712 if (this.displayField) {
14713 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14715 // this is an error condition!!!
14716 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14723 var choice = this.choices.createChild({
14725 cls: 'roo-select2-search-choice',
14734 cls: 'roo-select2-search-choice-close fa fa-times',
14739 }, this.searchField);
14741 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14743 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14751 this.inputEl().dom.value = '';
14756 onRemoveItem : function(e, _self, o)
14758 e.preventDefault();
14760 this.lastItem = Roo.apply([], this.item);
14762 var index = this.item.indexOf(o.data) * 1;
14765 Roo.log('not this item?!');
14769 this.item.splice(index, 1);
14774 this.fireEvent('remove', this, e);
14780 syncValue : function()
14782 if(!this.item.length){
14789 Roo.each(this.item, function(i){
14790 if(_this.valueField){
14791 value.push(i[_this.valueField]);
14798 this.value = value.join(',');
14800 if(this.hiddenField){
14801 this.hiddenField.dom.value = this.value;
14804 this.store.fireEvent("datachanged", this.store);
14809 clearItem : function()
14811 if(!this.multiple){
14817 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14825 if(this.tickable && !Roo.isTouch){
14826 this.view.refresh();
14830 inputEl: function ()
14832 if(Roo.isIOS && this.useNativeIOS){
14833 return this.el.select('select.roo-ios-select', true).first();
14836 if(Roo.isTouch && this.mobileTouchView){
14837 return this.el.select('input.form-control',true).first();
14841 return this.searchField;
14844 return this.el.select('input.form-control',true).first();
14847 onTickableFooterButtonClick : function(e, btn, el)
14849 e.preventDefault();
14851 this.lastItem = Roo.apply([], this.item);
14853 if(btn && btn.name == 'cancel'){
14854 this.tickItems = Roo.apply([], this.item);
14863 Roo.each(this.tickItems, function(o){
14871 validate : function()
14873 if(this.getVisibilityEl().hasClass('hidden')){
14877 var v = this.getRawValue();
14880 v = this.getValue();
14883 if(this.disabled || this.allowBlank || v.length){
14888 this.markInvalid();
14892 tickableInputEl : function()
14894 if(!this.tickable || !this.editable){
14895 return this.inputEl();
14898 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14902 getAutoCreateTouchView : function()
14907 cls: 'form-group' //input-group
14913 type : this.inputType,
14914 cls : 'form-control x-combo-noedit',
14915 autocomplete: 'new-password',
14916 placeholder : this.placeholder || '',
14921 input.name = this.name;
14925 input.cls += ' input-' + this.size;
14928 if (this.disabled) {
14929 input.disabled = true;
14940 inputblock.cls += ' input-group';
14942 inputblock.cn.unshift({
14944 cls : 'input-group-addon',
14949 if(this.removable && !this.multiple){
14950 inputblock.cls += ' roo-removable';
14952 inputblock.cn.push({
14955 cls : 'roo-combo-removable-btn close'
14959 if(this.hasFeedback && !this.allowBlank){
14961 inputblock.cls += ' has-feedback';
14963 inputblock.cn.push({
14965 cls: 'glyphicon form-control-feedback'
14972 inputblock.cls += (this.before) ? '' : ' input-group';
14974 inputblock.cn.push({
14976 cls : 'input-group-addon',
14987 cls: 'form-hidden-field'
15001 cls: 'form-hidden-field'
15005 cls: 'roo-select2-choices',
15009 cls: 'roo-select2-search-field',
15022 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15028 if(!this.multiple && this.showToggleBtn){
15035 if (this.caret != false) {
15038 cls: 'fa fa-' + this.caret
15045 cls : 'input-group-addon btn dropdown-toggle',
15050 cls: 'combobox-clear',
15064 combobox.cls += ' roo-select2-container-multi';
15067 var align = this.labelAlign || this.parentLabelAlign();
15069 if (align ==='left' && this.fieldLabel.length) {
15074 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15075 tooltip : 'This field is required'
15079 cls : 'control-label',
15080 html : this.fieldLabel
15091 var labelCfg = cfg.cn[1];
15092 var contentCfg = cfg.cn[2];
15095 if(this.indicatorpos == 'right'){
15100 cls : 'control-label',
15104 html : this.fieldLabel
15108 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15109 tooltip : 'This field is required'
15122 labelCfg = cfg.cn[0];
15123 contentCfg = cfg.cn[1];
15128 if(this.labelWidth > 12){
15129 labelCfg.style = "width: " + this.labelWidth + 'px';
15132 if(this.labelWidth < 13 && this.labelmd == 0){
15133 this.labelmd = this.labelWidth;
15136 if(this.labellg > 0){
15137 labelCfg.cls += ' col-lg-' + this.labellg;
15138 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15141 if(this.labelmd > 0){
15142 labelCfg.cls += ' col-md-' + this.labelmd;
15143 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15146 if(this.labelsm > 0){
15147 labelCfg.cls += ' col-sm-' + this.labelsm;
15148 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15151 if(this.labelxs > 0){
15152 labelCfg.cls += ' col-xs-' + this.labelxs;
15153 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15157 } else if ( this.fieldLabel.length) {
15161 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15162 tooltip : 'This field is required'
15166 cls : 'control-label',
15167 html : this.fieldLabel
15178 if(this.indicatorpos == 'right'){
15182 cls : 'control-label',
15183 html : this.fieldLabel,
15187 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15188 tooltip : 'This field is required'
15205 var settings = this;
15207 ['xs','sm','md','lg'].map(function(size){
15208 if (settings[size]) {
15209 cfg.cls += ' col-' + size + '-' + settings[size];
15216 initTouchView : function()
15218 this.renderTouchView();
15220 this.touchViewEl.on('scroll', function(){
15221 this.el.dom.scrollTop = 0;
15224 this.originalValue = this.getValue();
15226 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15228 this.inputEl().on("click", this.showTouchView, this);
15229 if (this.triggerEl) {
15230 this.triggerEl.on("click", this.showTouchView, this);
15234 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15235 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15237 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15239 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15240 this.store.on('load', this.onTouchViewLoad, this);
15241 this.store.on('loadexception', this.onTouchViewLoadException, this);
15243 if(this.hiddenName){
15245 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15247 this.hiddenField.dom.value =
15248 this.hiddenValue !== undefined ? this.hiddenValue :
15249 this.value !== undefined ? this.value : '';
15251 this.el.dom.removeAttribute('name');
15252 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15256 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15257 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15260 if(this.removable && !this.multiple){
15261 var close = this.closeTriggerEl();
15263 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15264 close.on('click', this.removeBtnClick, this, close);
15268 * fix the bug in Safari iOS8
15270 this.inputEl().on("focus", function(e){
15271 document.activeElement.blur();
15274 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15281 renderTouchView : function()
15283 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15284 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15286 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15287 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15289 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15290 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15291 this.touchViewBodyEl.setStyle('overflow', 'auto');
15293 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15294 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15296 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15297 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15301 showTouchView : function()
15307 this.touchViewHeaderEl.hide();
15309 if(this.modalTitle.length){
15310 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15311 this.touchViewHeaderEl.show();
15314 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15315 this.touchViewEl.show();
15317 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15319 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15320 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15322 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15324 if(this.modalTitle.length){
15325 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15328 this.touchViewBodyEl.setHeight(bodyHeight);
15332 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15334 this.touchViewEl.addClass('in');
15337 if(this._touchViewMask){
15338 Roo.get(document.body).addClass("x-body-masked");
15339 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15340 this._touchViewMask.setStyle('z-index', 10000);
15341 this._touchViewMask.addClass('show');
15344 this.doTouchViewQuery();
15348 hideTouchView : function()
15350 this.touchViewEl.removeClass('in');
15354 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15356 this.touchViewEl.setStyle('display', 'none');
15359 if(this._touchViewMask){
15360 this._touchViewMask.removeClass('show');
15361 Roo.get(document.body).removeClass("x-body-masked");
15365 setTouchViewValue : function()
15372 Roo.each(this.tickItems, function(o){
15377 this.hideTouchView();
15380 doTouchViewQuery : function()
15389 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15393 if(!this.alwaysQuery || this.mode == 'local'){
15394 this.onTouchViewLoad();
15401 onTouchViewBeforeLoad : function(combo,opts)
15407 onTouchViewLoad : function()
15409 if(this.store.getCount() < 1){
15410 this.onTouchViewEmptyResults();
15414 this.clearTouchView();
15416 var rawValue = this.getRawValue();
15418 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15420 this.tickItems = [];
15422 this.store.data.each(function(d, rowIndex){
15423 var row = this.touchViewListGroup.createChild(template);
15425 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15426 row.addClass(d.data.cls);
15429 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15432 html : d.data[this.displayField]
15435 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15436 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15439 row.removeClass('selected');
15440 if(!this.multiple && this.valueField &&
15441 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15444 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15445 row.addClass('selected');
15448 if(this.multiple && this.valueField &&
15449 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15453 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15454 this.tickItems.push(d.data);
15457 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15461 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15463 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15465 if(this.modalTitle.length){
15466 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15469 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15471 if(this.mobile_restrict_height && listHeight < bodyHeight){
15472 this.touchViewBodyEl.setHeight(listHeight);
15477 if(firstChecked && listHeight > bodyHeight){
15478 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15483 onTouchViewLoadException : function()
15485 this.hideTouchView();
15488 onTouchViewEmptyResults : function()
15490 this.clearTouchView();
15492 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15494 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15498 clearTouchView : function()
15500 this.touchViewListGroup.dom.innerHTML = '';
15503 onTouchViewClick : function(e, el, o)
15505 e.preventDefault();
15508 var rowIndex = o.rowIndex;
15510 var r = this.store.getAt(rowIndex);
15512 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15514 if(!this.multiple){
15515 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15516 c.dom.removeAttribute('checked');
15519 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15521 this.setFromData(r.data);
15523 var close = this.closeTriggerEl();
15529 this.hideTouchView();
15531 this.fireEvent('select', this, r, rowIndex);
15536 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15537 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15538 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15542 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15543 this.addItem(r.data);
15544 this.tickItems.push(r.data);
15548 getAutoCreateNativeIOS : function()
15551 cls: 'form-group' //input-group,
15556 cls : 'roo-ios-select'
15560 combobox.name = this.name;
15563 if (this.disabled) {
15564 combobox.disabled = true;
15567 var settings = this;
15569 ['xs','sm','md','lg'].map(function(size){
15570 if (settings[size]) {
15571 cfg.cls += ' col-' + size + '-' + settings[size];
15581 initIOSView : function()
15583 this.store.on('load', this.onIOSViewLoad, this);
15588 onIOSViewLoad : function()
15590 if(this.store.getCount() < 1){
15594 this.clearIOSView();
15596 if(this.allowBlank) {
15598 var default_text = '-- SELECT --';
15600 if(this.placeholder.length){
15601 default_text = this.placeholder;
15604 if(this.emptyTitle.length){
15605 default_text += ' - ' + this.emptyTitle + ' -';
15608 var opt = this.inputEl().createChild({
15611 html : default_text
15615 o[this.valueField] = 0;
15616 o[this.displayField] = default_text;
15618 this.ios_options.push({
15625 this.store.data.each(function(d, rowIndex){
15629 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15630 html = d.data[this.displayField];
15635 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15636 value = d.data[this.valueField];
15645 if(this.value == d.data[this.valueField]){
15646 option['selected'] = true;
15649 var opt = this.inputEl().createChild(option);
15651 this.ios_options.push({
15658 this.inputEl().on('change', function(){
15659 this.fireEvent('select', this);
15664 clearIOSView: function()
15666 this.inputEl().dom.innerHTML = '';
15668 this.ios_options = [];
15671 setIOSValue: function(v)
15675 if(!this.ios_options){
15679 Roo.each(this.ios_options, function(opts){
15681 opts.el.dom.removeAttribute('selected');
15683 if(opts.data[this.valueField] != v){
15687 opts.el.dom.setAttribute('selected', true);
15693 * @cfg {Boolean} grow
15697 * @cfg {Number} growMin
15701 * @cfg {Number} growMax
15710 Roo.apply(Roo.bootstrap.ComboBox, {
15714 cls: 'modal-header',
15736 cls: 'list-group-item',
15740 cls: 'roo-combobox-list-group-item-value'
15744 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15758 listItemCheckbox : {
15760 cls: 'list-group-item',
15764 cls: 'roo-combobox-list-group-item-value'
15768 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15784 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15789 cls: 'modal-footer',
15797 cls: 'col-xs-6 text-left',
15800 cls: 'btn btn-danger roo-touch-view-cancel',
15806 cls: 'col-xs-6 text-right',
15809 cls: 'btn btn-success roo-touch-view-ok',
15820 Roo.apply(Roo.bootstrap.ComboBox, {
15822 touchViewTemplate : {
15824 cls: 'modal fade roo-combobox-touch-view',
15828 cls: 'modal-dialog',
15829 style : 'position:fixed', // we have to fix position....
15833 cls: 'modal-content',
15835 Roo.bootstrap.ComboBox.header,
15836 Roo.bootstrap.ComboBox.body,
15837 Roo.bootstrap.ComboBox.footer
15846 * Ext JS Library 1.1.1
15847 * Copyright(c) 2006-2007, Ext JS, LLC.
15849 * Originally Released Under LGPL - original licence link has changed is not relivant.
15852 * <script type="text/javascript">
15857 * @extends Roo.util.Observable
15858 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15859 * This class also supports single and multi selection modes. <br>
15860 * Create a data model bound view:
15862 var store = new Roo.data.Store(...);
15864 var view = new Roo.View({
15866 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15868 singleSelect: true,
15869 selectedClass: "ydataview-selected",
15873 // listen for node click?
15874 view.on("click", function(vw, index, node, e){
15875 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15879 dataModel.load("foobar.xml");
15881 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15883 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15884 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15886 * Note: old style constructor is still suported (container, template, config)
15889 * Create a new View
15890 * @param {Object} config The config object
15893 Roo.View = function(config, depreciated_tpl, depreciated_config){
15895 this.parent = false;
15897 if (typeof(depreciated_tpl) == 'undefined') {
15898 // new way.. - universal constructor.
15899 Roo.apply(this, config);
15900 this.el = Roo.get(this.el);
15903 this.el = Roo.get(config);
15904 this.tpl = depreciated_tpl;
15905 Roo.apply(this, depreciated_config);
15907 this.wrapEl = this.el.wrap().wrap();
15908 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15911 if(typeof(this.tpl) == "string"){
15912 this.tpl = new Roo.Template(this.tpl);
15914 // support xtype ctors..
15915 this.tpl = new Roo.factory(this.tpl, Roo);
15919 this.tpl.compile();
15924 * @event beforeclick
15925 * Fires before a click is processed. Returns false to cancel the default action.
15926 * @param {Roo.View} this
15927 * @param {Number} index The index of the target node
15928 * @param {HTMLElement} node The target node
15929 * @param {Roo.EventObject} e The raw event object
15931 "beforeclick" : true,
15934 * Fires when a template node is clicked.
15935 * @param {Roo.View} this
15936 * @param {Number} index The index of the target node
15937 * @param {HTMLElement} node The target node
15938 * @param {Roo.EventObject} e The raw event object
15943 * Fires when a template node is double clicked.
15944 * @param {Roo.View} this
15945 * @param {Number} index The index of the target node
15946 * @param {HTMLElement} node The target node
15947 * @param {Roo.EventObject} e The raw event object
15951 * @event contextmenu
15952 * Fires when a template node is right clicked.
15953 * @param {Roo.View} this
15954 * @param {Number} index The index of the target node
15955 * @param {HTMLElement} node The target node
15956 * @param {Roo.EventObject} e The raw event object
15958 "contextmenu" : true,
15960 * @event selectionchange
15961 * Fires when the selected nodes change.
15962 * @param {Roo.View} this
15963 * @param {Array} selections Array of the selected nodes
15965 "selectionchange" : true,
15968 * @event beforeselect
15969 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15970 * @param {Roo.View} this
15971 * @param {HTMLElement} node The node to be selected
15972 * @param {Array} selections Array of currently selected nodes
15974 "beforeselect" : true,
15976 * @event preparedata
15977 * Fires on every row to render, to allow you to change the data.
15978 * @param {Roo.View} this
15979 * @param {Object} data to be rendered (change this)
15981 "preparedata" : true
15989 "click": this.onClick,
15990 "dblclick": this.onDblClick,
15991 "contextmenu": this.onContextMenu,
15995 this.selections = [];
15997 this.cmp = new Roo.CompositeElementLite([]);
15999 this.store = Roo.factory(this.store, Roo.data);
16000 this.setStore(this.store, true);
16003 if ( this.footer && this.footer.xtype) {
16005 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16007 this.footer.dataSource = this.store;
16008 this.footer.container = fctr;
16009 this.footer = Roo.factory(this.footer, Roo);
16010 fctr.insertFirst(this.el);
16012 // this is a bit insane - as the paging toolbar seems to detach the el..
16013 // dom.parentNode.parentNode.parentNode
16014 // they get detached?
16018 Roo.View.superclass.constructor.call(this);
16023 Roo.extend(Roo.View, Roo.util.Observable, {
16026 * @cfg {Roo.data.Store} store Data store to load data from.
16031 * @cfg {String|Roo.Element} el The container element.
16036 * @cfg {String|Roo.Template} tpl The template used by this View
16040 * @cfg {String} dataName the named area of the template to use as the data area
16041 * Works with domtemplates roo-name="name"
16045 * @cfg {String} selectedClass The css class to add to selected nodes
16047 selectedClass : "x-view-selected",
16049 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16054 * @cfg {String} text to display on mask (default Loading)
16058 * @cfg {Boolean} multiSelect Allow multiple selection
16060 multiSelect : false,
16062 * @cfg {Boolean} singleSelect Allow single selection
16064 singleSelect: false,
16067 * @cfg {Boolean} toggleSelect - selecting
16069 toggleSelect : false,
16072 * @cfg {Boolean} tickable - selecting
16077 * Returns the element this view is bound to.
16078 * @return {Roo.Element}
16080 getEl : function(){
16081 return this.wrapEl;
16087 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16089 refresh : function(){
16090 //Roo.log('refresh');
16093 // if we are using something like 'domtemplate', then
16094 // the what gets used is:
16095 // t.applySubtemplate(NAME, data, wrapping data..)
16096 // the outer template then get' applied with
16097 // the store 'extra data'
16098 // and the body get's added to the
16099 // roo-name="data" node?
16100 // <span class='roo-tpl-{name}'></span> ?????
16104 this.clearSelections();
16105 this.el.update("");
16107 var records = this.store.getRange();
16108 if(records.length < 1) {
16110 // is this valid?? = should it render a template??
16112 this.el.update(this.emptyText);
16116 if (this.dataName) {
16117 this.el.update(t.apply(this.store.meta)); //????
16118 el = this.el.child('.roo-tpl-' + this.dataName);
16121 for(var i = 0, len = records.length; i < len; i++){
16122 var data = this.prepareData(records[i].data, i, records[i]);
16123 this.fireEvent("preparedata", this, data, i, records[i]);
16125 var d = Roo.apply({}, data);
16128 Roo.apply(d, {'roo-id' : Roo.id()});
16132 Roo.each(this.parent.item, function(item){
16133 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16136 Roo.apply(d, {'roo-data-checked' : 'checked'});
16140 html[html.length] = Roo.util.Format.trim(
16142 t.applySubtemplate(this.dataName, d, this.store.meta) :
16149 el.update(html.join(""));
16150 this.nodes = el.dom.childNodes;
16151 this.updateIndexes(0);
16156 * Function to override to reformat the data that is sent to
16157 * the template for each node.
16158 * DEPRICATED - use the preparedata event handler.
16159 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16160 * a JSON object for an UpdateManager bound view).
16162 prepareData : function(data, index, record)
16164 this.fireEvent("preparedata", this, data, index, record);
16168 onUpdate : function(ds, record){
16169 // Roo.log('on update');
16170 this.clearSelections();
16171 var index = this.store.indexOf(record);
16172 var n = this.nodes[index];
16173 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16174 n.parentNode.removeChild(n);
16175 this.updateIndexes(index, index);
16181 onAdd : function(ds, records, index)
16183 //Roo.log(['on Add', ds, records, index] );
16184 this.clearSelections();
16185 if(this.nodes.length == 0){
16189 var n = this.nodes[index];
16190 for(var i = 0, len = records.length; i < len; i++){
16191 var d = this.prepareData(records[i].data, i, records[i]);
16193 this.tpl.insertBefore(n, d);
16196 this.tpl.append(this.el, d);
16199 this.updateIndexes(index);
16202 onRemove : function(ds, record, index){
16203 // Roo.log('onRemove');
16204 this.clearSelections();
16205 var el = this.dataName ?
16206 this.el.child('.roo-tpl-' + this.dataName) :
16209 el.dom.removeChild(this.nodes[index]);
16210 this.updateIndexes(index);
16214 * Refresh an individual node.
16215 * @param {Number} index
16217 refreshNode : function(index){
16218 this.onUpdate(this.store, this.store.getAt(index));
16221 updateIndexes : function(startIndex, endIndex){
16222 var ns = this.nodes;
16223 startIndex = startIndex || 0;
16224 endIndex = endIndex || ns.length - 1;
16225 for(var i = startIndex; i <= endIndex; i++){
16226 ns[i].nodeIndex = i;
16231 * Changes the data store this view uses and refresh the view.
16232 * @param {Store} store
16234 setStore : function(store, initial){
16235 if(!initial && this.store){
16236 this.store.un("datachanged", this.refresh);
16237 this.store.un("add", this.onAdd);
16238 this.store.un("remove", this.onRemove);
16239 this.store.un("update", this.onUpdate);
16240 this.store.un("clear", this.refresh);
16241 this.store.un("beforeload", this.onBeforeLoad);
16242 this.store.un("load", this.onLoad);
16243 this.store.un("loadexception", this.onLoad);
16247 store.on("datachanged", this.refresh, this);
16248 store.on("add", this.onAdd, this);
16249 store.on("remove", this.onRemove, this);
16250 store.on("update", this.onUpdate, this);
16251 store.on("clear", this.refresh, this);
16252 store.on("beforeload", this.onBeforeLoad, this);
16253 store.on("load", this.onLoad, this);
16254 store.on("loadexception", this.onLoad, this);
16262 * onbeforeLoad - masks the loading area.
16265 onBeforeLoad : function(store,opts)
16267 //Roo.log('onBeforeLoad');
16269 this.el.update("");
16271 this.el.mask(this.mask ? this.mask : "Loading" );
16273 onLoad : function ()
16280 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16281 * @param {HTMLElement} node
16282 * @return {HTMLElement} The template node
16284 findItemFromChild : function(node){
16285 var el = this.dataName ?
16286 this.el.child('.roo-tpl-' + this.dataName,true) :
16289 if(!node || node.parentNode == el){
16292 var p = node.parentNode;
16293 while(p && p != el){
16294 if(p.parentNode == el){
16303 onClick : function(e){
16304 var item = this.findItemFromChild(e.getTarget());
16306 var index = this.indexOf(item);
16307 if(this.onItemClick(item, index, e) !== false){
16308 this.fireEvent("click", this, index, item, e);
16311 this.clearSelections();
16316 onContextMenu : function(e){
16317 var item = this.findItemFromChild(e.getTarget());
16319 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16324 onDblClick : function(e){
16325 var item = this.findItemFromChild(e.getTarget());
16327 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16331 onItemClick : function(item, index, e)
16333 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16336 if (this.toggleSelect) {
16337 var m = this.isSelected(item) ? 'unselect' : 'select';
16340 _t[m](item, true, false);
16343 if(this.multiSelect || this.singleSelect){
16344 if(this.multiSelect && e.shiftKey && this.lastSelection){
16345 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16347 this.select(item, this.multiSelect && e.ctrlKey);
16348 this.lastSelection = item;
16351 if(!this.tickable){
16352 e.preventDefault();
16360 * Get the number of selected nodes.
16363 getSelectionCount : function(){
16364 return this.selections.length;
16368 * Get the currently selected nodes.
16369 * @return {Array} An array of HTMLElements
16371 getSelectedNodes : function(){
16372 return this.selections;
16376 * Get the indexes of the selected nodes.
16379 getSelectedIndexes : function(){
16380 var indexes = [], s = this.selections;
16381 for(var i = 0, len = s.length; i < len; i++){
16382 indexes.push(s[i].nodeIndex);
16388 * Clear all selections
16389 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16391 clearSelections : function(suppressEvent){
16392 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16393 this.cmp.elements = this.selections;
16394 this.cmp.removeClass(this.selectedClass);
16395 this.selections = [];
16396 if(!suppressEvent){
16397 this.fireEvent("selectionchange", this, this.selections);
16403 * Returns true if the passed node is selected
16404 * @param {HTMLElement/Number} node The node or node index
16405 * @return {Boolean}
16407 isSelected : function(node){
16408 var s = this.selections;
16412 node = this.getNode(node);
16413 return s.indexOf(node) !== -1;
16418 * @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
16419 * @param {Boolean} keepExisting (optional) true to keep existing selections
16420 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16422 select : function(nodeInfo, keepExisting, suppressEvent){
16423 if(nodeInfo instanceof Array){
16425 this.clearSelections(true);
16427 for(var i = 0, len = nodeInfo.length; i < len; i++){
16428 this.select(nodeInfo[i], true, true);
16432 var node = this.getNode(nodeInfo);
16433 if(!node || this.isSelected(node)){
16434 return; // already selected.
16437 this.clearSelections(true);
16440 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16441 Roo.fly(node).addClass(this.selectedClass);
16442 this.selections.push(node);
16443 if(!suppressEvent){
16444 this.fireEvent("selectionchange", this, this.selections);
16452 * @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
16453 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16454 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16456 unselect : function(nodeInfo, keepExisting, suppressEvent)
16458 if(nodeInfo instanceof Array){
16459 Roo.each(this.selections, function(s) {
16460 this.unselect(s, nodeInfo);
16464 var node = this.getNode(nodeInfo);
16465 if(!node || !this.isSelected(node)){
16466 //Roo.log("not selected");
16467 return; // not selected.
16471 Roo.each(this.selections, function(s) {
16473 Roo.fly(node).removeClass(this.selectedClass);
16480 this.selections= ns;
16481 this.fireEvent("selectionchange", this, this.selections);
16485 * Gets a template node.
16486 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16487 * @return {HTMLElement} The node or null if it wasn't found
16489 getNode : function(nodeInfo){
16490 if(typeof nodeInfo == "string"){
16491 return document.getElementById(nodeInfo);
16492 }else if(typeof nodeInfo == "number"){
16493 return this.nodes[nodeInfo];
16499 * Gets a range template nodes.
16500 * @param {Number} startIndex
16501 * @param {Number} endIndex
16502 * @return {Array} An array of nodes
16504 getNodes : function(start, end){
16505 var ns = this.nodes;
16506 start = start || 0;
16507 end = typeof end == "undefined" ? ns.length - 1 : end;
16510 for(var i = start; i <= end; i++){
16514 for(var i = start; i >= end; i--){
16522 * Finds the index of the passed node
16523 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16524 * @return {Number} The index of the node or -1
16526 indexOf : function(node){
16527 node = this.getNode(node);
16528 if(typeof node.nodeIndex == "number"){
16529 return node.nodeIndex;
16531 var ns = this.nodes;
16532 for(var i = 0, len = ns.length; i < len; i++){
16543 * based on jquery fullcalendar
16547 Roo.bootstrap = Roo.bootstrap || {};
16549 * @class Roo.bootstrap.Calendar
16550 * @extends Roo.bootstrap.Component
16551 * Bootstrap Calendar class
16552 * @cfg {Boolean} loadMask (true|false) default false
16553 * @cfg {Object} header generate the user specific header of the calendar, default false
16556 * Create a new Container
16557 * @param {Object} config The config object
16562 Roo.bootstrap.Calendar = function(config){
16563 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16567 * Fires when a date is selected
16568 * @param {DatePicker} this
16569 * @param {Date} date The selected date
16573 * @event monthchange
16574 * Fires when the displayed month changes
16575 * @param {DatePicker} this
16576 * @param {Date} date The selected month
16578 'monthchange': true,
16580 * @event evententer
16581 * Fires when mouse over an event
16582 * @param {Calendar} this
16583 * @param {event} Event
16585 'evententer': true,
16587 * @event eventleave
16588 * Fires when the mouse leaves an
16589 * @param {Calendar} this
16592 'eventleave': true,
16594 * @event eventclick
16595 * Fires when the mouse click an
16596 * @param {Calendar} this
16605 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16608 * @cfg {Number} startDay
16609 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16617 getAutoCreate : function(){
16620 var fc_button = function(name, corner, style, content ) {
16621 return Roo.apply({},{
16623 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16625 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16628 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16639 style : 'width:100%',
16646 cls : 'fc-header-left',
16648 fc_button('prev', 'left', 'arrow', '‹' ),
16649 fc_button('next', 'right', 'arrow', '›' ),
16650 { tag: 'span', cls: 'fc-header-space' },
16651 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16659 cls : 'fc-header-center',
16663 cls: 'fc-header-title',
16666 html : 'month / year'
16674 cls : 'fc-header-right',
16676 /* fc_button('month', 'left', '', 'month' ),
16677 fc_button('week', '', '', 'week' ),
16678 fc_button('day', 'right', '', 'day' )
16690 header = this.header;
16693 var cal_heads = function() {
16695 // fixme - handle this.
16697 for (var i =0; i < Date.dayNames.length; i++) {
16698 var d = Date.dayNames[i];
16701 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16702 html : d.substring(0,3)
16706 ret[0].cls += ' fc-first';
16707 ret[6].cls += ' fc-last';
16710 var cal_cell = function(n) {
16713 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16718 cls: 'fc-day-number',
16722 cls: 'fc-day-content',
16726 style: 'position: relative;' // height: 17px;
16738 var cal_rows = function() {
16741 for (var r = 0; r < 6; r++) {
16748 for (var i =0; i < Date.dayNames.length; i++) {
16749 var d = Date.dayNames[i];
16750 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16753 row.cn[0].cls+=' fc-first';
16754 row.cn[0].cn[0].style = 'min-height:90px';
16755 row.cn[6].cls+=' fc-last';
16759 ret[0].cls += ' fc-first';
16760 ret[4].cls += ' fc-prev-last';
16761 ret[5].cls += ' fc-last';
16768 cls: 'fc-border-separate',
16769 style : 'width:100%',
16777 cls : 'fc-first fc-last',
16795 cls : 'fc-content',
16796 style : "position: relative;",
16799 cls : 'fc-view fc-view-month fc-grid',
16800 style : 'position: relative',
16801 unselectable : 'on',
16804 cls : 'fc-event-container',
16805 style : 'position:absolute;z-index:8;top:0;left:0;'
16823 initEvents : function()
16826 throw "can not find store for calendar";
16832 style: "text-align:center",
16836 style: "background-color:white;width:50%;margin:250 auto",
16840 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16851 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16853 var size = this.el.select('.fc-content', true).first().getSize();
16854 this.maskEl.setSize(size.width, size.height);
16855 this.maskEl.enableDisplayMode("block");
16856 if(!this.loadMask){
16857 this.maskEl.hide();
16860 this.store = Roo.factory(this.store, Roo.data);
16861 this.store.on('load', this.onLoad, this);
16862 this.store.on('beforeload', this.onBeforeLoad, this);
16866 this.cells = this.el.select('.fc-day',true);
16867 //Roo.log(this.cells);
16868 this.textNodes = this.el.query('.fc-day-number');
16869 this.cells.addClassOnOver('fc-state-hover');
16871 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16872 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16873 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16874 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16876 this.on('monthchange', this.onMonthChange, this);
16878 this.update(new Date().clearTime());
16881 resize : function() {
16882 var sz = this.el.getSize();
16884 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16885 this.el.select('.fc-day-content div',true).setHeight(34);
16890 showPrevMonth : function(e){
16891 this.update(this.activeDate.add("mo", -1));
16893 showToday : function(e){
16894 this.update(new Date().clearTime());
16897 showNextMonth : function(e){
16898 this.update(this.activeDate.add("mo", 1));
16902 showPrevYear : function(){
16903 this.update(this.activeDate.add("y", -1));
16907 showNextYear : function(){
16908 this.update(this.activeDate.add("y", 1));
16913 update : function(date)
16915 var vd = this.activeDate;
16916 this.activeDate = date;
16917 // if(vd && this.el){
16918 // var t = date.getTime();
16919 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16920 // Roo.log('using add remove');
16922 // this.fireEvent('monthchange', this, date);
16924 // this.cells.removeClass("fc-state-highlight");
16925 // this.cells.each(function(c){
16926 // if(c.dateValue == t){
16927 // c.addClass("fc-state-highlight");
16928 // setTimeout(function(){
16929 // try{c.dom.firstChild.focus();}catch(e){}
16939 var days = date.getDaysInMonth();
16941 var firstOfMonth = date.getFirstDateOfMonth();
16942 var startingPos = firstOfMonth.getDay()-this.startDay;
16944 if(startingPos < this.startDay){
16948 var pm = date.add(Date.MONTH, -1);
16949 var prevStart = pm.getDaysInMonth()-startingPos;
16951 this.cells = this.el.select('.fc-day',true);
16952 this.textNodes = this.el.query('.fc-day-number');
16953 this.cells.addClassOnOver('fc-state-hover');
16955 var cells = this.cells.elements;
16956 var textEls = this.textNodes;
16958 Roo.each(cells, function(cell){
16959 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16962 days += startingPos;
16964 // convert everything to numbers so it's fast
16965 var day = 86400000;
16966 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16969 //Roo.log(prevStart);
16971 var today = new Date().clearTime().getTime();
16972 var sel = date.clearTime().getTime();
16973 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16974 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16975 var ddMatch = this.disabledDatesRE;
16976 var ddText = this.disabledDatesText;
16977 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16978 var ddaysText = this.disabledDaysText;
16979 var format = this.format;
16981 var setCellClass = function(cal, cell){
16985 //Roo.log('set Cell Class');
16987 var t = d.getTime();
16991 cell.dateValue = t;
16993 cell.className += " fc-today";
16994 cell.className += " fc-state-highlight";
16995 cell.title = cal.todayText;
16998 // disable highlight in other month..
16999 //cell.className += " fc-state-highlight";
17004 cell.className = " fc-state-disabled";
17005 cell.title = cal.minText;
17009 cell.className = " fc-state-disabled";
17010 cell.title = cal.maxText;
17014 if(ddays.indexOf(d.getDay()) != -1){
17015 cell.title = ddaysText;
17016 cell.className = " fc-state-disabled";
17019 if(ddMatch && format){
17020 var fvalue = d.dateFormat(format);
17021 if(ddMatch.test(fvalue)){
17022 cell.title = ddText.replace("%0", fvalue);
17023 cell.className = " fc-state-disabled";
17027 if (!cell.initialClassName) {
17028 cell.initialClassName = cell.dom.className;
17031 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17036 for(; i < startingPos; i++) {
17037 textEls[i].innerHTML = (++prevStart);
17038 d.setDate(d.getDate()+1);
17040 cells[i].className = "fc-past fc-other-month";
17041 setCellClass(this, cells[i]);
17046 for(; i < days; i++){
17047 intDay = i - startingPos + 1;
17048 textEls[i].innerHTML = (intDay);
17049 d.setDate(d.getDate()+1);
17051 cells[i].className = ''; // "x-date-active";
17052 setCellClass(this, cells[i]);
17056 for(; i < 42; i++) {
17057 textEls[i].innerHTML = (++extraDays);
17058 d.setDate(d.getDate()+1);
17060 cells[i].className = "fc-future fc-other-month";
17061 setCellClass(this, cells[i]);
17064 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17066 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17068 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17069 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17071 if(totalRows != 6){
17072 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17073 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17076 this.fireEvent('monthchange', this, date);
17080 if(!this.internalRender){
17081 var main = this.el.dom.firstChild;
17082 var w = main.offsetWidth;
17083 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17084 Roo.fly(main).setWidth(w);
17085 this.internalRender = true;
17086 // opera does not respect the auto grow header center column
17087 // then, after it gets a width opera refuses to recalculate
17088 // without a second pass
17089 if(Roo.isOpera && !this.secondPass){
17090 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17091 this.secondPass = true;
17092 this.update.defer(10, this, [date]);
17099 findCell : function(dt) {
17100 dt = dt.clearTime().getTime();
17102 this.cells.each(function(c){
17103 //Roo.log("check " +c.dateValue + '?=' + dt);
17104 if(c.dateValue == dt){
17114 findCells : function(ev) {
17115 var s = ev.start.clone().clearTime().getTime();
17117 var e= ev.end.clone().clearTime().getTime();
17120 this.cells.each(function(c){
17121 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17123 if(c.dateValue > e){
17126 if(c.dateValue < s){
17135 // findBestRow: function(cells)
17139 // for (var i =0 ; i < cells.length;i++) {
17140 // ret = Math.max(cells[i].rows || 0,ret);
17147 addItem : function(ev)
17149 // look for vertical location slot in
17150 var cells = this.findCells(ev);
17152 // ev.row = this.findBestRow(cells);
17154 // work out the location.
17158 for(var i =0; i < cells.length; i++) {
17160 cells[i].row = cells[0].row;
17163 cells[i].row = cells[i].row + 1;
17173 if (crow.start.getY() == cells[i].getY()) {
17175 crow.end = cells[i];
17192 cells[0].events.push(ev);
17194 this.calevents.push(ev);
17197 clearEvents: function() {
17199 if(!this.calevents){
17203 Roo.each(this.cells.elements, function(c){
17209 Roo.each(this.calevents, function(e) {
17210 Roo.each(e.els, function(el) {
17211 el.un('mouseenter' ,this.onEventEnter, this);
17212 el.un('mouseleave' ,this.onEventLeave, this);
17217 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17223 renderEvents: function()
17227 this.cells.each(function(c) {
17236 if(c.row != c.events.length){
17237 r = 4 - (4 - (c.row - c.events.length));
17240 c.events = ev.slice(0, r);
17241 c.more = ev.slice(r);
17243 if(c.more.length && c.more.length == 1){
17244 c.events.push(c.more.pop());
17247 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17251 this.cells.each(function(c) {
17253 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17256 for (var e = 0; e < c.events.length; e++){
17257 var ev = c.events[e];
17258 var rows = ev.rows;
17260 for(var i = 0; i < rows.length; i++) {
17262 // how many rows should it span..
17265 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17266 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17268 unselectable : "on",
17271 cls: 'fc-event-inner',
17275 // cls: 'fc-event-time',
17276 // html : cells.length > 1 ? '' : ev.time
17280 cls: 'fc-event-title',
17281 html : String.format('{0}', ev.title)
17288 cls: 'ui-resizable-handle ui-resizable-e',
17289 html : '  '
17296 cfg.cls += ' fc-event-start';
17298 if ((i+1) == rows.length) {
17299 cfg.cls += ' fc-event-end';
17302 var ctr = _this.el.select('.fc-event-container',true).first();
17303 var cg = ctr.createChild(cfg);
17305 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17306 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17308 var r = (c.more.length) ? 1 : 0;
17309 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17310 cg.setWidth(ebox.right - sbox.x -2);
17312 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17313 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17314 cg.on('click', _this.onEventClick, _this, ev);
17325 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17326 style : 'position: absolute',
17327 unselectable : "on",
17330 cls: 'fc-event-inner',
17334 cls: 'fc-event-title',
17342 cls: 'ui-resizable-handle ui-resizable-e',
17343 html : '  '
17349 var ctr = _this.el.select('.fc-event-container',true).first();
17350 var cg = ctr.createChild(cfg);
17352 var sbox = c.select('.fc-day-content',true).first().getBox();
17353 var ebox = c.select('.fc-day-content',true).first().getBox();
17355 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17356 cg.setWidth(ebox.right - sbox.x -2);
17358 cg.on('click', _this.onMoreEventClick, _this, c.more);
17368 onEventEnter: function (e, el,event,d) {
17369 this.fireEvent('evententer', this, el, event);
17372 onEventLeave: function (e, el,event,d) {
17373 this.fireEvent('eventleave', this, el, event);
17376 onEventClick: function (e, el,event,d) {
17377 this.fireEvent('eventclick', this, el, event);
17380 onMonthChange: function () {
17384 onMoreEventClick: function(e, el, more)
17388 this.calpopover.placement = 'right';
17389 this.calpopover.setTitle('More');
17391 this.calpopover.setContent('');
17393 var ctr = this.calpopover.el.select('.popover-content', true).first();
17395 Roo.each(more, function(m){
17397 cls : 'fc-event-hori fc-event-draggable',
17400 var cg = ctr.createChild(cfg);
17402 cg.on('click', _this.onEventClick, _this, m);
17405 this.calpopover.show(el);
17410 onLoad: function ()
17412 this.calevents = [];
17415 if(this.store.getCount() > 0){
17416 this.store.data.each(function(d){
17419 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17420 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17421 time : d.data.start_time,
17422 title : d.data.title,
17423 description : d.data.description,
17424 venue : d.data.venue
17429 this.renderEvents();
17431 if(this.calevents.length && this.loadMask){
17432 this.maskEl.hide();
17436 onBeforeLoad: function()
17438 this.clearEvents();
17440 this.maskEl.show();
17454 * @class Roo.bootstrap.Popover
17455 * @extends Roo.bootstrap.Component
17456 * Bootstrap Popover class
17457 * @cfg {String} html contents of the popover (or false to use children..)
17458 * @cfg {String} title of popover (or false to hide)
17459 * @cfg {String} placement how it is placed
17460 * @cfg {String} trigger click || hover (or false to trigger manually)
17461 * @cfg {String} over what (parent or false to trigger manually.)
17462 * @cfg {Number} delay - delay before showing
17465 * Create a new Popover
17466 * @param {Object} config The config object
17469 Roo.bootstrap.Popover = function(config){
17470 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17476 * After the popover show
17478 * @param {Roo.bootstrap.Popover} this
17483 * After the popover hide
17485 * @param {Roo.bootstrap.Popover} this
17491 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17493 title: 'Fill in a title',
17496 placement : 'right',
17497 trigger : 'hover', // hover
17503 can_build_overlaid : false,
17505 getChildContainer : function()
17507 return this.el.select('.popover-content',true).first();
17510 getAutoCreate : function(){
17513 cls : 'popover roo-dynamic',
17514 style: 'display:block',
17520 cls : 'popover-inner',
17524 cls: 'popover-title',
17528 cls : 'popover-content',
17539 setTitle: function(str)
17542 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17544 setContent: function(str)
17547 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17549 // as it get's added to the bottom of the page.
17550 onRender : function(ct, position)
17552 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17554 var cfg = Roo.apply({}, this.getAutoCreate());
17558 cfg.cls += ' ' + this.cls;
17561 cfg.style = this.style;
17563 //Roo.log("adding to ");
17564 this.el = Roo.get(document.body).createChild(cfg, position);
17565 // Roo.log(this.el);
17570 initEvents : function()
17572 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17573 this.el.enableDisplayMode('block');
17575 if (this.over === false) {
17578 if (this.triggers === false) {
17581 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17582 var triggers = this.trigger ? this.trigger.split(' ') : [];
17583 Roo.each(triggers, function(trigger) {
17585 if (trigger == 'click') {
17586 on_el.on('click', this.toggle, this);
17587 } else if (trigger != 'manual') {
17588 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17589 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17591 on_el.on(eventIn ,this.enter, this);
17592 on_el.on(eventOut, this.leave, this);
17603 toggle : function () {
17604 this.hoverState == 'in' ? this.leave() : this.enter();
17607 enter : function () {
17609 clearTimeout(this.timeout);
17611 this.hoverState = 'in';
17613 if (!this.delay || !this.delay.show) {
17618 this.timeout = setTimeout(function () {
17619 if (_t.hoverState == 'in') {
17622 }, this.delay.show)
17625 leave : function() {
17626 clearTimeout(this.timeout);
17628 this.hoverState = 'out';
17630 if (!this.delay || !this.delay.hide) {
17635 this.timeout = setTimeout(function () {
17636 if (_t.hoverState == 'out') {
17639 }, this.delay.hide)
17642 show : function (on_el)
17645 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17649 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17650 if (this.html !== false) {
17651 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17653 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17654 if (!this.title.length) {
17655 this.el.select('.popover-title',true).hide();
17658 var placement = typeof this.placement == 'function' ?
17659 this.placement.call(this, this.el, on_el) :
17662 var autoToken = /\s?auto?\s?/i;
17663 var autoPlace = autoToken.test(placement);
17665 placement = placement.replace(autoToken, '') || 'top';
17669 //this.el.setXY([0,0]);
17671 this.el.dom.style.display='block';
17672 this.el.addClass(placement);
17674 //this.el.appendTo(on_el);
17676 var p = this.getPosition();
17677 var box = this.el.getBox();
17682 var align = Roo.bootstrap.Popover.alignment[placement];
17685 this.el.alignTo(on_el, align[0],align[1]);
17686 //var arrow = this.el.select('.arrow',true).first();
17687 //arrow.set(align[2],
17689 this.el.addClass('in');
17692 if (this.el.hasClass('fade')) {
17696 this.hoverState = 'in';
17698 this.fireEvent('show', this);
17703 this.el.setXY([0,0]);
17704 this.el.removeClass('in');
17706 this.hoverState = null;
17708 this.fireEvent('hide', this);
17713 Roo.bootstrap.Popover.alignment = {
17714 'left' : ['r-l', [-10,0], 'right'],
17715 'right' : ['l-r', [10,0], 'left'],
17716 'bottom' : ['t-b', [0,10], 'top'],
17717 'top' : [ 'b-t', [0,-10], 'bottom']
17728 * @class Roo.bootstrap.Progress
17729 * @extends Roo.bootstrap.Component
17730 * Bootstrap Progress class
17731 * @cfg {Boolean} striped striped of the progress bar
17732 * @cfg {Boolean} active animated of the progress bar
17736 * Create a new Progress
17737 * @param {Object} config The config object
17740 Roo.bootstrap.Progress = function(config){
17741 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17744 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17749 getAutoCreate : function(){
17757 cfg.cls += ' progress-striped';
17761 cfg.cls += ' active';
17780 * @class Roo.bootstrap.ProgressBar
17781 * @extends Roo.bootstrap.Component
17782 * Bootstrap ProgressBar class
17783 * @cfg {Number} aria_valuenow aria-value now
17784 * @cfg {Number} aria_valuemin aria-value min
17785 * @cfg {Number} aria_valuemax aria-value max
17786 * @cfg {String} label label for the progress bar
17787 * @cfg {String} panel (success | info | warning | danger )
17788 * @cfg {String} role role of the progress bar
17789 * @cfg {String} sr_only text
17793 * Create a new ProgressBar
17794 * @param {Object} config The config object
17797 Roo.bootstrap.ProgressBar = function(config){
17798 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17801 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17805 aria_valuemax : 100,
17811 getAutoCreate : function()
17816 cls: 'progress-bar',
17817 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17829 cfg.role = this.role;
17832 if(this.aria_valuenow){
17833 cfg['aria-valuenow'] = this.aria_valuenow;
17836 if(this.aria_valuemin){
17837 cfg['aria-valuemin'] = this.aria_valuemin;
17840 if(this.aria_valuemax){
17841 cfg['aria-valuemax'] = this.aria_valuemax;
17844 if(this.label && !this.sr_only){
17845 cfg.html = this.label;
17849 cfg.cls += ' progress-bar-' + this.panel;
17855 update : function(aria_valuenow)
17857 this.aria_valuenow = aria_valuenow;
17859 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17874 * @class Roo.bootstrap.TabGroup
17875 * @extends Roo.bootstrap.Column
17876 * Bootstrap Column class
17877 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17878 * @cfg {Boolean} carousel true to make the group behave like a carousel
17879 * @cfg {Boolean} bullets show bullets for the panels
17880 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17881 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17882 * @cfg {Boolean} showarrow (true|false) show arrow default true
17885 * Create a new TabGroup
17886 * @param {Object} config The config object
17889 Roo.bootstrap.TabGroup = function(config){
17890 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17892 this.navId = Roo.id();
17895 Roo.bootstrap.TabGroup.register(this);
17899 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17902 transition : false,
17907 slideOnTouch : false,
17910 getAutoCreate : function()
17912 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17914 cfg.cls += ' tab-content';
17916 if (this.carousel) {
17917 cfg.cls += ' carousel slide';
17920 cls : 'carousel-inner',
17924 if(this.bullets && !Roo.isTouch){
17927 cls : 'carousel-bullets',
17931 if(this.bullets_cls){
17932 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17939 cfg.cn[0].cn.push(bullets);
17942 if(this.showarrow){
17943 cfg.cn[0].cn.push({
17945 class : 'carousel-arrow',
17949 class : 'carousel-prev',
17953 class : 'fa fa-chevron-left'
17959 class : 'carousel-next',
17963 class : 'fa fa-chevron-right'
17976 initEvents: function()
17978 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17979 // this.el.on("touchstart", this.onTouchStart, this);
17982 if(this.autoslide){
17985 this.slideFn = window.setInterval(function() {
17986 _this.showPanelNext();
17990 if(this.showarrow){
17991 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17992 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17998 // onTouchStart : function(e, el, o)
18000 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18004 // this.showPanelNext();
18008 getChildContainer : function()
18010 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18014 * register a Navigation item
18015 * @param {Roo.bootstrap.NavItem} the navitem to add
18017 register : function(item)
18019 this.tabs.push( item);
18020 item.navId = this.navId; // not really needed..
18025 getActivePanel : function()
18028 Roo.each(this.tabs, function(t) {
18038 getPanelByName : function(n)
18041 Roo.each(this.tabs, function(t) {
18042 if (t.tabId == n) {
18050 indexOfPanel : function(p)
18053 Roo.each(this.tabs, function(t,i) {
18054 if (t.tabId == p.tabId) {
18063 * show a specific panel
18064 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18065 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18067 showPanel : function (pan)
18069 if(this.transition || typeof(pan) == 'undefined'){
18070 Roo.log("waiting for the transitionend");
18074 if (typeof(pan) == 'number') {
18075 pan = this.tabs[pan];
18078 if (typeof(pan) == 'string') {
18079 pan = this.getPanelByName(pan);
18082 var cur = this.getActivePanel();
18085 Roo.log('pan or acitve pan is undefined');
18089 if (pan.tabId == this.getActivePanel().tabId) {
18093 if (false === cur.fireEvent('beforedeactivate')) {
18097 if(this.bullets > 0 && !Roo.isTouch){
18098 this.setActiveBullet(this.indexOfPanel(pan));
18101 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18103 this.transition = true;
18104 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18105 var lr = dir == 'next' ? 'left' : 'right';
18106 pan.el.addClass(dir); // or prev
18107 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18108 cur.el.addClass(lr); // or right
18109 pan.el.addClass(lr);
18112 cur.el.on('transitionend', function() {
18113 Roo.log("trans end?");
18115 pan.el.removeClass([lr,dir]);
18116 pan.setActive(true);
18118 cur.el.removeClass([lr]);
18119 cur.setActive(false);
18121 _this.transition = false;
18123 }, this, { single: true } );
18128 cur.setActive(false);
18129 pan.setActive(true);
18134 showPanelNext : function()
18136 var i = this.indexOfPanel(this.getActivePanel());
18138 if (i >= this.tabs.length - 1 && !this.autoslide) {
18142 if (i >= this.tabs.length - 1 && this.autoslide) {
18146 this.showPanel(this.tabs[i+1]);
18149 showPanelPrev : function()
18151 var i = this.indexOfPanel(this.getActivePanel());
18153 if (i < 1 && !this.autoslide) {
18157 if (i < 1 && this.autoslide) {
18158 i = this.tabs.length;
18161 this.showPanel(this.tabs[i-1]);
18165 addBullet: function()
18167 if(!this.bullets || Roo.isTouch){
18170 var ctr = this.el.select('.carousel-bullets',true).first();
18171 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18172 var bullet = ctr.createChild({
18173 cls : 'bullet bullet-' + i
18174 },ctr.dom.lastChild);
18179 bullet.on('click', (function(e, el, o, ii, t){
18181 e.preventDefault();
18183 this.showPanel(ii);
18185 if(this.autoslide && this.slideFn){
18186 clearInterval(this.slideFn);
18187 this.slideFn = window.setInterval(function() {
18188 _this.showPanelNext();
18192 }).createDelegate(this, [i, bullet], true));
18197 setActiveBullet : function(i)
18203 Roo.each(this.el.select('.bullet', true).elements, function(el){
18204 el.removeClass('selected');
18207 var bullet = this.el.select('.bullet-' + i, true).first();
18213 bullet.addClass('selected');
18224 Roo.apply(Roo.bootstrap.TabGroup, {
18228 * register a Navigation Group
18229 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18231 register : function(navgrp)
18233 this.groups[navgrp.navId] = navgrp;
18237 * fetch a Navigation Group based on the navigation ID
18238 * if one does not exist , it will get created.
18239 * @param {string} the navgroup to add
18240 * @returns {Roo.bootstrap.NavGroup} the navgroup
18242 get: function(navId) {
18243 if (typeof(this.groups[navId]) == 'undefined') {
18244 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18246 return this.groups[navId] ;
18261 * @class Roo.bootstrap.TabPanel
18262 * @extends Roo.bootstrap.Component
18263 * Bootstrap TabPanel class
18264 * @cfg {Boolean} active panel active
18265 * @cfg {String} html panel content
18266 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18267 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18268 * @cfg {String} href click to link..
18272 * Create a new TabPanel
18273 * @param {Object} config The config object
18276 Roo.bootstrap.TabPanel = function(config){
18277 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18281 * Fires when the active status changes
18282 * @param {Roo.bootstrap.TabPanel} this
18283 * @param {Boolean} state the new state
18288 * @event beforedeactivate
18289 * Fires before a tab is de-activated - can be used to do validation on a form.
18290 * @param {Roo.bootstrap.TabPanel} this
18291 * @return {Boolean} false if there is an error
18294 'beforedeactivate': true
18297 this.tabId = this.tabId || Roo.id();
18301 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18309 getAutoCreate : function(){
18312 // item is needed for carousel - not sure if it has any effect otherwise
18313 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18314 html: this.html || ''
18318 cfg.cls += ' active';
18322 cfg.tabId = this.tabId;
18329 initEvents: function()
18331 var p = this.parent();
18333 this.navId = this.navId || p.navId;
18335 if (typeof(this.navId) != 'undefined') {
18336 // not really needed.. but just in case.. parent should be a NavGroup.
18337 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18341 var i = tg.tabs.length - 1;
18343 if(this.active && tg.bullets > 0 && i < tg.bullets){
18344 tg.setActiveBullet(i);
18348 this.el.on('click', this.onClick, this);
18351 this.el.on("touchstart", this.onTouchStart, this);
18352 this.el.on("touchmove", this.onTouchMove, this);
18353 this.el.on("touchend", this.onTouchEnd, this);
18358 onRender : function(ct, position)
18360 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18363 setActive : function(state)
18365 Roo.log("panel - set active " + this.tabId + "=" + state);
18367 this.active = state;
18369 this.el.removeClass('active');
18371 } else if (!this.el.hasClass('active')) {
18372 this.el.addClass('active');
18375 this.fireEvent('changed', this, state);
18378 onClick : function(e)
18380 e.preventDefault();
18382 if(!this.href.length){
18386 window.location.href = this.href;
18395 onTouchStart : function(e)
18397 this.swiping = false;
18399 this.startX = e.browserEvent.touches[0].clientX;
18400 this.startY = e.browserEvent.touches[0].clientY;
18403 onTouchMove : function(e)
18405 this.swiping = true;
18407 this.endX = e.browserEvent.touches[0].clientX;
18408 this.endY = e.browserEvent.touches[0].clientY;
18411 onTouchEnd : function(e)
18418 var tabGroup = this.parent();
18420 if(this.endX > this.startX){ // swiping right
18421 tabGroup.showPanelPrev();
18425 if(this.startX > this.endX){ // swiping left
18426 tabGroup.showPanelNext();
18445 * @class Roo.bootstrap.DateField
18446 * @extends Roo.bootstrap.Input
18447 * Bootstrap DateField class
18448 * @cfg {Number} weekStart default 0
18449 * @cfg {String} viewMode default empty, (months|years)
18450 * @cfg {String} minViewMode default empty, (months|years)
18451 * @cfg {Number} startDate default -Infinity
18452 * @cfg {Number} endDate default Infinity
18453 * @cfg {Boolean} todayHighlight default false
18454 * @cfg {Boolean} todayBtn default false
18455 * @cfg {Boolean} calendarWeeks default false
18456 * @cfg {Object} daysOfWeekDisabled default empty
18457 * @cfg {Boolean} singleMode default false (true | false)
18459 * @cfg {Boolean} keyboardNavigation default true
18460 * @cfg {String} language default en
18463 * Create a new DateField
18464 * @param {Object} config The config object
18467 Roo.bootstrap.DateField = function(config){
18468 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18472 * Fires when this field show.
18473 * @param {Roo.bootstrap.DateField} this
18474 * @param {Mixed} date The date value
18479 * Fires when this field hide.
18480 * @param {Roo.bootstrap.DateField} this
18481 * @param {Mixed} date The date value
18486 * Fires when select a date.
18487 * @param {Roo.bootstrap.DateField} this
18488 * @param {Mixed} date The date value
18492 * @event beforeselect
18493 * Fires when before select a date.
18494 * @param {Roo.bootstrap.DateField} this
18495 * @param {Mixed} date The date value
18497 beforeselect : true
18501 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18504 * @cfg {String} format
18505 * The default date format string which can be overriden for localization support. The format must be
18506 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18510 * @cfg {String} altFormats
18511 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18512 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18514 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18522 todayHighlight : false,
18528 keyboardNavigation: true,
18530 calendarWeeks: false,
18532 startDate: -Infinity,
18536 daysOfWeekDisabled: [],
18540 singleMode : false,
18542 UTCDate: function()
18544 return new Date(Date.UTC.apply(Date, arguments));
18547 UTCToday: function()
18549 var today = new Date();
18550 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18553 getDate: function() {
18554 var d = this.getUTCDate();
18555 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18558 getUTCDate: function() {
18562 setDate: function(d) {
18563 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18566 setUTCDate: function(d) {
18568 this.setValue(this.formatDate(this.date));
18571 onRender: function(ct, position)
18574 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18576 this.language = this.language || 'en';
18577 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18578 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18580 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18581 this.format = this.format || 'm/d/y';
18582 this.isInline = false;
18583 this.isInput = true;
18584 this.component = this.el.select('.add-on', true).first() || false;
18585 this.component = (this.component && this.component.length === 0) ? false : this.component;
18586 this.hasInput = this.component && this.inputEl().length;
18588 if (typeof(this.minViewMode === 'string')) {
18589 switch (this.minViewMode) {
18591 this.minViewMode = 1;
18594 this.minViewMode = 2;
18597 this.minViewMode = 0;
18602 if (typeof(this.viewMode === 'string')) {
18603 switch (this.viewMode) {
18616 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18618 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18620 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18622 this.picker().on('mousedown', this.onMousedown, this);
18623 this.picker().on('click', this.onClick, this);
18625 this.picker().addClass('datepicker-dropdown');
18627 this.startViewMode = this.viewMode;
18629 if(this.singleMode){
18630 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18631 v.setVisibilityMode(Roo.Element.DISPLAY);
18635 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18636 v.setStyle('width', '189px');
18640 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18641 if(!this.calendarWeeks){
18646 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18647 v.attr('colspan', function(i, val){
18648 return parseInt(val) + 1;
18653 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18655 this.setStartDate(this.startDate);
18656 this.setEndDate(this.endDate);
18658 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18665 if(this.isInline) {
18670 picker : function()
18672 return this.pickerEl;
18673 // return this.el.select('.datepicker', true).first();
18676 fillDow: function()
18678 var dowCnt = this.weekStart;
18687 if(this.calendarWeeks){
18695 while (dowCnt < this.weekStart + 7) {
18699 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18703 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18706 fillMonths: function()
18709 var months = this.picker().select('>.datepicker-months td', true).first();
18711 months.dom.innerHTML = '';
18717 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18720 months.createChild(month);
18727 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;
18729 if (this.date < this.startDate) {
18730 this.viewDate = new Date(this.startDate);
18731 } else if (this.date > this.endDate) {
18732 this.viewDate = new Date(this.endDate);
18734 this.viewDate = new Date(this.date);
18742 var d = new Date(this.viewDate),
18743 year = d.getUTCFullYear(),
18744 month = d.getUTCMonth(),
18745 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18746 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18747 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18748 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18749 currentDate = this.date && this.date.valueOf(),
18750 today = this.UTCToday();
18752 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18754 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18756 // this.picker.select('>tfoot th.today').
18757 // .text(dates[this.language].today)
18758 // .toggle(this.todayBtn !== false);
18760 this.updateNavArrows();
18763 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18765 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18767 prevMonth.setUTCDate(day);
18769 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18771 var nextMonth = new Date(prevMonth);
18773 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18775 nextMonth = nextMonth.valueOf();
18777 var fillMonths = false;
18779 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18781 while(prevMonth.valueOf() <= nextMonth) {
18784 if (prevMonth.getUTCDay() === this.weekStart) {
18786 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18794 if(this.calendarWeeks){
18795 // ISO 8601: First week contains first thursday.
18796 // ISO also states week starts on Monday, but we can be more abstract here.
18798 // Start of current week: based on weekstart/current date
18799 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18800 // Thursday of this week
18801 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18802 // First Thursday of year, year from thursday
18803 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18804 // Calendar week: ms between thursdays, div ms per day, div 7 days
18805 calWeek = (th - yth) / 864e5 / 7 + 1;
18807 fillMonths.cn.push({
18815 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18817 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18820 if (this.todayHighlight &&
18821 prevMonth.getUTCFullYear() == today.getFullYear() &&
18822 prevMonth.getUTCMonth() == today.getMonth() &&
18823 prevMonth.getUTCDate() == today.getDate()) {
18824 clsName += ' today';
18827 if (currentDate && prevMonth.valueOf() === currentDate) {
18828 clsName += ' active';
18831 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18832 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18833 clsName += ' disabled';
18836 fillMonths.cn.push({
18838 cls: 'day ' + clsName,
18839 html: prevMonth.getDate()
18842 prevMonth.setDate(prevMonth.getDate()+1);
18845 var currentYear = this.date && this.date.getUTCFullYear();
18846 var currentMonth = this.date && this.date.getUTCMonth();
18848 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18850 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18851 v.removeClass('active');
18853 if(currentYear === year && k === currentMonth){
18854 v.addClass('active');
18857 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18858 v.addClass('disabled');
18864 year = parseInt(year/10, 10) * 10;
18866 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18868 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18871 for (var i = -1; i < 11; i++) {
18872 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18874 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18882 showMode: function(dir)
18885 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18888 Roo.each(this.picker().select('>div',true).elements, function(v){
18889 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18892 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18897 if(this.isInline) {
18901 this.picker().removeClass(['bottom', 'top']);
18903 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18905 * place to the top of element!
18909 this.picker().addClass('top');
18910 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18915 this.picker().addClass('bottom');
18917 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18920 parseDate : function(value)
18922 if(!value || value instanceof Date){
18925 var v = Date.parseDate(value, this.format);
18926 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18927 v = Date.parseDate(value, 'Y-m-d');
18929 if(!v && this.altFormats){
18930 if(!this.altFormatsArray){
18931 this.altFormatsArray = this.altFormats.split("|");
18933 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18934 v = Date.parseDate(value, this.altFormatsArray[i]);
18940 formatDate : function(date, fmt)
18942 return (!date || !(date instanceof Date)) ?
18943 date : date.dateFormat(fmt || this.format);
18946 onFocus : function()
18948 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18952 onBlur : function()
18954 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18956 var d = this.inputEl().getValue();
18963 showPopup : function()
18965 this.picker().show();
18969 this.fireEvent('showpopup', this, this.date);
18972 hidePopup : function()
18974 if(this.isInline) {
18977 this.picker().hide();
18978 this.viewMode = this.startViewMode;
18981 this.fireEvent('hidepopup', this, this.date);
18985 onMousedown: function(e)
18987 e.stopPropagation();
18988 e.preventDefault();
18993 Roo.bootstrap.DateField.superclass.keyup.call(this);
18997 setValue: function(v)
18999 if(this.fireEvent('beforeselect', this, v) !== false){
19000 var d = new Date(this.parseDate(v) ).clearTime();
19002 if(isNaN(d.getTime())){
19003 this.date = this.viewDate = '';
19004 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19008 v = this.formatDate(d);
19010 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19012 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19016 this.fireEvent('select', this, this.date);
19020 getValue: function()
19022 return this.formatDate(this.date);
19025 fireKey: function(e)
19027 if (!this.picker().isVisible()){
19028 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19034 var dateChanged = false,
19036 newDate, newViewDate;
19041 e.preventDefault();
19045 if (!this.keyboardNavigation) {
19048 dir = e.keyCode == 37 ? -1 : 1;
19051 newDate = this.moveYear(this.date, dir);
19052 newViewDate = this.moveYear(this.viewDate, dir);
19053 } else if (e.shiftKey){
19054 newDate = this.moveMonth(this.date, dir);
19055 newViewDate = this.moveMonth(this.viewDate, dir);
19057 newDate = new Date(this.date);
19058 newDate.setUTCDate(this.date.getUTCDate() + dir);
19059 newViewDate = new Date(this.viewDate);
19060 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19062 if (this.dateWithinRange(newDate)){
19063 this.date = newDate;
19064 this.viewDate = newViewDate;
19065 this.setValue(this.formatDate(this.date));
19067 e.preventDefault();
19068 dateChanged = true;
19073 if (!this.keyboardNavigation) {
19076 dir = e.keyCode == 38 ? -1 : 1;
19078 newDate = this.moveYear(this.date, dir);
19079 newViewDate = this.moveYear(this.viewDate, dir);
19080 } else if (e.shiftKey){
19081 newDate = this.moveMonth(this.date, dir);
19082 newViewDate = this.moveMonth(this.viewDate, dir);
19084 newDate = new Date(this.date);
19085 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19086 newViewDate = new Date(this.viewDate);
19087 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19089 if (this.dateWithinRange(newDate)){
19090 this.date = newDate;
19091 this.viewDate = newViewDate;
19092 this.setValue(this.formatDate(this.date));
19094 e.preventDefault();
19095 dateChanged = true;
19099 this.setValue(this.formatDate(this.date));
19101 e.preventDefault();
19104 this.setValue(this.formatDate(this.date));
19118 onClick: function(e)
19120 e.stopPropagation();
19121 e.preventDefault();
19123 var target = e.getTarget();
19125 if(target.nodeName.toLowerCase() === 'i'){
19126 target = Roo.get(target).dom.parentNode;
19129 var nodeName = target.nodeName;
19130 var className = target.className;
19131 var html = target.innerHTML;
19132 //Roo.log(nodeName);
19134 switch(nodeName.toLowerCase()) {
19136 switch(className) {
19142 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19143 switch(this.viewMode){
19145 this.viewDate = this.moveMonth(this.viewDate, dir);
19149 this.viewDate = this.moveYear(this.viewDate, dir);
19155 var date = new Date();
19156 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19158 this.setValue(this.formatDate(this.date));
19165 if (className.indexOf('disabled') < 0) {
19166 this.viewDate.setUTCDate(1);
19167 if (className.indexOf('month') > -1) {
19168 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19170 var year = parseInt(html, 10) || 0;
19171 this.viewDate.setUTCFullYear(year);
19175 if(this.singleMode){
19176 this.setValue(this.formatDate(this.viewDate));
19187 //Roo.log(className);
19188 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19189 var day = parseInt(html, 10) || 1;
19190 var year = this.viewDate.getUTCFullYear(),
19191 month = this.viewDate.getUTCMonth();
19193 if (className.indexOf('old') > -1) {
19200 } else if (className.indexOf('new') > -1) {
19208 //Roo.log([year,month,day]);
19209 this.date = this.UTCDate(year, month, day,0,0,0,0);
19210 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19212 //Roo.log(this.formatDate(this.date));
19213 this.setValue(this.formatDate(this.date));
19220 setStartDate: function(startDate)
19222 this.startDate = startDate || -Infinity;
19223 if (this.startDate !== -Infinity) {
19224 this.startDate = this.parseDate(this.startDate);
19227 this.updateNavArrows();
19230 setEndDate: function(endDate)
19232 this.endDate = endDate || Infinity;
19233 if (this.endDate !== Infinity) {
19234 this.endDate = this.parseDate(this.endDate);
19237 this.updateNavArrows();
19240 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19242 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19243 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19244 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19246 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19247 return parseInt(d, 10);
19250 this.updateNavArrows();
19253 updateNavArrows: function()
19255 if(this.singleMode){
19259 var d = new Date(this.viewDate),
19260 year = d.getUTCFullYear(),
19261 month = d.getUTCMonth();
19263 Roo.each(this.picker().select('.prev', true).elements, function(v){
19265 switch (this.viewMode) {
19268 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19274 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19281 Roo.each(this.picker().select('.next', true).elements, function(v){
19283 switch (this.viewMode) {
19286 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19292 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19300 moveMonth: function(date, dir)
19305 var new_date = new Date(date.valueOf()),
19306 day = new_date.getUTCDate(),
19307 month = new_date.getUTCMonth(),
19308 mag = Math.abs(dir),
19310 dir = dir > 0 ? 1 : -1;
19313 // If going back one month, make sure month is not current month
19314 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19316 return new_date.getUTCMonth() == month;
19318 // If going forward one month, make sure month is as expected
19319 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19321 return new_date.getUTCMonth() != new_month;
19323 new_month = month + dir;
19324 new_date.setUTCMonth(new_month);
19325 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19326 if (new_month < 0 || new_month > 11) {
19327 new_month = (new_month + 12) % 12;
19330 // For magnitudes >1, move one month at a time...
19331 for (var i=0; i<mag; i++) {
19332 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19333 new_date = this.moveMonth(new_date, dir);
19335 // ...then reset the day, keeping it in the new month
19336 new_month = new_date.getUTCMonth();
19337 new_date.setUTCDate(day);
19339 return new_month != new_date.getUTCMonth();
19342 // Common date-resetting loop -- if date is beyond end of month, make it
19345 new_date.setUTCDate(--day);
19346 new_date.setUTCMonth(new_month);
19351 moveYear: function(date, dir)
19353 return this.moveMonth(date, dir*12);
19356 dateWithinRange: function(date)
19358 return date >= this.startDate && date <= this.endDate;
19364 this.picker().remove();
19367 validateValue : function(value)
19369 if(this.getVisibilityEl().hasClass('hidden')){
19373 if(value.length < 1) {
19374 if(this.allowBlank){
19380 if(value.length < this.minLength){
19383 if(value.length > this.maxLength){
19387 var vt = Roo.form.VTypes;
19388 if(!vt[this.vtype](value, this)){
19392 if(typeof this.validator == "function"){
19393 var msg = this.validator(value);
19399 if(this.regex && !this.regex.test(value)){
19403 if(typeof(this.parseDate(value)) == 'undefined'){
19407 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19411 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19421 this.date = this.viewDate = '';
19423 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19428 Roo.apply(Roo.bootstrap.DateField, {
19439 html: '<i class="fa fa-arrow-left"/>'
19449 html: '<i class="fa fa-arrow-right"/>'
19491 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19492 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19493 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19494 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19495 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19508 navFnc: 'FullYear',
19513 navFnc: 'FullYear',
19518 Roo.apply(Roo.bootstrap.DateField, {
19522 cls: 'datepicker dropdown-menu roo-dynamic',
19526 cls: 'datepicker-days',
19530 cls: 'table-condensed',
19532 Roo.bootstrap.DateField.head,
19536 Roo.bootstrap.DateField.footer
19543 cls: 'datepicker-months',
19547 cls: 'table-condensed',
19549 Roo.bootstrap.DateField.head,
19550 Roo.bootstrap.DateField.content,
19551 Roo.bootstrap.DateField.footer
19558 cls: 'datepicker-years',
19562 cls: 'table-condensed',
19564 Roo.bootstrap.DateField.head,
19565 Roo.bootstrap.DateField.content,
19566 Roo.bootstrap.DateField.footer
19585 * @class Roo.bootstrap.TimeField
19586 * @extends Roo.bootstrap.Input
19587 * Bootstrap DateField class
19591 * Create a new TimeField
19592 * @param {Object} config The config object
19595 Roo.bootstrap.TimeField = function(config){
19596 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19600 * Fires when this field show.
19601 * @param {Roo.bootstrap.DateField} thisthis
19602 * @param {Mixed} date The date value
19607 * Fires when this field hide.
19608 * @param {Roo.bootstrap.DateField} this
19609 * @param {Mixed} date The date value
19614 * Fires when select a date.
19615 * @param {Roo.bootstrap.DateField} this
19616 * @param {Mixed} date The date value
19622 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19625 * @cfg {String} format
19626 * The default time format string which can be overriden for localization support. The format must be
19627 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19631 onRender: function(ct, position)
19634 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19636 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19638 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19640 this.pop = this.picker().select('>.datepicker-time',true).first();
19641 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19643 this.picker().on('mousedown', this.onMousedown, this);
19644 this.picker().on('click', this.onClick, this);
19646 this.picker().addClass('datepicker-dropdown');
19651 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19652 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19653 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19654 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19655 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19656 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19660 fireKey: function(e){
19661 if (!this.picker().isVisible()){
19662 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19668 e.preventDefault();
19676 this.onTogglePeriod();
19679 this.onIncrementMinutes();
19682 this.onDecrementMinutes();
19691 onClick: function(e) {
19692 e.stopPropagation();
19693 e.preventDefault();
19696 picker : function()
19698 return this.el.select('.datepicker', true).first();
19701 fillTime: function()
19703 var time = this.pop.select('tbody', true).first();
19705 time.dom.innerHTML = '';
19720 cls: 'hours-up glyphicon glyphicon-chevron-up'
19740 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19761 cls: 'timepicker-hour',
19776 cls: 'timepicker-minute',
19791 cls: 'btn btn-primary period',
19813 cls: 'hours-down glyphicon glyphicon-chevron-down'
19833 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19851 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19858 var hours = this.time.getHours();
19859 var minutes = this.time.getMinutes();
19872 hours = hours - 12;
19876 hours = '0' + hours;
19880 minutes = '0' + minutes;
19883 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19884 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19885 this.pop.select('button', true).first().dom.innerHTML = period;
19891 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19893 var cls = ['bottom'];
19895 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19902 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19907 this.picker().addClass(cls.join('-'));
19911 Roo.each(cls, function(c){
19913 _this.picker().setTop(_this.inputEl().getHeight());
19917 _this.picker().setTop(0 - _this.picker().getHeight());
19922 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19926 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19933 onFocus : function()
19935 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19939 onBlur : function()
19941 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19947 this.picker().show();
19952 this.fireEvent('show', this, this.date);
19957 this.picker().hide();
19960 this.fireEvent('hide', this, this.date);
19963 setTime : function()
19966 this.setValue(this.time.format(this.format));
19968 this.fireEvent('select', this, this.date);
19973 onMousedown: function(e){
19974 e.stopPropagation();
19975 e.preventDefault();
19978 onIncrementHours: function()
19980 Roo.log('onIncrementHours');
19981 this.time = this.time.add(Date.HOUR, 1);
19986 onDecrementHours: function()
19988 Roo.log('onDecrementHours');
19989 this.time = this.time.add(Date.HOUR, -1);
19993 onIncrementMinutes: function()
19995 Roo.log('onIncrementMinutes');
19996 this.time = this.time.add(Date.MINUTE, 1);
20000 onDecrementMinutes: function()
20002 Roo.log('onDecrementMinutes');
20003 this.time = this.time.add(Date.MINUTE, -1);
20007 onTogglePeriod: function()
20009 Roo.log('onTogglePeriod');
20010 this.time = this.time.add(Date.HOUR, 12);
20017 Roo.apply(Roo.bootstrap.TimeField, {
20047 cls: 'btn btn-info ok',
20059 Roo.apply(Roo.bootstrap.TimeField, {
20063 cls: 'datepicker dropdown-menu',
20067 cls: 'datepicker-time',
20071 cls: 'table-condensed',
20073 Roo.bootstrap.TimeField.content,
20074 Roo.bootstrap.TimeField.footer
20093 * @class Roo.bootstrap.MonthField
20094 * @extends Roo.bootstrap.Input
20095 * Bootstrap MonthField class
20097 * @cfg {String} language default en
20100 * Create a new MonthField
20101 * @param {Object} config The config object
20104 Roo.bootstrap.MonthField = function(config){
20105 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20110 * Fires when this field show.
20111 * @param {Roo.bootstrap.MonthField} this
20112 * @param {Mixed} date The date value
20117 * Fires when this field hide.
20118 * @param {Roo.bootstrap.MonthField} this
20119 * @param {Mixed} date The date value
20124 * Fires when select a date.
20125 * @param {Roo.bootstrap.MonthField} this
20126 * @param {String} oldvalue The old value
20127 * @param {String} newvalue The new value
20133 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20135 onRender: function(ct, position)
20138 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20140 this.language = this.language || 'en';
20141 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20142 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20144 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20145 this.isInline = false;
20146 this.isInput = true;
20147 this.component = this.el.select('.add-on', true).first() || false;
20148 this.component = (this.component && this.component.length === 0) ? false : this.component;
20149 this.hasInput = this.component && this.inputEL().length;
20151 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20153 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20155 this.picker().on('mousedown', this.onMousedown, this);
20156 this.picker().on('click', this.onClick, this);
20158 this.picker().addClass('datepicker-dropdown');
20160 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20161 v.setStyle('width', '189px');
20168 if(this.isInline) {
20174 setValue: function(v, suppressEvent)
20176 var o = this.getValue();
20178 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20182 if(suppressEvent !== true){
20183 this.fireEvent('select', this, o, v);
20188 getValue: function()
20193 onClick: function(e)
20195 e.stopPropagation();
20196 e.preventDefault();
20198 var target = e.getTarget();
20200 if(target.nodeName.toLowerCase() === 'i'){
20201 target = Roo.get(target).dom.parentNode;
20204 var nodeName = target.nodeName;
20205 var className = target.className;
20206 var html = target.innerHTML;
20208 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20212 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20214 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20220 picker : function()
20222 return this.pickerEl;
20225 fillMonths: function()
20228 var months = this.picker().select('>.datepicker-months td', true).first();
20230 months.dom.innerHTML = '';
20236 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20239 months.createChild(month);
20248 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20249 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20252 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20253 e.removeClass('active');
20255 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20256 e.addClass('active');
20263 if(this.isInline) {
20267 this.picker().removeClass(['bottom', 'top']);
20269 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20271 * place to the top of element!
20275 this.picker().addClass('top');
20276 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20281 this.picker().addClass('bottom');
20283 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20286 onFocus : function()
20288 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20292 onBlur : function()
20294 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20296 var d = this.inputEl().getValue();
20305 this.picker().show();
20306 this.picker().select('>.datepicker-months', true).first().show();
20310 this.fireEvent('show', this, this.date);
20315 if(this.isInline) {
20318 this.picker().hide();
20319 this.fireEvent('hide', this, this.date);
20323 onMousedown: function(e)
20325 e.stopPropagation();
20326 e.preventDefault();
20331 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20335 fireKey: function(e)
20337 if (!this.picker().isVisible()){
20338 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20349 e.preventDefault();
20353 dir = e.keyCode == 37 ? -1 : 1;
20355 this.vIndex = this.vIndex + dir;
20357 if(this.vIndex < 0){
20361 if(this.vIndex > 11){
20365 if(isNaN(this.vIndex)){
20369 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20375 dir = e.keyCode == 38 ? -1 : 1;
20377 this.vIndex = this.vIndex + dir * 4;
20379 if(this.vIndex < 0){
20383 if(this.vIndex > 11){
20387 if(isNaN(this.vIndex)){
20391 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20396 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20397 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20401 e.preventDefault();
20404 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20405 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20421 this.picker().remove();
20426 Roo.apply(Roo.bootstrap.MonthField, {
20445 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20446 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20451 Roo.apply(Roo.bootstrap.MonthField, {
20455 cls: 'datepicker dropdown-menu roo-dynamic',
20459 cls: 'datepicker-months',
20463 cls: 'table-condensed',
20465 Roo.bootstrap.DateField.content
20485 * @class Roo.bootstrap.CheckBox
20486 * @extends Roo.bootstrap.Input
20487 * Bootstrap CheckBox class
20489 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20490 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20491 * @cfg {String} boxLabel The text that appears beside the checkbox
20492 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20493 * @cfg {Boolean} checked initnal the element
20494 * @cfg {Boolean} inline inline the element (default false)
20495 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20496 * @cfg {String} tooltip label tooltip
20499 * Create a new CheckBox
20500 * @param {Object} config The config object
20503 Roo.bootstrap.CheckBox = function(config){
20504 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20509 * Fires when the element is checked or unchecked.
20510 * @param {Roo.bootstrap.CheckBox} this This input
20511 * @param {Boolean} checked The new checked value
20516 * Fires when the element is click.
20517 * @param {Roo.bootstrap.CheckBox} this This input
20524 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20526 inputType: 'checkbox',
20535 getAutoCreate : function()
20537 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20543 cfg.cls = 'form-group ' + this.inputType; //input-group
20546 cfg.cls += ' ' + this.inputType + '-inline';
20552 type : this.inputType,
20553 value : this.inputValue,
20554 cls : 'roo-' + this.inputType, //'form-box',
20555 placeholder : this.placeholder || ''
20559 if(this.inputType != 'radio'){
20563 cls : 'roo-hidden-value',
20564 value : this.checked ? this.inputValue : this.valueOff
20569 if (this.weight) { // Validity check?
20570 cfg.cls += " " + this.inputType + "-" + this.weight;
20573 if (this.disabled) {
20574 input.disabled=true;
20578 input.checked = this.checked;
20583 input.name = this.name;
20585 if(this.inputType != 'radio'){
20586 hidden.name = this.name;
20587 input.name = '_hidden_' + this.name;
20592 input.cls += ' input-' + this.size;
20597 ['xs','sm','md','lg'].map(function(size){
20598 if (settings[size]) {
20599 cfg.cls += ' col-' + size + '-' + settings[size];
20603 var inputblock = input;
20605 if (this.before || this.after) {
20608 cls : 'input-group',
20613 inputblock.cn.push({
20615 cls : 'input-group-addon',
20620 inputblock.cn.push(input);
20622 if(this.inputType != 'radio'){
20623 inputblock.cn.push(hidden);
20627 inputblock.cn.push({
20629 cls : 'input-group-addon',
20636 if (align ==='left' && this.fieldLabel.length) {
20637 // Roo.log("left and has label");
20642 cls : 'control-label',
20643 html : this.fieldLabel
20653 if(this.labelWidth > 12){
20654 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20657 if(this.labelWidth < 13 && this.labelmd == 0){
20658 this.labelmd = this.labelWidth;
20661 if(this.labellg > 0){
20662 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20663 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20666 if(this.labelmd > 0){
20667 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20668 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20671 if(this.labelsm > 0){
20672 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20673 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20676 if(this.labelxs > 0){
20677 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20678 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20681 } else if ( this.fieldLabel.length) {
20682 // Roo.log(" label");
20686 tag: this.boxLabel ? 'span' : 'label',
20688 cls: 'control-label box-input-label',
20689 //cls : 'input-group-addon',
20690 html : this.fieldLabel
20699 // Roo.log(" no label && no align");
20700 cfg.cn = [ inputblock ] ;
20706 var boxLabelCfg = {
20708 //'for': id, // box label is handled by onclick - so no for...
20710 html: this.boxLabel
20714 boxLabelCfg.tooltip = this.tooltip;
20717 cfg.cn.push(boxLabelCfg);
20720 if(this.inputType != 'radio'){
20721 cfg.cn.push(hidden);
20729 * return the real input element.
20731 inputEl: function ()
20733 return this.el.select('input.roo-' + this.inputType,true).first();
20735 hiddenEl: function ()
20737 return this.el.select('input.roo-hidden-value',true).first();
20740 labelEl: function()
20742 return this.el.select('label.control-label',true).first();
20744 /* depricated... */
20748 return this.labelEl();
20751 boxLabelEl: function()
20753 return this.el.select('label.box-label',true).first();
20756 initEvents : function()
20758 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20760 this.inputEl().on('click', this.onClick, this);
20762 if (this.boxLabel) {
20763 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20766 this.startValue = this.getValue();
20769 Roo.bootstrap.CheckBox.register(this);
20773 onClick : function(e)
20775 if(this.fireEvent('click', this, e) !== false){
20776 this.setChecked(!this.checked);
20781 setChecked : function(state,suppressEvent)
20783 this.startValue = this.getValue();
20785 if(this.inputType == 'radio'){
20787 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20788 e.dom.checked = false;
20791 this.inputEl().dom.checked = true;
20793 this.inputEl().dom.value = this.inputValue;
20795 if(suppressEvent !== true){
20796 this.fireEvent('check', this, true);
20804 this.checked = state;
20806 this.inputEl().dom.checked = state;
20809 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20811 if(suppressEvent !== true){
20812 this.fireEvent('check', this, state);
20818 getValue : function()
20820 if(this.inputType == 'radio'){
20821 return this.getGroupValue();
20824 return this.hiddenEl().dom.value;
20828 getGroupValue : function()
20830 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20834 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20837 setValue : function(v,suppressEvent)
20839 if(this.inputType == 'radio'){
20840 this.setGroupValue(v, suppressEvent);
20844 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20849 setGroupValue : function(v, suppressEvent)
20851 this.startValue = this.getValue();
20853 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20854 e.dom.checked = false;
20856 if(e.dom.value == v){
20857 e.dom.checked = true;
20861 if(suppressEvent !== true){
20862 this.fireEvent('check', this, true);
20870 validate : function()
20872 if(this.getVisibilityEl().hasClass('hidden')){
20878 (this.inputType == 'radio' && this.validateRadio()) ||
20879 (this.inputType == 'checkbox' && this.validateCheckbox())
20885 this.markInvalid();
20889 validateRadio : function()
20891 if(this.getVisibilityEl().hasClass('hidden')){
20895 if(this.allowBlank){
20901 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20902 if(!e.dom.checked){
20914 validateCheckbox : function()
20917 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20918 //return (this.getValue() == this.inputValue) ? true : false;
20921 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20929 for(var i in group){
20930 if(group[i].el.isVisible(true)){
20938 for(var i in group){
20943 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20950 * Mark this field as valid
20952 markValid : function()
20956 this.fireEvent('valid', this);
20958 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20961 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20968 if(this.inputType == 'radio'){
20969 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20970 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20971 e.findParent('.form-group', false, true).addClass(_this.validClass);
20978 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20979 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20983 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20989 for(var i in group){
20990 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20991 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20996 * Mark this field as invalid
20997 * @param {String} msg The validation message
20999 markInvalid : function(msg)
21001 if(this.allowBlank){
21007 this.fireEvent('invalid', this, msg);
21009 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21012 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21016 label.markInvalid();
21019 if(this.inputType == 'radio'){
21020 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21021 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21022 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21029 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21030 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21034 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21040 for(var i in group){
21041 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21042 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21047 clearInvalid : function()
21049 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21051 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21053 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21055 if (label && label.iconEl) {
21056 label.iconEl.removeClass(label.validClass);
21057 label.iconEl.removeClass(label.invalidClass);
21061 disable : function()
21063 if(this.inputType != 'radio'){
21064 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21071 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21072 _this.getActionEl().addClass(this.disabledClass);
21073 e.dom.disabled = true;
21077 this.disabled = true;
21078 this.fireEvent("disable", this);
21082 enable : function()
21084 if(this.inputType != 'radio'){
21085 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21092 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21093 _this.getActionEl().removeClass(this.disabledClass);
21094 e.dom.disabled = false;
21098 this.disabled = false;
21099 this.fireEvent("enable", this);
21103 setBoxLabel : function(v)
21108 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21114 Roo.apply(Roo.bootstrap.CheckBox, {
21119 * register a CheckBox Group
21120 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21122 register : function(checkbox)
21124 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21125 this.groups[checkbox.groupId] = {};
21128 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21132 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21136 * fetch a CheckBox Group based on the group ID
21137 * @param {string} the group ID
21138 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21140 get: function(groupId) {
21141 if (typeof(this.groups[groupId]) == 'undefined') {
21145 return this.groups[groupId] ;
21158 * @class Roo.bootstrap.Radio
21159 * @extends Roo.bootstrap.Component
21160 * Bootstrap Radio class
21161 * @cfg {String} boxLabel - the label associated
21162 * @cfg {String} value - the value of radio
21165 * Create a new Radio
21166 * @param {Object} config The config object
21168 Roo.bootstrap.Radio = function(config){
21169 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21173 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21179 getAutoCreate : function()
21183 cls : 'form-group radio',
21188 html : this.boxLabel
21196 initEvents : function()
21198 this.parent().register(this);
21200 this.el.on('click', this.onClick, this);
21204 onClick : function(e)
21206 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21207 this.setChecked(true);
21211 setChecked : function(state, suppressEvent)
21213 this.parent().setValue(this.value, suppressEvent);
21217 setBoxLabel : function(v)
21222 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21237 * @class Roo.bootstrap.SecurePass
21238 * @extends Roo.bootstrap.Input
21239 * Bootstrap SecurePass class
21243 * Create a new SecurePass
21244 * @param {Object} config The config object
21247 Roo.bootstrap.SecurePass = function (config) {
21248 // these go here, so the translation tool can replace them..
21250 PwdEmpty: "Please type a password, and then retype it to confirm.",
21251 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21252 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21253 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21254 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21255 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21256 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21257 TooWeak: "Your password is Too Weak."
21259 this.meterLabel = "Password strength:";
21260 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21261 this.meterClass = [
21262 "roo-password-meter-tooweak",
21263 "roo-password-meter-weak",
21264 "roo-password-meter-medium",
21265 "roo-password-meter-strong",
21266 "roo-password-meter-grey"
21271 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21274 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21276 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21278 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21279 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21280 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21281 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21282 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21283 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21284 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21294 * @cfg {String/Object} Label for the strength meter (defaults to
21295 * 'Password strength:')
21300 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21301 * ['Weak', 'Medium', 'Strong'])
21304 pwdStrengths: false,
21317 initEvents: function ()
21319 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21321 if (this.el.is('input[type=password]') && Roo.isSafari) {
21322 this.el.on('keydown', this.SafariOnKeyDown, this);
21325 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21328 onRender: function (ct, position)
21330 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21331 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21332 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21334 this.trigger.createChild({
21339 cls: 'roo-password-meter-grey col-xs-12',
21342 //width: this.meterWidth + 'px'
21346 cls: 'roo-password-meter-text'
21352 if (this.hideTrigger) {
21353 this.trigger.setDisplayed(false);
21355 this.setSize(this.width || '', this.height || '');
21358 onDestroy: function ()
21360 if (this.trigger) {
21361 this.trigger.removeAllListeners();
21362 this.trigger.remove();
21365 this.wrap.remove();
21367 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21370 checkStrength: function ()
21372 var pwd = this.inputEl().getValue();
21373 if (pwd == this._lastPwd) {
21378 if (this.ClientSideStrongPassword(pwd)) {
21380 } else if (this.ClientSideMediumPassword(pwd)) {
21382 } else if (this.ClientSideWeakPassword(pwd)) {
21388 Roo.log('strength1: ' + strength);
21390 //var pm = this.trigger.child('div/div/div').dom;
21391 var pm = this.trigger.child('div/div');
21392 pm.removeClass(this.meterClass);
21393 pm.addClass(this.meterClass[strength]);
21396 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21398 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21400 this._lastPwd = pwd;
21404 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21406 this._lastPwd = '';
21408 var pm = this.trigger.child('div/div');
21409 pm.removeClass(this.meterClass);
21410 pm.addClass('roo-password-meter-grey');
21413 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21416 this.inputEl().dom.type='password';
21419 validateValue: function (value)
21422 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21425 if (value.length == 0) {
21426 if (this.allowBlank) {
21427 this.clearInvalid();
21431 this.markInvalid(this.errors.PwdEmpty);
21432 this.errorMsg = this.errors.PwdEmpty;
21440 if ('[\x21-\x7e]*'.match(value)) {
21441 this.markInvalid(this.errors.PwdBadChar);
21442 this.errorMsg = this.errors.PwdBadChar;
21445 if (value.length < 6) {
21446 this.markInvalid(this.errors.PwdShort);
21447 this.errorMsg = this.errors.PwdShort;
21450 if (value.length > 16) {
21451 this.markInvalid(this.errors.PwdLong);
21452 this.errorMsg = this.errors.PwdLong;
21456 if (this.ClientSideStrongPassword(value)) {
21458 } else if (this.ClientSideMediumPassword(value)) {
21460 } else if (this.ClientSideWeakPassword(value)) {
21467 if (strength < 2) {
21468 //this.markInvalid(this.errors.TooWeak);
21469 this.errorMsg = this.errors.TooWeak;
21474 console.log('strength2: ' + strength);
21476 //var pm = this.trigger.child('div/div/div').dom;
21478 var pm = this.trigger.child('div/div');
21479 pm.removeClass(this.meterClass);
21480 pm.addClass(this.meterClass[strength]);
21482 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21484 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21486 this.errorMsg = '';
21490 CharacterSetChecks: function (type)
21493 this.fResult = false;
21496 isctype: function (character, type)
21499 case this.kCapitalLetter:
21500 if (character >= 'A' && character <= 'Z') {
21505 case this.kSmallLetter:
21506 if (character >= 'a' && character <= 'z') {
21512 if (character >= '0' && character <= '9') {
21517 case this.kPunctuation:
21518 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21529 IsLongEnough: function (pwd, size)
21531 return !(pwd == null || isNaN(size) || pwd.length < size);
21534 SpansEnoughCharacterSets: function (word, nb)
21536 if (!this.IsLongEnough(word, nb))
21541 var characterSetChecks = new Array(
21542 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21543 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21546 for (var index = 0; index < word.length; ++index) {
21547 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21548 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21549 characterSetChecks[nCharSet].fResult = true;
21556 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21557 if (characterSetChecks[nCharSet].fResult) {
21562 if (nCharSets < nb) {
21568 ClientSideStrongPassword: function (pwd)
21570 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21573 ClientSideMediumPassword: function (pwd)
21575 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21578 ClientSideWeakPassword: function (pwd)
21580 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21583 })//<script type="text/javascript">
21586 * Based Ext JS Library 1.1.1
21587 * Copyright(c) 2006-2007, Ext JS, LLC.
21593 * @class Roo.HtmlEditorCore
21594 * @extends Roo.Component
21595 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21597 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21600 Roo.HtmlEditorCore = function(config){
21603 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21608 * @event initialize
21609 * Fires when the editor is fully initialized (including the iframe)
21610 * @param {Roo.HtmlEditorCore} this
21615 * Fires when the editor is first receives the focus. Any insertion must wait
21616 * until after this event.
21617 * @param {Roo.HtmlEditorCore} this
21621 * @event beforesync
21622 * Fires before the textarea is updated with content from the editor iframe. Return false
21623 * to cancel the sync.
21624 * @param {Roo.HtmlEditorCore} this
21625 * @param {String} html
21629 * @event beforepush
21630 * Fires before the iframe editor is updated with content from the textarea. Return false
21631 * to cancel the push.
21632 * @param {Roo.HtmlEditorCore} this
21633 * @param {String} html
21638 * Fires when the textarea is updated with content from the editor iframe.
21639 * @param {Roo.HtmlEditorCore} this
21640 * @param {String} html
21645 * Fires when the iframe editor is updated with content from the textarea.
21646 * @param {Roo.HtmlEditorCore} this
21647 * @param {String} html
21652 * @event editorevent
21653 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21654 * @param {Roo.HtmlEditorCore} this
21660 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21662 // defaults : white / black...
21663 this.applyBlacklists();
21670 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21674 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21680 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21685 * @cfg {Number} height (in pixels)
21689 * @cfg {Number} width (in pixels)
21694 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21697 stylesheets: false,
21702 // private properties
21703 validationEvent : false,
21705 initialized : false,
21707 sourceEditMode : false,
21708 onFocus : Roo.emptyFn,
21710 hideMode:'offsets',
21714 // blacklist + whitelisted elements..
21721 * Protected method that will not generally be called directly. It
21722 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21723 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21725 getDocMarkup : function(){
21729 // inherit styels from page...??
21730 if (this.stylesheets === false) {
21732 Roo.get(document.head).select('style').each(function(node) {
21733 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21736 Roo.get(document.head).select('link').each(function(node) {
21737 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21740 } else if (!this.stylesheets.length) {
21742 st = '<style type="text/css">' +
21743 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21746 st = '<style type="text/css">' +
21751 st += '<style type="text/css">' +
21752 'IMG { cursor: pointer } ' +
21755 var cls = 'roo-htmleditor-body';
21757 if(this.bodyCls.length){
21758 cls += ' ' + this.bodyCls;
21761 return '<html><head>' + st +
21762 //<style type="text/css">' +
21763 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21765 ' </head><body class="' + cls + '"></body></html>';
21769 onRender : function(ct, position)
21772 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21773 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21776 this.el.dom.style.border = '0 none';
21777 this.el.dom.setAttribute('tabIndex', -1);
21778 this.el.addClass('x-hidden hide');
21782 if(Roo.isIE){ // fix IE 1px bogus margin
21783 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21787 this.frameId = Roo.id();
21791 var iframe = this.owner.wrap.createChild({
21793 cls: 'form-control', // bootstrap..
21795 name: this.frameId,
21796 frameBorder : 'no',
21797 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21802 this.iframe = iframe.dom;
21804 this.assignDocWin();
21806 this.doc.designMode = 'on';
21809 this.doc.write(this.getDocMarkup());
21813 var task = { // must defer to wait for browser to be ready
21815 //console.log("run task?" + this.doc.readyState);
21816 this.assignDocWin();
21817 if(this.doc.body || this.doc.readyState == 'complete'){
21819 this.doc.designMode="on";
21823 Roo.TaskMgr.stop(task);
21824 this.initEditor.defer(10, this);
21831 Roo.TaskMgr.start(task);
21836 onResize : function(w, h)
21838 Roo.log('resize: ' +w + ',' + h );
21839 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21843 if(typeof w == 'number'){
21845 this.iframe.style.width = w + 'px';
21847 if(typeof h == 'number'){
21849 this.iframe.style.height = h + 'px';
21851 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21858 * Toggles the editor between standard and source edit mode.
21859 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21861 toggleSourceEdit : function(sourceEditMode){
21863 this.sourceEditMode = sourceEditMode === true;
21865 if(this.sourceEditMode){
21867 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21870 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21871 //this.iframe.className = '';
21874 //this.setSize(this.owner.wrap.getSize());
21875 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21882 * Protected method that will not generally be called directly. If you need/want
21883 * custom HTML cleanup, this is the method you should override.
21884 * @param {String} html The HTML to be cleaned
21885 * return {String} The cleaned HTML
21887 cleanHtml : function(html){
21888 html = String(html);
21889 if(html.length > 5){
21890 if(Roo.isSafari){ // strip safari nonsense
21891 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21894 if(html == ' '){
21901 * HTML Editor -> Textarea
21902 * Protected method that will not generally be called directly. Syncs the contents
21903 * of the editor iframe with the textarea.
21905 syncValue : function(){
21906 if(this.initialized){
21907 var bd = (this.doc.body || this.doc.documentElement);
21908 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21909 var html = bd.innerHTML;
21911 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21912 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21914 html = '<div style="'+m[0]+'">' + html + '</div>';
21917 html = this.cleanHtml(html);
21918 // fix up the special chars.. normaly like back quotes in word...
21919 // however we do not want to do this with chinese..
21920 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21921 var cc = b.charCodeAt();
21923 (cc >= 0x4E00 && cc < 0xA000 ) ||
21924 (cc >= 0x3400 && cc < 0x4E00 ) ||
21925 (cc >= 0xf900 && cc < 0xfb00 )
21931 if(this.owner.fireEvent('beforesync', this, html) !== false){
21932 this.el.dom.value = html;
21933 this.owner.fireEvent('sync', this, html);
21939 * Protected method that will not generally be called directly. Pushes the value of the textarea
21940 * into the iframe editor.
21942 pushValue : function(){
21943 if(this.initialized){
21944 var v = this.el.dom.value.trim();
21946 // if(v.length < 1){
21950 if(this.owner.fireEvent('beforepush', this, v) !== false){
21951 var d = (this.doc.body || this.doc.documentElement);
21953 this.cleanUpPaste();
21954 this.el.dom.value = d.innerHTML;
21955 this.owner.fireEvent('push', this, v);
21961 deferFocus : function(){
21962 this.focus.defer(10, this);
21966 focus : function(){
21967 if(this.win && !this.sourceEditMode){
21974 assignDocWin: function()
21976 var iframe = this.iframe;
21979 this.doc = iframe.contentWindow.document;
21980 this.win = iframe.contentWindow;
21982 // if (!Roo.get(this.frameId)) {
21985 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21986 // this.win = Roo.get(this.frameId).dom.contentWindow;
21988 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21992 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21993 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21998 initEditor : function(){
21999 //console.log("INIT EDITOR");
22000 this.assignDocWin();
22004 this.doc.designMode="on";
22006 this.doc.write(this.getDocMarkup());
22009 var dbody = (this.doc.body || this.doc.documentElement);
22010 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22011 // this copies styles from the containing element into thsi one..
22012 // not sure why we need all of this..
22013 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22015 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22016 //ss['background-attachment'] = 'fixed'; // w3c
22017 dbody.bgProperties = 'fixed'; // ie
22018 //Roo.DomHelper.applyStyles(dbody, ss);
22019 Roo.EventManager.on(this.doc, {
22020 //'mousedown': this.onEditorEvent,
22021 'mouseup': this.onEditorEvent,
22022 'dblclick': this.onEditorEvent,
22023 'click': this.onEditorEvent,
22024 'keyup': this.onEditorEvent,
22029 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22031 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22032 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22034 this.initialized = true;
22036 this.owner.fireEvent('initialize', this);
22041 onDestroy : function(){
22047 //for (var i =0; i < this.toolbars.length;i++) {
22048 // // fixme - ask toolbars for heights?
22049 // this.toolbars[i].onDestroy();
22052 //this.wrap.dom.innerHTML = '';
22053 //this.wrap.remove();
22058 onFirstFocus : function(){
22060 this.assignDocWin();
22063 this.activated = true;
22066 if(Roo.isGecko){ // prevent silly gecko errors
22068 var s = this.win.getSelection();
22069 if(!s.focusNode || s.focusNode.nodeType != 3){
22070 var r = s.getRangeAt(0);
22071 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22076 this.execCmd('useCSS', true);
22077 this.execCmd('styleWithCSS', false);
22080 this.owner.fireEvent('activate', this);
22084 adjustFont: function(btn){
22085 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22086 //if(Roo.isSafari){ // safari
22089 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22090 if(Roo.isSafari){ // safari
22091 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22092 v = (v < 10) ? 10 : v;
22093 v = (v > 48) ? 48 : v;
22094 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22099 v = Math.max(1, v+adjust);
22101 this.execCmd('FontSize', v );
22104 onEditorEvent : function(e)
22106 this.owner.fireEvent('editorevent', this, e);
22107 // this.updateToolbar();
22108 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22111 insertTag : function(tg)
22113 // could be a bit smarter... -> wrap the current selected tRoo..
22114 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22116 range = this.createRange(this.getSelection());
22117 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22118 wrappingNode.appendChild(range.extractContents());
22119 range.insertNode(wrappingNode);
22126 this.execCmd("formatblock", tg);
22130 insertText : function(txt)
22134 var range = this.createRange();
22135 range.deleteContents();
22136 //alert(Sender.getAttribute('label'));
22138 range.insertNode(this.doc.createTextNode(txt));
22144 * Executes a Midas editor command on the editor document and performs necessary focus and
22145 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22146 * @param {String} cmd The Midas command
22147 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22149 relayCmd : function(cmd, value){
22151 this.execCmd(cmd, value);
22152 this.owner.fireEvent('editorevent', this);
22153 //this.updateToolbar();
22154 this.owner.deferFocus();
22158 * Executes a Midas editor command directly on the editor document.
22159 * For visual commands, you should use {@link #relayCmd} instead.
22160 * <b>This should only be called after the editor is initialized.</b>
22161 * @param {String} cmd The Midas command
22162 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22164 execCmd : function(cmd, value){
22165 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22172 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22174 * @param {String} text | dom node..
22176 insertAtCursor : function(text)
22179 if(!this.activated){
22185 var r = this.doc.selection.createRange();
22196 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22200 // from jquery ui (MIT licenced)
22202 var win = this.win;
22204 if (win.getSelection && win.getSelection().getRangeAt) {
22205 range = win.getSelection().getRangeAt(0);
22206 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22207 range.insertNode(node);
22208 } else if (win.document.selection && win.document.selection.createRange) {
22209 // no firefox support
22210 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22211 win.document.selection.createRange().pasteHTML(txt);
22213 // no firefox support
22214 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22215 this.execCmd('InsertHTML', txt);
22224 mozKeyPress : function(e){
22226 var c = e.getCharCode(), cmd;
22229 c = String.fromCharCode(c).toLowerCase();
22243 this.cleanUpPaste.defer(100, this);
22251 e.preventDefault();
22259 fixKeys : function(){ // load time branching for fastest keydown performance
22261 return function(e){
22262 var k = e.getKey(), r;
22265 r = this.doc.selection.createRange();
22268 r.pasteHTML('    ');
22275 r = this.doc.selection.createRange();
22277 var target = r.parentElement();
22278 if(!target || target.tagName.toLowerCase() != 'li'){
22280 r.pasteHTML('<br />');
22286 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22287 this.cleanUpPaste.defer(100, this);
22293 }else if(Roo.isOpera){
22294 return function(e){
22295 var k = e.getKey();
22299 this.execCmd('InsertHTML','    ');
22302 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22303 this.cleanUpPaste.defer(100, this);
22308 }else if(Roo.isSafari){
22309 return function(e){
22310 var k = e.getKey();
22314 this.execCmd('InsertText','\t');
22318 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22319 this.cleanUpPaste.defer(100, this);
22327 getAllAncestors: function()
22329 var p = this.getSelectedNode();
22332 a.push(p); // push blank onto stack..
22333 p = this.getParentElement();
22337 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22341 a.push(this.doc.body);
22345 lastSelNode : false,
22348 getSelection : function()
22350 this.assignDocWin();
22351 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22354 getSelectedNode: function()
22356 // this may only work on Gecko!!!
22358 // should we cache this!!!!
22363 var range = this.createRange(this.getSelection()).cloneRange();
22366 var parent = range.parentElement();
22368 var testRange = range.duplicate();
22369 testRange.moveToElementText(parent);
22370 if (testRange.inRange(range)) {
22373 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22376 parent = parent.parentElement;
22381 // is ancestor a text element.
22382 var ac = range.commonAncestorContainer;
22383 if (ac.nodeType == 3) {
22384 ac = ac.parentNode;
22387 var ar = ac.childNodes;
22390 var other_nodes = [];
22391 var has_other_nodes = false;
22392 for (var i=0;i<ar.length;i++) {
22393 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22396 // fullly contained node.
22398 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22403 // probably selected..
22404 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22405 other_nodes.push(ar[i]);
22409 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22414 has_other_nodes = true;
22416 if (!nodes.length && other_nodes.length) {
22417 nodes= other_nodes;
22419 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22425 createRange: function(sel)
22427 // this has strange effects when using with
22428 // top toolbar - not sure if it's a great idea.
22429 //this.editor.contentWindow.focus();
22430 if (typeof sel != "undefined") {
22432 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22434 return this.doc.createRange();
22437 return this.doc.createRange();
22440 getParentElement: function()
22443 this.assignDocWin();
22444 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22446 var range = this.createRange(sel);
22449 var p = range.commonAncestorContainer;
22450 while (p.nodeType == 3) { // text node
22461 * Range intersection.. the hard stuff...
22465 * [ -- selected range --- ]
22469 * if end is before start or hits it. fail.
22470 * if start is after end or hits it fail.
22472 * if either hits (but other is outside. - then it's not
22478 // @see http://www.thismuchiknow.co.uk/?p=64.
22479 rangeIntersectsNode : function(range, node)
22481 var nodeRange = node.ownerDocument.createRange();
22483 nodeRange.selectNode(node);
22485 nodeRange.selectNodeContents(node);
22488 var rangeStartRange = range.cloneRange();
22489 rangeStartRange.collapse(true);
22491 var rangeEndRange = range.cloneRange();
22492 rangeEndRange.collapse(false);
22494 var nodeStartRange = nodeRange.cloneRange();
22495 nodeStartRange.collapse(true);
22497 var nodeEndRange = nodeRange.cloneRange();
22498 nodeEndRange.collapse(false);
22500 return rangeStartRange.compareBoundaryPoints(
22501 Range.START_TO_START, nodeEndRange) == -1 &&
22502 rangeEndRange.compareBoundaryPoints(
22503 Range.START_TO_START, nodeStartRange) == 1;
22507 rangeCompareNode : function(range, node)
22509 var nodeRange = node.ownerDocument.createRange();
22511 nodeRange.selectNode(node);
22513 nodeRange.selectNodeContents(node);
22517 range.collapse(true);
22519 nodeRange.collapse(true);
22521 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22522 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22524 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22526 var nodeIsBefore = ss == 1;
22527 var nodeIsAfter = ee == -1;
22529 if (nodeIsBefore && nodeIsAfter) {
22532 if (!nodeIsBefore && nodeIsAfter) {
22533 return 1; //right trailed.
22536 if (nodeIsBefore && !nodeIsAfter) {
22537 return 2; // left trailed.
22543 // private? - in a new class?
22544 cleanUpPaste : function()
22546 // cleans up the whole document..
22547 Roo.log('cleanuppaste');
22549 this.cleanUpChildren(this.doc.body);
22550 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22551 if (clean != this.doc.body.innerHTML) {
22552 this.doc.body.innerHTML = clean;
22557 cleanWordChars : function(input) {// change the chars to hex code
22558 var he = Roo.HtmlEditorCore;
22560 var output = input;
22561 Roo.each(he.swapCodes, function(sw) {
22562 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22564 output = output.replace(swapper, sw[1]);
22571 cleanUpChildren : function (n)
22573 if (!n.childNodes.length) {
22576 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22577 this.cleanUpChild(n.childNodes[i]);
22584 cleanUpChild : function (node)
22587 //console.log(node);
22588 if (node.nodeName == "#text") {
22589 // clean up silly Windows -- stuff?
22592 if (node.nodeName == "#comment") {
22593 node.parentNode.removeChild(node);
22594 // clean up silly Windows -- stuff?
22597 var lcname = node.tagName.toLowerCase();
22598 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22599 // whitelist of tags..
22601 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22603 node.parentNode.removeChild(node);
22608 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22610 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22611 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22613 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22614 // remove_keep_children = true;
22617 if (remove_keep_children) {
22618 this.cleanUpChildren(node);
22619 // inserts everything just before this node...
22620 while (node.childNodes.length) {
22621 var cn = node.childNodes[0];
22622 node.removeChild(cn);
22623 node.parentNode.insertBefore(cn, node);
22625 node.parentNode.removeChild(node);
22629 if (!node.attributes || !node.attributes.length) {
22630 this.cleanUpChildren(node);
22634 function cleanAttr(n,v)
22637 if (v.match(/^\./) || v.match(/^\//)) {
22640 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22643 if (v.match(/^#/)) {
22646 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22647 node.removeAttribute(n);
22651 var cwhite = this.cwhite;
22652 var cblack = this.cblack;
22654 function cleanStyle(n,v)
22656 if (v.match(/expression/)) { //XSS?? should we even bother..
22657 node.removeAttribute(n);
22661 var parts = v.split(/;/);
22664 Roo.each(parts, function(p) {
22665 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22669 var l = p.split(':').shift().replace(/\s+/g,'');
22670 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22672 if ( cwhite.length && cblack.indexOf(l) > -1) {
22673 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22674 //node.removeAttribute(n);
22678 // only allow 'c whitelisted system attributes'
22679 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22680 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22681 //node.removeAttribute(n);
22691 if (clean.length) {
22692 node.setAttribute(n, clean.join(';'));
22694 node.removeAttribute(n);
22700 for (var i = node.attributes.length-1; i > -1 ; i--) {
22701 var a = node.attributes[i];
22704 if (a.name.toLowerCase().substr(0,2)=='on') {
22705 node.removeAttribute(a.name);
22708 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22709 node.removeAttribute(a.name);
22712 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22713 cleanAttr(a.name,a.value); // fixme..
22716 if (a.name == 'style') {
22717 cleanStyle(a.name,a.value);
22720 /// clean up MS crap..
22721 // tecnically this should be a list of valid class'es..
22724 if (a.name == 'class') {
22725 if (a.value.match(/^Mso/)) {
22726 node.className = '';
22729 if (a.value.match(/^body$/)) {
22730 node.className = '';
22741 this.cleanUpChildren(node);
22747 * Clean up MS wordisms...
22749 cleanWord : function(node)
22754 this.cleanWord(this.doc.body);
22757 if (node.nodeName == "#text") {
22758 // clean up silly Windows -- stuff?
22761 if (node.nodeName == "#comment") {
22762 node.parentNode.removeChild(node);
22763 // clean up silly Windows -- stuff?
22767 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22768 node.parentNode.removeChild(node);
22772 // remove - but keep children..
22773 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22774 while (node.childNodes.length) {
22775 var cn = node.childNodes[0];
22776 node.removeChild(cn);
22777 node.parentNode.insertBefore(cn, node);
22779 node.parentNode.removeChild(node);
22780 this.iterateChildren(node, this.cleanWord);
22784 if (node.className.length) {
22786 var cn = node.className.split(/\W+/);
22788 Roo.each(cn, function(cls) {
22789 if (cls.match(/Mso[a-zA-Z]+/)) {
22794 node.className = cna.length ? cna.join(' ') : '';
22796 node.removeAttribute("class");
22800 if (node.hasAttribute("lang")) {
22801 node.removeAttribute("lang");
22804 if (node.hasAttribute("style")) {
22806 var styles = node.getAttribute("style").split(";");
22808 Roo.each(styles, function(s) {
22809 if (!s.match(/:/)) {
22812 var kv = s.split(":");
22813 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22816 // what ever is left... we allow.
22819 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22820 if (!nstyle.length) {
22821 node.removeAttribute('style');
22824 this.iterateChildren(node, this.cleanWord);
22830 * iterateChildren of a Node, calling fn each time, using this as the scole..
22831 * @param {DomNode} node node to iterate children of.
22832 * @param {Function} fn method of this class to call on each item.
22834 iterateChildren : function(node, fn)
22836 if (!node.childNodes.length) {
22839 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22840 fn.call(this, node.childNodes[i])
22846 * cleanTableWidths.
22848 * Quite often pasting from word etc.. results in tables with column and widths.
22849 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22852 cleanTableWidths : function(node)
22857 this.cleanTableWidths(this.doc.body);
22862 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22865 Roo.log(node.tagName);
22866 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22867 this.iterateChildren(node, this.cleanTableWidths);
22870 if (node.hasAttribute('width')) {
22871 node.removeAttribute('width');
22875 if (node.hasAttribute("style")) {
22878 var styles = node.getAttribute("style").split(";");
22880 Roo.each(styles, function(s) {
22881 if (!s.match(/:/)) {
22884 var kv = s.split(":");
22885 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22888 // what ever is left... we allow.
22891 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22892 if (!nstyle.length) {
22893 node.removeAttribute('style');
22897 this.iterateChildren(node, this.cleanTableWidths);
22905 domToHTML : function(currentElement, depth, nopadtext) {
22907 depth = depth || 0;
22908 nopadtext = nopadtext || false;
22910 if (!currentElement) {
22911 return this.domToHTML(this.doc.body);
22914 //Roo.log(currentElement);
22916 var allText = false;
22917 var nodeName = currentElement.nodeName;
22918 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22920 if (nodeName == '#text') {
22922 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22927 if (nodeName != 'BODY') {
22930 // Prints the node tagName, such as <A>, <IMG>, etc
22933 for(i = 0; i < currentElement.attributes.length;i++) {
22935 var aname = currentElement.attributes.item(i).name;
22936 if (!currentElement.attributes.item(i).value.length) {
22939 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22942 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22951 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22954 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22959 // Traverse the tree
22961 var currentElementChild = currentElement.childNodes.item(i);
22962 var allText = true;
22963 var innerHTML = '';
22965 while (currentElementChild) {
22966 // Formatting code (indent the tree so it looks nice on the screen)
22967 var nopad = nopadtext;
22968 if (lastnode == 'SPAN') {
22972 if (currentElementChild.nodeName == '#text') {
22973 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22974 toadd = nopadtext ? toadd : toadd.trim();
22975 if (!nopad && toadd.length > 80) {
22976 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22978 innerHTML += toadd;
22981 currentElementChild = currentElement.childNodes.item(i);
22987 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22989 // Recursively traverse the tree structure of the child node
22990 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22991 lastnode = currentElementChild.nodeName;
22993 currentElementChild=currentElement.childNodes.item(i);
22999 // The remaining code is mostly for formatting the tree
23000 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23005 ret+= "</"+tagName+">";
23011 applyBlacklists : function()
23013 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23014 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23018 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23019 if (b.indexOf(tag) > -1) {
23022 this.white.push(tag);
23026 Roo.each(w, function(tag) {
23027 if (b.indexOf(tag) > -1) {
23030 if (this.white.indexOf(tag) > -1) {
23033 this.white.push(tag);
23038 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23039 if (w.indexOf(tag) > -1) {
23042 this.black.push(tag);
23046 Roo.each(b, function(tag) {
23047 if (w.indexOf(tag) > -1) {
23050 if (this.black.indexOf(tag) > -1) {
23053 this.black.push(tag);
23058 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23059 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23063 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23064 if (b.indexOf(tag) > -1) {
23067 this.cwhite.push(tag);
23071 Roo.each(w, function(tag) {
23072 if (b.indexOf(tag) > -1) {
23075 if (this.cwhite.indexOf(tag) > -1) {
23078 this.cwhite.push(tag);
23083 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23084 if (w.indexOf(tag) > -1) {
23087 this.cblack.push(tag);
23091 Roo.each(b, function(tag) {
23092 if (w.indexOf(tag) > -1) {
23095 if (this.cblack.indexOf(tag) > -1) {
23098 this.cblack.push(tag);
23103 setStylesheets : function(stylesheets)
23105 if(typeof(stylesheets) == 'string'){
23106 Roo.get(this.iframe.contentDocument.head).createChild({
23108 rel : 'stylesheet',
23117 Roo.each(stylesheets, function(s) {
23122 Roo.get(_this.iframe.contentDocument.head).createChild({
23124 rel : 'stylesheet',
23133 removeStylesheets : function()
23137 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23142 setStyle : function(style)
23144 Roo.get(this.iframe.contentDocument.head).createChild({
23153 // hide stuff that is not compatible
23167 * @event specialkey
23171 * @cfg {String} fieldClass @hide
23174 * @cfg {String} focusClass @hide
23177 * @cfg {String} autoCreate @hide
23180 * @cfg {String} inputType @hide
23183 * @cfg {String} invalidClass @hide
23186 * @cfg {String} invalidText @hide
23189 * @cfg {String} msgFx @hide
23192 * @cfg {String} validateOnBlur @hide
23196 Roo.HtmlEditorCore.white = [
23197 'area', 'br', 'img', 'input', 'hr', 'wbr',
23199 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23200 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23201 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23202 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23203 'table', 'ul', 'xmp',
23205 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23208 'dir', 'menu', 'ol', 'ul', 'dl',
23214 Roo.HtmlEditorCore.black = [
23215 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23217 'base', 'basefont', 'bgsound', 'blink', 'body',
23218 'frame', 'frameset', 'head', 'html', 'ilayer',
23219 'iframe', 'layer', 'link', 'meta', 'object',
23220 'script', 'style' ,'title', 'xml' // clean later..
23222 Roo.HtmlEditorCore.clean = [
23223 'script', 'style', 'title', 'xml'
23225 Roo.HtmlEditorCore.remove = [
23230 Roo.HtmlEditorCore.ablack = [
23234 Roo.HtmlEditorCore.aclean = [
23235 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23239 Roo.HtmlEditorCore.pwhite= [
23240 'http', 'https', 'mailto'
23243 // white listed style attributes.
23244 Roo.HtmlEditorCore.cwhite= [
23245 // 'text-align', /// default is to allow most things..
23251 // black listed style attributes.
23252 Roo.HtmlEditorCore.cblack= [
23253 // 'font-size' -- this can be set by the project
23257 Roo.HtmlEditorCore.swapCodes =[
23276 * @class Roo.bootstrap.HtmlEditor
23277 * @extends Roo.bootstrap.TextArea
23278 * Bootstrap HtmlEditor class
23281 * Create a new HtmlEditor
23282 * @param {Object} config The config object
23285 Roo.bootstrap.HtmlEditor = function(config){
23286 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23287 if (!this.toolbars) {
23288 this.toolbars = [];
23291 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23294 * @event initialize
23295 * Fires when the editor is fully initialized (including the iframe)
23296 * @param {HtmlEditor} this
23301 * Fires when the editor is first receives the focus. Any insertion must wait
23302 * until after this event.
23303 * @param {HtmlEditor} this
23307 * @event beforesync
23308 * Fires before the textarea is updated with content from the editor iframe. Return false
23309 * to cancel the sync.
23310 * @param {HtmlEditor} this
23311 * @param {String} html
23315 * @event beforepush
23316 * Fires before the iframe editor is updated with content from the textarea. Return false
23317 * to cancel the push.
23318 * @param {HtmlEditor} this
23319 * @param {String} html
23324 * Fires when the textarea is updated with content from the editor iframe.
23325 * @param {HtmlEditor} this
23326 * @param {String} html
23331 * Fires when the iframe editor is updated with content from the textarea.
23332 * @param {HtmlEditor} this
23333 * @param {String} html
23337 * @event editmodechange
23338 * Fires when the editor switches edit modes
23339 * @param {HtmlEditor} this
23340 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23342 editmodechange: true,
23344 * @event editorevent
23345 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23346 * @param {HtmlEditor} this
23350 * @event firstfocus
23351 * Fires when on first focus - needed by toolbars..
23352 * @param {HtmlEditor} this
23357 * Auto save the htmlEditor value as a file into Events
23358 * @param {HtmlEditor} this
23362 * @event savedpreview
23363 * preview the saved version of htmlEditor
23364 * @param {HtmlEditor} this
23371 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23375 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23380 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23385 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23390 * @cfg {Number} height (in pixels)
23394 * @cfg {Number} width (in pixels)
23399 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23402 stylesheets: false,
23407 // private properties
23408 validationEvent : false,
23410 initialized : false,
23413 onFocus : Roo.emptyFn,
23415 hideMode:'offsets',
23417 tbContainer : false,
23421 toolbarContainer :function() {
23422 return this.wrap.select('.x-html-editor-tb',true).first();
23426 * Protected method that will not generally be called directly. It
23427 * is called when the editor creates its toolbar. Override this method if you need to
23428 * add custom toolbar buttons.
23429 * @param {HtmlEditor} editor
23431 createToolbar : function(){
23432 Roo.log('renewing');
23433 Roo.log("create toolbars");
23435 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23436 this.toolbars[0].render(this.toolbarContainer());
23440 // if (!editor.toolbars || !editor.toolbars.length) {
23441 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23444 // for (var i =0 ; i < editor.toolbars.length;i++) {
23445 // editor.toolbars[i] = Roo.factory(
23446 // typeof(editor.toolbars[i]) == 'string' ?
23447 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23448 // Roo.bootstrap.HtmlEditor);
23449 // editor.toolbars[i].init(editor);
23455 onRender : function(ct, position)
23457 // Roo.log("Call onRender: " + this.xtype);
23459 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23461 this.wrap = this.inputEl().wrap({
23462 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23465 this.editorcore.onRender(ct, position);
23467 if (this.resizable) {
23468 this.resizeEl = new Roo.Resizable(this.wrap, {
23472 minHeight : this.height,
23473 height: this.height,
23474 handles : this.resizable,
23477 resize : function(r, w, h) {
23478 _t.onResize(w,h); // -something
23484 this.createToolbar(this);
23487 if(!this.width && this.resizable){
23488 this.setSize(this.wrap.getSize());
23490 if (this.resizeEl) {
23491 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23492 // should trigger onReize..
23498 onResize : function(w, h)
23500 Roo.log('resize: ' +w + ',' + h );
23501 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23505 if(this.inputEl() ){
23506 if(typeof w == 'number'){
23507 var aw = w - this.wrap.getFrameWidth('lr');
23508 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23511 if(typeof h == 'number'){
23512 var tbh = -11; // fixme it needs to tool bar size!
23513 for (var i =0; i < this.toolbars.length;i++) {
23514 // fixme - ask toolbars for heights?
23515 tbh += this.toolbars[i].el.getHeight();
23516 //if (this.toolbars[i].footer) {
23517 // tbh += this.toolbars[i].footer.el.getHeight();
23525 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23526 ah -= 5; // knock a few pixes off for look..
23527 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23531 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23532 this.editorcore.onResize(ew,eh);
23537 * Toggles the editor between standard and source edit mode.
23538 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23540 toggleSourceEdit : function(sourceEditMode)
23542 this.editorcore.toggleSourceEdit(sourceEditMode);
23544 if(this.editorcore.sourceEditMode){
23545 Roo.log('editor - showing textarea');
23548 // Roo.log(this.syncValue());
23550 this.inputEl().removeClass(['hide', 'x-hidden']);
23551 this.inputEl().dom.removeAttribute('tabIndex');
23552 this.inputEl().focus();
23554 Roo.log('editor - hiding textarea');
23556 // Roo.log(this.pushValue());
23559 this.inputEl().addClass(['hide', 'x-hidden']);
23560 this.inputEl().dom.setAttribute('tabIndex', -1);
23561 //this.deferFocus();
23564 if(this.resizable){
23565 this.setSize(this.wrap.getSize());
23568 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23571 // private (for BoxComponent)
23572 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23574 // private (for BoxComponent)
23575 getResizeEl : function(){
23579 // private (for BoxComponent)
23580 getPositionEl : function(){
23585 initEvents : function(){
23586 this.originalValue = this.getValue();
23590 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23593 // markInvalid : Roo.emptyFn,
23595 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23598 // clearInvalid : Roo.emptyFn,
23600 setValue : function(v){
23601 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23602 this.editorcore.pushValue();
23607 deferFocus : function(){
23608 this.focus.defer(10, this);
23612 focus : function(){
23613 this.editorcore.focus();
23619 onDestroy : function(){
23625 for (var i =0; i < this.toolbars.length;i++) {
23626 // fixme - ask toolbars for heights?
23627 this.toolbars[i].onDestroy();
23630 this.wrap.dom.innerHTML = '';
23631 this.wrap.remove();
23636 onFirstFocus : function(){
23637 //Roo.log("onFirstFocus");
23638 this.editorcore.onFirstFocus();
23639 for (var i =0; i < this.toolbars.length;i++) {
23640 this.toolbars[i].onFirstFocus();
23646 syncValue : function()
23648 this.editorcore.syncValue();
23651 pushValue : function()
23653 this.editorcore.pushValue();
23657 // hide stuff that is not compatible
23671 * @event specialkey
23675 * @cfg {String} fieldClass @hide
23678 * @cfg {String} focusClass @hide
23681 * @cfg {String} autoCreate @hide
23684 * @cfg {String} inputType @hide
23687 * @cfg {String} invalidClass @hide
23690 * @cfg {String} invalidText @hide
23693 * @cfg {String} msgFx @hide
23696 * @cfg {String} validateOnBlur @hide
23705 Roo.namespace('Roo.bootstrap.htmleditor');
23707 * @class Roo.bootstrap.HtmlEditorToolbar1
23712 new Roo.bootstrap.HtmlEditor({
23715 new Roo.bootstrap.HtmlEditorToolbar1({
23716 disable : { fonts: 1 , format: 1, ..., ... , ...],
23722 * @cfg {Object} disable List of elements to disable..
23723 * @cfg {Array} btns List of additional buttons.
23727 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23730 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23733 Roo.apply(this, config);
23735 // default disabled, based on 'good practice'..
23736 this.disable = this.disable || {};
23737 Roo.applyIf(this.disable, {
23740 specialElements : true
23742 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23744 this.editor = config.editor;
23745 this.editorcore = config.editor.editorcore;
23747 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23749 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23750 // dont call parent... till later.
23752 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23757 editorcore : false,
23762 "h1","h2","h3","h4","h5","h6",
23764 "abbr", "acronym", "address", "cite", "samp", "var",
23768 onRender : function(ct, position)
23770 // Roo.log("Call onRender: " + this.xtype);
23772 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23774 this.el.dom.style.marginBottom = '0';
23776 var editorcore = this.editorcore;
23777 var editor= this.editor;
23780 var btn = function(id,cmd , toggle, handler, html){
23782 var event = toggle ? 'toggle' : 'click';
23787 xns: Roo.bootstrap,
23790 enableToggle:toggle !== false,
23792 pressed : toggle ? false : null,
23795 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23796 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23802 // var cb_box = function...
23807 xns: Roo.bootstrap,
23808 glyphicon : 'font',
23812 xns: Roo.bootstrap,
23816 Roo.each(this.formats, function(f) {
23817 style.menu.items.push({
23819 xns: Roo.bootstrap,
23820 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23825 editorcore.insertTag(this.tagname);
23832 children.push(style);
23834 btn('bold',false,true);
23835 btn('italic',false,true);
23836 btn('align-left', 'justifyleft',true);
23837 btn('align-center', 'justifycenter',true);
23838 btn('align-right' , 'justifyright',true);
23839 btn('link', false, false, function(btn) {
23840 //Roo.log("create link?");
23841 var url = prompt(this.createLinkText, this.defaultLinkValue);
23842 if(url && url != 'http:/'+'/'){
23843 this.editorcore.relayCmd('createlink', url);
23846 btn('list','insertunorderedlist',true);
23847 btn('pencil', false,true, function(btn){
23849 this.toggleSourceEdit(btn.pressed);
23852 if (this.editor.btns.length > 0) {
23853 for (var i = 0; i<this.editor.btns.length; i++) {
23854 children.push(this.editor.btns[i]);
23862 xns: Roo.bootstrap,
23867 xns: Roo.bootstrap,
23872 cog.menu.items.push({
23874 xns: Roo.bootstrap,
23875 html : Clean styles,
23880 editorcore.insertTag(this.tagname);
23889 this.xtype = 'NavSimplebar';
23891 for(var i=0;i< children.length;i++) {
23893 this.buttons.add(this.addxtypeChild(children[i]));
23897 editor.on('editorevent', this.updateToolbar, this);
23899 onBtnClick : function(id)
23901 this.editorcore.relayCmd(id);
23902 this.editorcore.focus();
23906 * Protected method that will not generally be called directly. It triggers
23907 * a toolbar update by reading the markup state of the current selection in the editor.
23909 updateToolbar: function(){
23911 if(!this.editorcore.activated){
23912 this.editor.onFirstFocus(); // is this neeed?
23916 var btns = this.buttons;
23917 var doc = this.editorcore.doc;
23918 btns.get('bold').setActive(doc.queryCommandState('bold'));
23919 btns.get('italic').setActive(doc.queryCommandState('italic'));
23920 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23922 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23923 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23924 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23926 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23927 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23930 var ans = this.editorcore.getAllAncestors();
23931 if (this.formatCombo) {
23934 var store = this.formatCombo.store;
23935 this.formatCombo.setValue("");
23936 for (var i =0; i < ans.length;i++) {
23937 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23939 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23947 // hides menus... - so this cant be on a menu...
23948 Roo.bootstrap.MenuMgr.hideAll();
23950 Roo.bootstrap.MenuMgr.hideAll();
23951 //this.editorsyncValue();
23953 onFirstFocus: function() {
23954 this.buttons.each(function(item){
23958 toggleSourceEdit : function(sourceEditMode){
23961 if(sourceEditMode){
23962 Roo.log("disabling buttons");
23963 this.buttons.each( function(item){
23964 if(item.cmd != 'pencil'){
23970 Roo.log("enabling buttons");
23971 if(this.editorcore.initialized){
23972 this.buttons.each( function(item){
23978 Roo.log("calling toggole on editor");
23979 // tell the editor that it's been pressed..
23980 this.editor.toggleSourceEdit(sourceEditMode);
23990 * @class Roo.bootstrap.Table.AbstractSelectionModel
23991 * @extends Roo.util.Observable
23992 * Abstract base class for grid SelectionModels. It provides the interface that should be
23993 * implemented by descendant classes. This class should not be directly instantiated.
23996 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23997 this.locked = false;
23998 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24002 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24003 /** @ignore Called by the grid automatically. Do not call directly. */
24004 init : function(grid){
24010 * Locks the selections.
24013 this.locked = true;
24017 * Unlocks the selections.
24019 unlock : function(){
24020 this.locked = false;
24024 * Returns true if the selections are locked.
24025 * @return {Boolean}
24027 isLocked : function(){
24028 return this.locked;
24032 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24033 * @class Roo.bootstrap.Table.RowSelectionModel
24034 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24035 * It supports multiple selections and keyboard selection/navigation.
24037 * @param {Object} config
24040 Roo.bootstrap.Table.RowSelectionModel = function(config){
24041 Roo.apply(this, config);
24042 this.selections = new Roo.util.MixedCollection(false, function(o){
24047 this.lastActive = false;
24051 * @event selectionchange
24052 * Fires when the selection changes
24053 * @param {SelectionModel} this
24055 "selectionchange" : true,
24057 * @event afterselectionchange
24058 * Fires after the selection changes (eg. by key press or clicking)
24059 * @param {SelectionModel} this
24061 "afterselectionchange" : true,
24063 * @event beforerowselect
24064 * Fires when a row is selected being selected, return false to cancel.
24065 * @param {SelectionModel} this
24066 * @param {Number} rowIndex The selected index
24067 * @param {Boolean} keepExisting False if other selections will be cleared
24069 "beforerowselect" : true,
24072 * Fires when a row is selected.
24073 * @param {SelectionModel} this
24074 * @param {Number} rowIndex The selected index
24075 * @param {Roo.data.Record} r The record
24077 "rowselect" : true,
24079 * @event rowdeselect
24080 * Fires when a row is deselected.
24081 * @param {SelectionModel} this
24082 * @param {Number} rowIndex The selected index
24084 "rowdeselect" : true
24086 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24087 this.locked = false;
24090 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24092 * @cfg {Boolean} singleSelect
24093 * True to allow selection of only one row at a time (defaults to false)
24095 singleSelect : false,
24098 initEvents : function()
24101 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24102 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24103 //}else{ // allow click to work like normal
24104 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24106 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24107 this.grid.on("rowclick", this.handleMouseDown, this);
24109 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24110 "up" : function(e){
24112 this.selectPrevious(e.shiftKey);
24113 }else if(this.last !== false && this.lastActive !== false){
24114 var last = this.last;
24115 this.selectRange(this.last, this.lastActive-1);
24116 this.grid.getView().focusRow(this.lastActive);
24117 if(last !== false){
24121 this.selectFirstRow();
24123 this.fireEvent("afterselectionchange", this);
24125 "down" : function(e){
24127 this.selectNext(e.shiftKey);
24128 }else if(this.last !== false && this.lastActive !== false){
24129 var last = this.last;
24130 this.selectRange(this.last, this.lastActive+1);
24131 this.grid.getView().focusRow(this.lastActive);
24132 if(last !== false){
24136 this.selectFirstRow();
24138 this.fireEvent("afterselectionchange", this);
24142 this.grid.store.on('load', function(){
24143 this.selections.clear();
24146 var view = this.grid.view;
24147 view.on("refresh", this.onRefresh, this);
24148 view.on("rowupdated", this.onRowUpdated, this);
24149 view.on("rowremoved", this.onRemove, this);
24154 onRefresh : function()
24156 var ds = this.grid.store, i, v = this.grid.view;
24157 var s = this.selections;
24158 s.each(function(r){
24159 if((i = ds.indexOfId(r.id)) != -1){
24168 onRemove : function(v, index, r){
24169 this.selections.remove(r);
24173 onRowUpdated : function(v, index, r){
24174 if(this.isSelected(r)){
24175 v.onRowSelect(index);
24181 * @param {Array} records The records to select
24182 * @param {Boolean} keepExisting (optional) True to keep existing selections
24184 selectRecords : function(records, keepExisting)
24187 this.clearSelections();
24189 var ds = this.grid.store;
24190 for(var i = 0, len = records.length; i < len; i++){
24191 this.selectRow(ds.indexOf(records[i]), true);
24196 * Gets the number of selected rows.
24199 getCount : function(){
24200 return this.selections.length;
24204 * Selects the first row in the grid.
24206 selectFirstRow : function(){
24211 * Select the last row.
24212 * @param {Boolean} keepExisting (optional) True to keep existing selections
24214 selectLastRow : function(keepExisting){
24215 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24216 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24220 * Selects the row immediately following the last selected row.
24221 * @param {Boolean} keepExisting (optional) True to keep existing selections
24223 selectNext : function(keepExisting)
24225 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24226 this.selectRow(this.last+1, keepExisting);
24227 this.grid.getView().focusRow(this.last);
24232 * Selects the row that precedes the last selected row.
24233 * @param {Boolean} keepExisting (optional) True to keep existing selections
24235 selectPrevious : function(keepExisting){
24237 this.selectRow(this.last-1, keepExisting);
24238 this.grid.getView().focusRow(this.last);
24243 * Returns the selected records
24244 * @return {Array} Array of selected records
24246 getSelections : function(){
24247 return [].concat(this.selections.items);
24251 * Returns the first selected record.
24254 getSelected : function(){
24255 return this.selections.itemAt(0);
24260 * Clears all selections.
24262 clearSelections : function(fast)
24268 var ds = this.grid.store;
24269 var s = this.selections;
24270 s.each(function(r){
24271 this.deselectRow(ds.indexOfId(r.id));
24275 this.selections.clear();
24282 * Selects all rows.
24284 selectAll : function(){
24288 this.selections.clear();
24289 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24290 this.selectRow(i, true);
24295 * Returns True if there is a selection.
24296 * @return {Boolean}
24298 hasSelection : function(){
24299 return this.selections.length > 0;
24303 * Returns True if the specified row is selected.
24304 * @param {Number/Record} record The record or index of the record to check
24305 * @return {Boolean}
24307 isSelected : function(index){
24308 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24309 return (r && this.selections.key(r.id) ? true : false);
24313 * Returns True if the specified record id is selected.
24314 * @param {String} id The id of record to check
24315 * @return {Boolean}
24317 isIdSelected : function(id){
24318 return (this.selections.key(id) ? true : false);
24323 handleMouseDBClick : function(e, t){
24327 handleMouseDown : function(e, t)
24329 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24330 if(this.isLocked() || rowIndex < 0 ){
24333 if(e.shiftKey && this.last !== false){
24334 var last = this.last;
24335 this.selectRange(last, rowIndex, e.ctrlKey);
24336 this.last = last; // reset the last
24340 var isSelected = this.isSelected(rowIndex);
24341 //Roo.log("select row:" + rowIndex);
24343 this.deselectRow(rowIndex);
24345 this.selectRow(rowIndex, true);
24349 if(e.button !== 0 && isSelected){
24350 alert('rowIndex 2: ' + rowIndex);
24351 view.focusRow(rowIndex);
24352 }else if(e.ctrlKey && isSelected){
24353 this.deselectRow(rowIndex);
24354 }else if(!isSelected){
24355 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24356 view.focusRow(rowIndex);
24360 this.fireEvent("afterselectionchange", this);
24363 handleDragableRowClick : function(grid, rowIndex, e)
24365 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24366 this.selectRow(rowIndex, false);
24367 grid.view.focusRow(rowIndex);
24368 this.fireEvent("afterselectionchange", this);
24373 * Selects multiple rows.
24374 * @param {Array} rows Array of the indexes of the row to select
24375 * @param {Boolean} keepExisting (optional) True to keep existing selections
24377 selectRows : function(rows, keepExisting){
24379 this.clearSelections();
24381 for(var i = 0, len = rows.length; i < len; i++){
24382 this.selectRow(rows[i], true);
24387 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24388 * @param {Number} startRow The index of the first row in the range
24389 * @param {Number} endRow The index of the last row in the range
24390 * @param {Boolean} keepExisting (optional) True to retain existing selections
24392 selectRange : function(startRow, endRow, keepExisting){
24397 this.clearSelections();
24399 if(startRow <= endRow){
24400 for(var i = startRow; i <= endRow; i++){
24401 this.selectRow(i, true);
24404 for(var i = startRow; i >= endRow; i--){
24405 this.selectRow(i, true);
24411 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24412 * @param {Number} startRow The index of the first row in the range
24413 * @param {Number} endRow The index of the last row in the range
24415 deselectRange : function(startRow, endRow, preventViewNotify){
24419 for(var i = startRow; i <= endRow; i++){
24420 this.deselectRow(i, preventViewNotify);
24426 * @param {Number} row The index of the row to select
24427 * @param {Boolean} keepExisting (optional) True to keep existing selections
24429 selectRow : function(index, keepExisting, preventViewNotify)
24431 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24434 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24435 if(!keepExisting || this.singleSelect){
24436 this.clearSelections();
24439 var r = this.grid.store.getAt(index);
24440 //console.log('selectRow - record id :' + r.id);
24442 this.selections.add(r);
24443 this.last = this.lastActive = index;
24444 if(!preventViewNotify){
24445 var proxy = new Roo.Element(
24446 this.grid.getRowDom(index)
24448 proxy.addClass('bg-info info');
24450 this.fireEvent("rowselect", this, index, r);
24451 this.fireEvent("selectionchange", this);
24457 * @param {Number} row The index of the row to deselect
24459 deselectRow : function(index, preventViewNotify)
24464 if(this.last == index){
24467 if(this.lastActive == index){
24468 this.lastActive = false;
24471 var r = this.grid.store.getAt(index);
24476 this.selections.remove(r);
24477 //.console.log('deselectRow - record id :' + r.id);
24478 if(!preventViewNotify){
24480 var proxy = new Roo.Element(
24481 this.grid.getRowDom(index)
24483 proxy.removeClass('bg-info info');
24485 this.fireEvent("rowdeselect", this, index);
24486 this.fireEvent("selectionchange", this);
24490 restoreLast : function(){
24492 this.last = this._last;
24497 acceptsNav : function(row, col, cm){
24498 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24502 onEditorKey : function(field, e){
24503 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24508 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24510 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24512 }else if(k == e.ENTER && !e.ctrlKey){
24516 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24518 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24520 }else if(k == e.ESC){
24524 g.startEditing(newCell[0], newCell[1]);
24530 * Ext JS Library 1.1.1
24531 * Copyright(c) 2006-2007, Ext JS, LLC.
24533 * Originally Released Under LGPL - original licence link has changed is not relivant.
24536 * <script type="text/javascript">
24540 * @class Roo.bootstrap.PagingToolbar
24541 * @extends Roo.bootstrap.NavSimplebar
24542 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24544 * Create a new PagingToolbar
24545 * @param {Object} config The config object
24546 * @param {Roo.data.Store} store
24548 Roo.bootstrap.PagingToolbar = function(config)
24550 // old args format still supported... - xtype is prefered..
24551 // created from xtype...
24553 this.ds = config.dataSource;
24555 if (config.store && !this.ds) {
24556 this.store= Roo.factory(config.store, Roo.data);
24557 this.ds = this.store;
24558 this.ds.xmodule = this.xmodule || false;
24561 this.toolbarItems = [];
24562 if (config.items) {
24563 this.toolbarItems = config.items;
24566 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24571 this.bind(this.ds);
24574 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24578 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24580 * @cfg {Roo.data.Store} dataSource
24581 * The underlying data store providing the paged data
24584 * @cfg {String/HTMLElement/Element} container
24585 * container The id or element that will contain the toolbar
24588 * @cfg {Boolean} displayInfo
24589 * True to display the displayMsg (defaults to false)
24592 * @cfg {Number} pageSize
24593 * The number of records to display per page (defaults to 20)
24597 * @cfg {String} displayMsg
24598 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24600 displayMsg : 'Displaying {0} - {1} of {2}',
24602 * @cfg {String} emptyMsg
24603 * The message to display when no records are found (defaults to "No data to display")
24605 emptyMsg : 'No data to display',
24607 * Customizable piece of the default paging text (defaults to "Page")
24610 beforePageText : "Page",
24612 * Customizable piece of the default paging text (defaults to "of %0")
24615 afterPageText : "of {0}",
24617 * Customizable piece of the default paging text (defaults to "First Page")
24620 firstText : "First Page",
24622 * Customizable piece of the default paging text (defaults to "Previous Page")
24625 prevText : "Previous Page",
24627 * Customizable piece of the default paging text (defaults to "Next Page")
24630 nextText : "Next Page",
24632 * Customizable piece of the default paging text (defaults to "Last Page")
24635 lastText : "Last Page",
24637 * Customizable piece of the default paging text (defaults to "Refresh")
24640 refreshText : "Refresh",
24644 onRender : function(ct, position)
24646 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24647 this.navgroup.parentId = this.id;
24648 this.navgroup.onRender(this.el, null);
24649 // add the buttons to the navgroup
24651 if(this.displayInfo){
24652 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24653 this.displayEl = this.el.select('.x-paging-info', true).first();
24654 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24655 // this.displayEl = navel.el.select('span',true).first();
24661 Roo.each(_this.buttons, function(e){ // this might need to use render????
24662 Roo.factory(e).render(_this.el);
24666 Roo.each(_this.toolbarItems, function(e) {
24667 _this.navgroup.addItem(e);
24671 this.first = this.navgroup.addItem({
24672 tooltip: this.firstText,
24674 icon : 'fa fa-backward',
24676 preventDefault: true,
24677 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24680 this.prev = this.navgroup.addItem({
24681 tooltip: this.prevText,
24683 icon : 'fa fa-step-backward',
24685 preventDefault: true,
24686 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24688 //this.addSeparator();
24691 var field = this.navgroup.addItem( {
24693 cls : 'x-paging-position',
24695 html : this.beforePageText +
24696 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24697 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24700 this.field = field.el.select('input', true).first();
24701 this.field.on("keydown", this.onPagingKeydown, this);
24702 this.field.on("focus", function(){this.dom.select();});
24705 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24706 //this.field.setHeight(18);
24707 //this.addSeparator();
24708 this.next = this.navgroup.addItem({
24709 tooltip: this.nextText,
24711 html : ' <i class="fa fa-step-forward">',
24713 preventDefault: true,
24714 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24716 this.last = this.navgroup.addItem({
24717 tooltip: this.lastText,
24718 icon : 'fa fa-forward',
24721 preventDefault: true,
24722 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24724 //this.addSeparator();
24725 this.loading = this.navgroup.addItem({
24726 tooltip: this.refreshText,
24727 icon: 'fa fa-refresh',
24728 preventDefault: true,
24729 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24735 updateInfo : function(){
24736 if(this.displayEl){
24737 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24738 var msg = count == 0 ?
24742 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24744 this.displayEl.update(msg);
24749 onLoad : function(ds, r, o)
24751 this.cursor = o.params.start ? o.params.start : 0;
24753 var d = this.getPageData(),
24758 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24759 this.field.dom.value = ap;
24760 this.first.setDisabled(ap == 1);
24761 this.prev.setDisabled(ap == 1);
24762 this.next.setDisabled(ap == ps);
24763 this.last.setDisabled(ap == ps);
24764 this.loading.enable();
24769 getPageData : function(){
24770 var total = this.ds.getTotalCount();
24773 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24774 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24779 onLoadError : function(){
24780 this.loading.enable();
24784 onPagingKeydown : function(e){
24785 var k = e.getKey();
24786 var d = this.getPageData();
24788 var v = this.field.dom.value, pageNum;
24789 if(!v || isNaN(pageNum = parseInt(v, 10))){
24790 this.field.dom.value = d.activePage;
24793 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24794 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24797 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))
24799 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24800 this.field.dom.value = pageNum;
24801 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24804 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24806 var v = this.field.dom.value, pageNum;
24807 var increment = (e.shiftKey) ? 10 : 1;
24808 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24811 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24812 this.field.dom.value = d.activePage;
24815 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24817 this.field.dom.value = parseInt(v, 10) + increment;
24818 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24819 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24826 beforeLoad : function(){
24828 this.loading.disable();
24833 onClick : function(which){
24842 ds.load({params:{start: 0, limit: this.pageSize}});
24845 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24848 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24851 var total = ds.getTotalCount();
24852 var extra = total % this.pageSize;
24853 var lastStart = extra ? (total - extra) : total-this.pageSize;
24854 ds.load({params:{start: lastStart, limit: this.pageSize}});
24857 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24863 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24864 * @param {Roo.data.Store} store The data store to unbind
24866 unbind : function(ds){
24867 ds.un("beforeload", this.beforeLoad, this);
24868 ds.un("load", this.onLoad, this);
24869 ds.un("loadexception", this.onLoadError, this);
24870 ds.un("remove", this.updateInfo, this);
24871 ds.un("add", this.updateInfo, this);
24872 this.ds = undefined;
24876 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24877 * @param {Roo.data.Store} store The data store to bind
24879 bind : function(ds){
24880 ds.on("beforeload", this.beforeLoad, this);
24881 ds.on("load", this.onLoad, this);
24882 ds.on("loadexception", this.onLoadError, this);
24883 ds.on("remove", this.updateInfo, this);
24884 ds.on("add", this.updateInfo, this);
24895 * @class Roo.bootstrap.MessageBar
24896 * @extends Roo.bootstrap.Component
24897 * Bootstrap MessageBar class
24898 * @cfg {String} html contents of the MessageBar
24899 * @cfg {String} weight (info | success | warning | danger) default info
24900 * @cfg {String} beforeClass insert the bar before the given class
24901 * @cfg {Boolean} closable (true | false) default false
24902 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24905 * Create a new Element
24906 * @param {Object} config The config object
24909 Roo.bootstrap.MessageBar = function(config){
24910 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24913 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24919 beforeClass: 'bootstrap-sticky-wrap',
24921 getAutoCreate : function(){
24925 cls: 'alert alert-dismissable alert-' + this.weight,
24930 html: this.html || ''
24936 cfg.cls += ' alert-messages-fixed';
24950 onRender : function(ct, position)
24952 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24955 var cfg = Roo.apply({}, this.getAutoCreate());
24959 cfg.cls += ' ' + this.cls;
24962 cfg.style = this.style;
24964 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24966 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24969 this.el.select('>button.close').on('click', this.hide, this);
24975 if (!this.rendered) {
24981 this.fireEvent('show', this);
24987 if (!this.rendered) {
24993 this.fireEvent('hide', this);
24996 update : function()
24998 // var e = this.el.dom.firstChild;
25000 // if(this.closable){
25001 // e = e.nextSibling;
25004 // e.data = this.html || '';
25006 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25022 * @class Roo.bootstrap.Graph
25023 * @extends Roo.bootstrap.Component
25024 * Bootstrap Graph class
25028 @cfg {String} graphtype bar | vbar | pie
25029 @cfg {number} g_x coodinator | centre x (pie)
25030 @cfg {number} g_y coodinator | centre y (pie)
25031 @cfg {number} g_r radius (pie)
25032 @cfg {number} g_height height of the chart (respected by all elements in the set)
25033 @cfg {number} g_width width of the chart (respected by all elements in the set)
25034 @cfg {Object} title The title of the chart
25037 -opts (object) options for the chart
25039 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25040 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25042 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.
25043 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25045 o stretch (boolean)
25047 -opts (object) options for the pie
25050 o startAngle (number)
25051 o endAngle (number)
25055 * Create a new Input
25056 * @param {Object} config The config object
25059 Roo.bootstrap.Graph = function(config){
25060 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25066 * The img click event for the img.
25067 * @param {Roo.EventObject} e
25073 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25084 //g_colors: this.colors,
25091 getAutoCreate : function(){
25102 onRender : function(ct,position){
25105 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25107 if (typeof(Raphael) == 'undefined') {
25108 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25112 this.raphael = Raphael(this.el.dom);
25114 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25115 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25116 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25117 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25119 r.text(160, 10, "Single Series Chart").attr(txtattr);
25120 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25121 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25122 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25124 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25125 r.barchart(330, 10, 300, 220, data1);
25126 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25127 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25130 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25131 // r.barchart(30, 30, 560, 250, xdata, {
25132 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25133 // axis : "0 0 1 1",
25134 // axisxlabels : xdata
25135 // //yvalues : cols,
25138 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25140 // this.load(null,xdata,{
25141 // axis : "0 0 1 1",
25142 // axisxlabels : xdata
25147 load : function(graphtype,xdata,opts)
25149 this.raphael.clear();
25151 graphtype = this.graphtype;
25156 var r = this.raphael,
25157 fin = function () {
25158 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25160 fout = function () {
25161 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25163 pfin = function() {
25164 this.sector.stop();
25165 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25168 this.label[0].stop();
25169 this.label[0].attr({ r: 7.5 });
25170 this.label[1].attr({ "font-weight": 800 });
25173 pfout = function() {
25174 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25177 this.label[0].animate({ r: 5 }, 500, "bounce");
25178 this.label[1].attr({ "font-weight": 400 });
25184 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25187 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25190 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25191 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25193 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25200 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25205 setTitle: function(o)
25210 initEvents: function() {
25213 this.el.on('click', this.onClick, this);
25217 onClick : function(e)
25219 Roo.log('img onclick');
25220 this.fireEvent('click', this, e);
25232 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25235 * @class Roo.bootstrap.dash.NumberBox
25236 * @extends Roo.bootstrap.Component
25237 * Bootstrap NumberBox class
25238 * @cfg {String} headline Box headline
25239 * @cfg {String} content Box content
25240 * @cfg {String} icon Box icon
25241 * @cfg {String} footer Footer text
25242 * @cfg {String} fhref Footer href
25245 * Create a new NumberBox
25246 * @param {Object} config The config object
25250 Roo.bootstrap.dash.NumberBox = function(config){
25251 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25255 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25264 getAutoCreate : function(){
25268 cls : 'small-box ',
25276 cls : 'roo-headline',
25277 html : this.headline
25281 cls : 'roo-content',
25282 html : this.content
25296 cls : 'ion ' + this.icon
25305 cls : 'small-box-footer',
25306 href : this.fhref || '#',
25310 cfg.cn.push(footer);
25317 onRender : function(ct,position){
25318 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25325 setHeadline: function (value)
25327 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25330 setFooter: function (value, href)
25332 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25335 this.el.select('a.small-box-footer',true).first().attr('href', href);
25340 setContent: function (value)
25342 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25345 initEvents: function()
25359 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25362 * @class Roo.bootstrap.dash.TabBox
25363 * @extends Roo.bootstrap.Component
25364 * Bootstrap TabBox class
25365 * @cfg {String} title Title of the TabBox
25366 * @cfg {String} icon Icon of the TabBox
25367 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25368 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25371 * Create a new TabBox
25372 * @param {Object} config The config object
25376 Roo.bootstrap.dash.TabBox = function(config){
25377 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25382 * When a pane is added
25383 * @param {Roo.bootstrap.dash.TabPane} pane
25387 * @event activatepane
25388 * When a pane is activated
25389 * @param {Roo.bootstrap.dash.TabPane} pane
25391 "activatepane" : true
25399 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25404 tabScrollable : false,
25406 getChildContainer : function()
25408 return this.el.select('.tab-content', true).first();
25411 getAutoCreate : function(){
25415 cls: 'pull-left header',
25423 cls: 'fa ' + this.icon
25429 cls: 'nav nav-tabs pull-right',
25435 if(this.tabScrollable){
25442 cls: 'nav nav-tabs pull-right',
25453 cls: 'nav-tabs-custom',
25458 cls: 'tab-content no-padding',
25466 initEvents : function()
25468 //Roo.log('add add pane handler');
25469 this.on('addpane', this.onAddPane, this);
25472 * Updates the box title
25473 * @param {String} html to set the title to.
25475 setTitle : function(value)
25477 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25479 onAddPane : function(pane)
25481 this.panes.push(pane);
25482 //Roo.log('addpane');
25484 // tabs are rendere left to right..
25485 if(!this.showtabs){
25489 var ctr = this.el.select('.nav-tabs', true).first();
25492 var existing = ctr.select('.nav-tab',true);
25493 var qty = existing.getCount();;
25496 var tab = ctr.createChild({
25498 cls : 'nav-tab' + (qty ? '' : ' active'),
25506 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25509 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25511 pane.el.addClass('active');
25516 onTabClick : function(ev,un,ob,pane)
25518 //Roo.log('tab - prev default');
25519 ev.preventDefault();
25522 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25523 pane.tab.addClass('active');
25524 //Roo.log(pane.title);
25525 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25526 // technically we should have a deactivate event.. but maybe add later.
25527 // and it should not de-activate the selected tab...
25528 this.fireEvent('activatepane', pane);
25529 pane.el.addClass('active');
25530 pane.fireEvent('activate');
25535 getActivePane : function()
25538 Roo.each(this.panes, function(p) {
25539 if(p.el.hasClass('active')){
25560 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25562 * @class Roo.bootstrap.TabPane
25563 * @extends Roo.bootstrap.Component
25564 * Bootstrap TabPane class
25565 * @cfg {Boolean} active (false | true) Default false
25566 * @cfg {String} title title of panel
25570 * Create a new TabPane
25571 * @param {Object} config The config object
25574 Roo.bootstrap.dash.TabPane = function(config){
25575 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25581 * When a pane is activated
25582 * @param {Roo.bootstrap.dash.TabPane} pane
25589 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25594 // the tabBox that this is attached to.
25597 getAutoCreate : function()
25605 cfg.cls += ' active';
25610 initEvents : function()
25612 //Roo.log('trigger add pane handler');
25613 this.parent().fireEvent('addpane', this)
25617 * Updates the tab title
25618 * @param {String} html to set the title to.
25620 setTitle: function(str)
25626 this.tab.select('a', true).first().dom.innerHTML = str;
25643 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25646 * @class Roo.bootstrap.menu.Menu
25647 * @extends Roo.bootstrap.Component
25648 * Bootstrap Menu class - container for Menu
25649 * @cfg {String} html Text of the menu
25650 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25651 * @cfg {String} icon Font awesome icon
25652 * @cfg {String} pos Menu align to (top | bottom) default bottom
25656 * Create a new Menu
25657 * @param {Object} config The config object
25661 Roo.bootstrap.menu.Menu = function(config){
25662 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25666 * @event beforeshow
25667 * Fires before this menu is displayed
25668 * @param {Roo.bootstrap.menu.Menu} this
25672 * @event beforehide
25673 * Fires before this menu is hidden
25674 * @param {Roo.bootstrap.menu.Menu} this
25679 * Fires after this menu is displayed
25680 * @param {Roo.bootstrap.menu.Menu} this
25685 * Fires after this menu is hidden
25686 * @param {Roo.bootstrap.menu.Menu} this
25691 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25692 * @param {Roo.bootstrap.menu.Menu} this
25693 * @param {Roo.EventObject} e
25700 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25704 weight : 'default',
25709 getChildContainer : function() {
25710 if(this.isSubMenu){
25714 return this.el.select('ul.dropdown-menu', true).first();
25717 getAutoCreate : function()
25722 cls : 'roo-menu-text',
25730 cls : 'fa ' + this.icon
25741 cls : 'dropdown-button btn btn-' + this.weight,
25746 cls : 'dropdown-toggle btn btn-' + this.weight,
25756 cls : 'dropdown-menu'
25762 if(this.pos == 'top'){
25763 cfg.cls += ' dropup';
25766 if(this.isSubMenu){
25769 cls : 'dropdown-menu'
25776 onRender : function(ct, position)
25778 this.isSubMenu = ct.hasClass('dropdown-submenu');
25780 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25783 initEvents : function()
25785 if(this.isSubMenu){
25789 this.hidden = true;
25791 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25792 this.triggerEl.on('click', this.onTriggerPress, this);
25794 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25795 this.buttonEl.on('click', this.onClick, this);
25801 if(this.isSubMenu){
25805 return this.el.select('ul.dropdown-menu', true).first();
25808 onClick : function(e)
25810 this.fireEvent("click", this, e);
25813 onTriggerPress : function(e)
25815 if (this.isVisible()) {
25822 isVisible : function(){
25823 return !this.hidden;
25828 this.fireEvent("beforeshow", this);
25830 this.hidden = false;
25831 this.el.addClass('open');
25833 Roo.get(document).on("mouseup", this.onMouseUp, this);
25835 this.fireEvent("show", this);
25842 this.fireEvent("beforehide", this);
25844 this.hidden = true;
25845 this.el.removeClass('open');
25847 Roo.get(document).un("mouseup", this.onMouseUp);
25849 this.fireEvent("hide", this);
25852 onMouseUp : function()
25866 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25869 * @class Roo.bootstrap.menu.Item
25870 * @extends Roo.bootstrap.Component
25871 * Bootstrap MenuItem class
25872 * @cfg {Boolean} submenu (true | false) default false
25873 * @cfg {String} html text of the item
25874 * @cfg {String} href the link
25875 * @cfg {Boolean} disable (true | false) default false
25876 * @cfg {Boolean} preventDefault (true | false) default true
25877 * @cfg {String} icon Font awesome icon
25878 * @cfg {String} pos Submenu align to (left | right) default right
25882 * Create a new Item
25883 * @param {Object} config The config object
25887 Roo.bootstrap.menu.Item = function(config){
25888 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25892 * Fires when the mouse is hovering over this menu
25893 * @param {Roo.bootstrap.menu.Item} this
25894 * @param {Roo.EventObject} e
25899 * Fires when the mouse exits this menu
25900 * @param {Roo.bootstrap.menu.Item} this
25901 * @param {Roo.EventObject} e
25907 * The raw click event for the entire grid.
25908 * @param {Roo.EventObject} e
25914 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25919 preventDefault: true,
25924 getAutoCreate : function()
25929 cls : 'roo-menu-item-text',
25937 cls : 'fa ' + this.icon
25946 href : this.href || '#',
25953 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25957 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25959 if(this.pos == 'left'){
25960 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25967 initEvents : function()
25969 this.el.on('mouseover', this.onMouseOver, this);
25970 this.el.on('mouseout', this.onMouseOut, this);
25972 this.el.select('a', true).first().on('click', this.onClick, this);
25976 onClick : function(e)
25978 if(this.preventDefault){
25979 e.preventDefault();
25982 this.fireEvent("click", this, e);
25985 onMouseOver : function(e)
25987 if(this.submenu && this.pos == 'left'){
25988 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25991 this.fireEvent("mouseover", this, e);
25994 onMouseOut : function(e)
25996 this.fireEvent("mouseout", this, e);
26008 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26011 * @class Roo.bootstrap.menu.Separator
26012 * @extends Roo.bootstrap.Component
26013 * Bootstrap Separator class
26016 * Create a new Separator
26017 * @param {Object} config The config object
26021 Roo.bootstrap.menu.Separator = function(config){
26022 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26025 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26027 getAutoCreate : function(){
26048 * @class Roo.bootstrap.Tooltip
26049 * Bootstrap Tooltip class
26050 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26051 * to determine which dom element triggers the tooltip.
26053 * It needs to add support for additional attributes like tooltip-position
26056 * Create a new Toolti
26057 * @param {Object} config The config object
26060 Roo.bootstrap.Tooltip = function(config){
26061 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26063 this.alignment = Roo.bootstrap.Tooltip.alignment;
26065 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26066 this.alignment = config.alignment;
26071 Roo.apply(Roo.bootstrap.Tooltip, {
26073 * @function init initialize tooltip monitoring.
26077 currentTip : false,
26078 currentRegion : false,
26084 Roo.get(document).on('mouseover', this.enter ,this);
26085 Roo.get(document).on('mouseout', this.leave, this);
26088 this.currentTip = new Roo.bootstrap.Tooltip();
26091 enter : function(ev)
26093 var dom = ev.getTarget();
26095 //Roo.log(['enter',dom]);
26096 var el = Roo.fly(dom);
26097 if (this.currentEl) {
26099 //Roo.log(this.currentEl);
26100 //Roo.log(this.currentEl.contains(dom));
26101 if (this.currentEl == el) {
26104 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26110 if (this.currentTip.el) {
26111 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26115 if(!el || el.dom == document){
26121 // you can not look for children, as if el is the body.. then everythign is the child..
26122 if (!el.attr('tooltip')) { //
26123 if (!el.select("[tooltip]").elements.length) {
26126 // is the mouse over this child...?
26127 bindEl = el.select("[tooltip]").first();
26128 var xy = ev.getXY();
26129 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26130 //Roo.log("not in region.");
26133 //Roo.log("child element over..");
26136 this.currentEl = bindEl;
26137 this.currentTip.bind(bindEl);
26138 this.currentRegion = Roo.lib.Region.getRegion(dom);
26139 this.currentTip.enter();
26142 leave : function(ev)
26144 var dom = ev.getTarget();
26145 //Roo.log(['leave',dom]);
26146 if (!this.currentEl) {
26151 if (dom != this.currentEl.dom) {
26154 var xy = ev.getXY();
26155 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26158 // only activate leave if mouse cursor is outside... bounding box..
26163 if (this.currentTip) {
26164 this.currentTip.leave();
26166 //Roo.log('clear currentEl');
26167 this.currentEl = false;
26172 'left' : ['r-l', [-2,0], 'right'],
26173 'right' : ['l-r', [2,0], 'left'],
26174 'bottom' : ['t-b', [0,2], 'top'],
26175 'top' : [ 'b-t', [0,-2], 'bottom']
26181 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26186 delay : null, // can be { show : 300 , hide: 500}
26190 hoverState : null, //???
26192 placement : 'bottom',
26196 getAutoCreate : function(){
26203 cls : 'tooltip-arrow'
26206 cls : 'tooltip-inner'
26213 bind : function(el)
26219 enter : function () {
26221 if (this.timeout != null) {
26222 clearTimeout(this.timeout);
26225 this.hoverState = 'in';
26226 //Roo.log("enter - show");
26227 if (!this.delay || !this.delay.show) {
26232 this.timeout = setTimeout(function () {
26233 if (_t.hoverState == 'in') {
26236 }, this.delay.show);
26240 clearTimeout(this.timeout);
26242 this.hoverState = 'out';
26243 if (!this.delay || !this.delay.hide) {
26249 this.timeout = setTimeout(function () {
26250 //Roo.log("leave - timeout");
26252 if (_t.hoverState == 'out') {
26254 Roo.bootstrap.Tooltip.currentEl = false;
26259 show : function (msg)
26262 this.render(document.body);
26265 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26267 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26269 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26271 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26273 var placement = typeof this.placement == 'function' ?
26274 this.placement.call(this, this.el, on_el) :
26277 var autoToken = /\s?auto?\s?/i;
26278 var autoPlace = autoToken.test(placement);
26280 placement = placement.replace(autoToken, '') || 'top';
26284 //this.el.setXY([0,0]);
26286 //this.el.dom.style.display='block';
26288 //this.el.appendTo(on_el);
26290 var p = this.getPosition();
26291 var box = this.el.getBox();
26297 var align = this.alignment[placement];
26299 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26301 if(placement == 'top' || placement == 'bottom'){
26303 placement = 'right';
26306 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26307 placement = 'left';
26310 var scroll = Roo.select('body', true).first().getScroll();
26312 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26316 align = this.alignment[placement];
26319 this.el.alignTo(this.bindEl, align[0],align[1]);
26320 //var arrow = this.el.select('.arrow',true).first();
26321 //arrow.set(align[2],
26323 this.el.addClass(placement);
26325 this.el.addClass('in fade');
26327 this.hoverState = null;
26329 if (this.el.hasClass('fade')) {
26340 //this.el.setXY([0,0]);
26341 this.el.removeClass('in');
26357 * @class Roo.bootstrap.LocationPicker
26358 * @extends Roo.bootstrap.Component
26359 * Bootstrap LocationPicker class
26360 * @cfg {Number} latitude Position when init default 0
26361 * @cfg {Number} longitude Position when init default 0
26362 * @cfg {Number} zoom default 15
26363 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26364 * @cfg {Boolean} mapTypeControl default false
26365 * @cfg {Boolean} disableDoubleClickZoom default false
26366 * @cfg {Boolean} scrollwheel default true
26367 * @cfg {Boolean} streetViewControl default false
26368 * @cfg {Number} radius default 0
26369 * @cfg {String} locationName
26370 * @cfg {Boolean} draggable default true
26371 * @cfg {Boolean} enableAutocomplete default false
26372 * @cfg {Boolean} enableReverseGeocode default true
26373 * @cfg {String} markerTitle
26376 * Create a new LocationPicker
26377 * @param {Object} config The config object
26381 Roo.bootstrap.LocationPicker = function(config){
26383 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26388 * Fires when the picker initialized.
26389 * @param {Roo.bootstrap.LocationPicker} this
26390 * @param {Google Location} location
26394 * @event positionchanged
26395 * Fires when the picker position changed.
26396 * @param {Roo.bootstrap.LocationPicker} this
26397 * @param {Google Location} location
26399 positionchanged : true,
26402 * Fires when the map resize.
26403 * @param {Roo.bootstrap.LocationPicker} this
26408 * Fires when the map show.
26409 * @param {Roo.bootstrap.LocationPicker} this
26414 * Fires when the map hide.
26415 * @param {Roo.bootstrap.LocationPicker} this
26420 * Fires when click the map.
26421 * @param {Roo.bootstrap.LocationPicker} this
26422 * @param {Map event} e
26426 * @event mapRightClick
26427 * Fires when right click the map.
26428 * @param {Roo.bootstrap.LocationPicker} this
26429 * @param {Map event} e
26431 mapRightClick : true,
26433 * @event markerClick
26434 * Fires when click the marker.
26435 * @param {Roo.bootstrap.LocationPicker} this
26436 * @param {Map event} e
26438 markerClick : true,
26440 * @event markerRightClick
26441 * Fires when right click the marker.
26442 * @param {Roo.bootstrap.LocationPicker} this
26443 * @param {Map event} e
26445 markerRightClick : true,
26447 * @event OverlayViewDraw
26448 * Fires when OverlayView Draw
26449 * @param {Roo.bootstrap.LocationPicker} this
26451 OverlayViewDraw : true,
26453 * @event OverlayViewOnAdd
26454 * Fires when OverlayView Draw
26455 * @param {Roo.bootstrap.LocationPicker} this
26457 OverlayViewOnAdd : true,
26459 * @event OverlayViewOnRemove
26460 * Fires when OverlayView Draw
26461 * @param {Roo.bootstrap.LocationPicker} this
26463 OverlayViewOnRemove : true,
26465 * @event OverlayViewShow
26466 * Fires when OverlayView Draw
26467 * @param {Roo.bootstrap.LocationPicker} this
26468 * @param {Pixel} cpx
26470 OverlayViewShow : true,
26472 * @event OverlayViewHide
26473 * Fires when OverlayView Draw
26474 * @param {Roo.bootstrap.LocationPicker} this
26476 OverlayViewHide : true,
26478 * @event loadexception
26479 * Fires when load google lib failed.
26480 * @param {Roo.bootstrap.LocationPicker} this
26482 loadexception : true
26487 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26489 gMapContext: false,
26495 mapTypeControl: false,
26496 disableDoubleClickZoom: false,
26498 streetViewControl: false,
26502 enableAutocomplete: false,
26503 enableReverseGeocode: true,
26506 getAutoCreate: function()
26511 cls: 'roo-location-picker'
26517 initEvents: function(ct, position)
26519 if(!this.el.getWidth() || this.isApplied()){
26523 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26528 initial: function()
26530 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26531 this.fireEvent('loadexception', this);
26535 if(!this.mapTypeId){
26536 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26539 this.gMapContext = this.GMapContext();
26541 this.initOverlayView();
26543 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26547 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26548 _this.setPosition(_this.gMapContext.marker.position);
26551 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26552 _this.fireEvent('mapClick', this, event);
26556 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26557 _this.fireEvent('mapRightClick', this, event);
26561 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26562 _this.fireEvent('markerClick', this, event);
26566 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26567 _this.fireEvent('markerRightClick', this, event);
26571 this.setPosition(this.gMapContext.location);
26573 this.fireEvent('initial', this, this.gMapContext.location);
26576 initOverlayView: function()
26580 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26584 _this.fireEvent('OverlayViewDraw', _this);
26589 _this.fireEvent('OverlayViewOnAdd', _this);
26592 onRemove: function()
26594 _this.fireEvent('OverlayViewOnRemove', _this);
26597 show: function(cpx)
26599 _this.fireEvent('OverlayViewShow', _this, cpx);
26604 _this.fireEvent('OverlayViewHide', _this);
26610 fromLatLngToContainerPixel: function(event)
26612 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26615 isApplied: function()
26617 return this.getGmapContext() == false ? false : true;
26620 getGmapContext: function()
26622 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26625 GMapContext: function()
26627 var position = new google.maps.LatLng(this.latitude, this.longitude);
26629 var _map = new google.maps.Map(this.el.dom, {
26632 mapTypeId: this.mapTypeId,
26633 mapTypeControl: this.mapTypeControl,
26634 disableDoubleClickZoom: this.disableDoubleClickZoom,
26635 scrollwheel: this.scrollwheel,
26636 streetViewControl: this.streetViewControl,
26637 locationName: this.locationName,
26638 draggable: this.draggable,
26639 enableAutocomplete: this.enableAutocomplete,
26640 enableReverseGeocode: this.enableReverseGeocode
26643 var _marker = new google.maps.Marker({
26644 position: position,
26646 title: this.markerTitle,
26647 draggable: this.draggable
26654 location: position,
26655 radius: this.radius,
26656 locationName: this.locationName,
26657 addressComponents: {
26658 formatted_address: null,
26659 addressLine1: null,
26660 addressLine2: null,
26662 streetNumber: null,
26666 stateOrProvince: null
26669 domContainer: this.el.dom,
26670 geodecoder: new google.maps.Geocoder()
26674 drawCircle: function(center, radius, options)
26676 if (this.gMapContext.circle != null) {
26677 this.gMapContext.circle.setMap(null);
26681 options = Roo.apply({}, options, {
26682 strokeColor: "#0000FF",
26683 strokeOpacity: .35,
26685 fillColor: "#0000FF",
26689 options.map = this.gMapContext.map;
26690 options.radius = radius;
26691 options.center = center;
26692 this.gMapContext.circle = new google.maps.Circle(options);
26693 return this.gMapContext.circle;
26699 setPosition: function(location)
26701 this.gMapContext.location = location;
26702 this.gMapContext.marker.setPosition(location);
26703 this.gMapContext.map.panTo(location);
26704 this.drawCircle(location, this.gMapContext.radius, {});
26708 if (this.gMapContext.settings.enableReverseGeocode) {
26709 this.gMapContext.geodecoder.geocode({
26710 latLng: this.gMapContext.location
26711 }, function(results, status) {
26713 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26714 _this.gMapContext.locationName = results[0].formatted_address;
26715 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26717 _this.fireEvent('positionchanged', this, location);
26724 this.fireEvent('positionchanged', this, location);
26729 google.maps.event.trigger(this.gMapContext.map, "resize");
26731 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26733 this.fireEvent('resize', this);
26736 setPositionByLatLng: function(latitude, longitude)
26738 this.setPosition(new google.maps.LatLng(latitude, longitude));
26741 getCurrentPosition: function()
26744 latitude: this.gMapContext.location.lat(),
26745 longitude: this.gMapContext.location.lng()
26749 getAddressName: function()
26751 return this.gMapContext.locationName;
26754 getAddressComponents: function()
26756 return this.gMapContext.addressComponents;
26759 address_component_from_google_geocode: function(address_components)
26763 for (var i = 0; i < address_components.length; i++) {
26764 var component = address_components[i];
26765 if (component.types.indexOf("postal_code") >= 0) {
26766 result.postalCode = component.short_name;
26767 } else if (component.types.indexOf("street_number") >= 0) {
26768 result.streetNumber = component.short_name;
26769 } else if (component.types.indexOf("route") >= 0) {
26770 result.streetName = component.short_name;
26771 } else if (component.types.indexOf("neighborhood") >= 0) {
26772 result.city = component.short_name;
26773 } else if (component.types.indexOf("locality") >= 0) {
26774 result.city = component.short_name;
26775 } else if (component.types.indexOf("sublocality") >= 0) {
26776 result.district = component.short_name;
26777 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26778 result.stateOrProvince = component.short_name;
26779 } else if (component.types.indexOf("country") >= 0) {
26780 result.country = component.short_name;
26784 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26785 result.addressLine2 = "";
26789 setZoomLevel: function(zoom)
26791 this.gMapContext.map.setZoom(zoom);
26804 this.fireEvent('show', this);
26815 this.fireEvent('hide', this);
26820 Roo.apply(Roo.bootstrap.LocationPicker, {
26822 OverlayView : function(map, options)
26824 options = options || {};
26838 * @class Roo.bootstrap.Alert
26839 * @extends Roo.bootstrap.Component
26840 * Bootstrap Alert class
26841 * @cfg {String} title The title of alert
26842 * @cfg {String} html The content of alert
26843 * @cfg {String} weight ( success | info | warning | danger )
26844 * @cfg {String} faicon font-awesomeicon
26847 * Create a new alert
26848 * @param {Object} config The config object
26852 Roo.bootstrap.Alert = function(config){
26853 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26857 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26864 getAutoCreate : function()
26873 cls : 'roo-alert-icon'
26878 cls : 'roo-alert-title',
26883 cls : 'roo-alert-text',
26890 cfg.cn[0].cls += ' fa ' + this.faicon;
26894 cfg.cls += ' alert-' + this.weight;
26900 initEvents: function()
26902 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26905 setTitle : function(str)
26907 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26910 setText : function(str)
26912 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26915 setWeight : function(weight)
26918 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26921 this.weight = weight;
26923 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26926 setIcon : function(icon)
26929 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26932 this.faicon = icon;
26934 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26955 * @class Roo.bootstrap.UploadCropbox
26956 * @extends Roo.bootstrap.Component
26957 * Bootstrap UploadCropbox class
26958 * @cfg {String} emptyText show when image has been loaded
26959 * @cfg {String} rotateNotify show when image too small to rotate
26960 * @cfg {Number} errorTimeout default 3000
26961 * @cfg {Number} minWidth default 300
26962 * @cfg {Number} minHeight default 300
26963 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26964 * @cfg {Boolean} isDocument (true|false) default false
26965 * @cfg {String} url action url
26966 * @cfg {String} paramName default 'imageUpload'
26967 * @cfg {String} method default POST
26968 * @cfg {Boolean} loadMask (true|false) default true
26969 * @cfg {Boolean} loadingText default 'Loading...'
26972 * Create a new UploadCropbox
26973 * @param {Object} config The config object
26976 Roo.bootstrap.UploadCropbox = function(config){
26977 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26981 * @event beforeselectfile
26982 * Fire before select file
26983 * @param {Roo.bootstrap.UploadCropbox} this
26985 "beforeselectfile" : true,
26988 * Fire after initEvent
26989 * @param {Roo.bootstrap.UploadCropbox} this
26994 * Fire after initEvent
26995 * @param {Roo.bootstrap.UploadCropbox} this
26996 * @param {String} data
27001 * Fire when preparing the file data
27002 * @param {Roo.bootstrap.UploadCropbox} this
27003 * @param {Object} file
27008 * Fire when get exception
27009 * @param {Roo.bootstrap.UploadCropbox} this
27010 * @param {XMLHttpRequest} xhr
27012 "exception" : true,
27014 * @event beforeloadcanvas
27015 * Fire before load the canvas
27016 * @param {Roo.bootstrap.UploadCropbox} this
27017 * @param {String} src
27019 "beforeloadcanvas" : true,
27022 * Fire when trash image
27023 * @param {Roo.bootstrap.UploadCropbox} this
27028 * Fire when download the image
27029 * @param {Roo.bootstrap.UploadCropbox} this
27033 * @event footerbuttonclick
27034 * Fire when footerbuttonclick
27035 * @param {Roo.bootstrap.UploadCropbox} this
27036 * @param {String} type
27038 "footerbuttonclick" : true,
27042 * @param {Roo.bootstrap.UploadCropbox} this
27047 * Fire when rotate the image
27048 * @param {Roo.bootstrap.UploadCropbox} this
27049 * @param {String} pos
27054 * Fire when inspect the file
27055 * @param {Roo.bootstrap.UploadCropbox} this
27056 * @param {Object} file
27061 * Fire when xhr upload the file
27062 * @param {Roo.bootstrap.UploadCropbox} this
27063 * @param {Object} data
27068 * Fire when arrange the file data
27069 * @param {Roo.bootstrap.UploadCropbox} this
27070 * @param {Object} formData
27075 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27078 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27080 emptyText : 'Click to upload image',
27081 rotateNotify : 'Image is too small to rotate',
27082 errorTimeout : 3000,
27096 cropType : 'image/jpeg',
27098 canvasLoaded : false,
27099 isDocument : false,
27101 paramName : 'imageUpload',
27103 loadingText : 'Loading...',
27106 getAutoCreate : function()
27110 cls : 'roo-upload-cropbox',
27114 cls : 'roo-upload-cropbox-selector',
27119 cls : 'roo-upload-cropbox-body',
27120 style : 'cursor:pointer',
27124 cls : 'roo-upload-cropbox-preview'
27128 cls : 'roo-upload-cropbox-thumb'
27132 cls : 'roo-upload-cropbox-empty-notify',
27133 html : this.emptyText
27137 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27138 html : this.rotateNotify
27144 cls : 'roo-upload-cropbox-footer',
27147 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27157 onRender : function(ct, position)
27159 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27161 if (this.buttons.length) {
27163 Roo.each(this.buttons, function(bb) {
27165 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27167 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27173 this.maskEl = this.el;
27177 initEvents : function()
27179 this.urlAPI = (window.createObjectURL && window) ||
27180 (window.URL && URL.revokeObjectURL && URL) ||
27181 (window.webkitURL && webkitURL);
27183 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27184 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27186 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27187 this.selectorEl.hide();
27189 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27190 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27192 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27193 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27194 this.thumbEl.hide();
27196 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27197 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27199 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27200 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27201 this.errorEl.hide();
27203 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27204 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27205 this.footerEl.hide();
27207 this.setThumbBoxSize();
27213 this.fireEvent('initial', this);
27220 window.addEventListener("resize", function() { _this.resize(); } );
27222 this.bodyEl.on('click', this.beforeSelectFile, this);
27225 this.bodyEl.on('touchstart', this.onTouchStart, this);
27226 this.bodyEl.on('touchmove', this.onTouchMove, this);
27227 this.bodyEl.on('touchend', this.onTouchEnd, this);
27231 this.bodyEl.on('mousedown', this.onMouseDown, this);
27232 this.bodyEl.on('mousemove', this.onMouseMove, this);
27233 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27234 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27235 Roo.get(document).on('mouseup', this.onMouseUp, this);
27238 this.selectorEl.on('change', this.onFileSelected, this);
27244 this.baseScale = 1;
27246 this.baseRotate = 1;
27247 this.dragable = false;
27248 this.pinching = false;
27251 this.cropData = false;
27252 this.notifyEl.dom.innerHTML = this.emptyText;
27254 this.selectorEl.dom.value = '';
27258 resize : function()
27260 if(this.fireEvent('resize', this) != false){
27261 this.setThumbBoxPosition();
27262 this.setCanvasPosition();
27266 onFooterButtonClick : function(e, el, o, type)
27269 case 'rotate-left' :
27270 this.onRotateLeft(e);
27272 case 'rotate-right' :
27273 this.onRotateRight(e);
27276 this.beforeSelectFile(e);
27291 this.fireEvent('footerbuttonclick', this, type);
27294 beforeSelectFile : function(e)
27296 e.preventDefault();
27298 if(this.fireEvent('beforeselectfile', this) != false){
27299 this.selectorEl.dom.click();
27303 onFileSelected : function(e)
27305 e.preventDefault();
27307 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27311 var file = this.selectorEl.dom.files[0];
27313 if(this.fireEvent('inspect', this, file) != false){
27314 this.prepare(file);
27319 trash : function(e)
27321 this.fireEvent('trash', this);
27324 download : function(e)
27326 this.fireEvent('download', this);
27329 loadCanvas : function(src)
27331 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27335 this.imageEl = document.createElement('img');
27339 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27341 this.imageEl.src = src;
27345 onLoadCanvas : function()
27347 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27348 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27350 this.bodyEl.un('click', this.beforeSelectFile, this);
27352 this.notifyEl.hide();
27353 this.thumbEl.show();
27354 this.footerEl.show();
27356 this.baseRotateLevel();
27358 if(this.isDocument){
27359 this.setThumbBoxSize();
27362 this.setThumbBoxPosition();
27364 this.baseScaleLevel();
27370 this.canvasLoaded = true;
27373 this.maskEl.unmask();
27378 setCanvasPosition : function()
27380 if(!this.canvasEl){
27384 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27385 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27387 this.previewEl.setLeft(pw);
27388 this.previewEl.setTop(ph);
27392 onMouseDown : function(e)
27396 this.dragable = true;
27397 this.pinching = false;
27399 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27400 this.dragable = false;
27404 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27405 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27409 onMouseMove : function(e)
27413 if(!this.canvasLoaded){
27417 if (!this.dragable){
27421 var minX = Math.ceil(this.thumbEl.getLeft(true));
27422 var minY = Math.ceil(this.thumbEl.getTop(true));
27424 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27425 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27427 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27428 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27430 x = x - this.mouseX;
27431 y = y - this.mouseY;
27433 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27434 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27436 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27437 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27439 this.previewEl.setLeft(bgX);
27440 this.previewEl.setTop(bgY);
27442 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27443 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27446 onMouseUp : function(e)
27450 this.dragable = false;
27453 onMouseWheel : function(e)
27457 this.startScale = this.scale;
27459 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27461 if(!this.zoomable()){
27462 this.scale = this.startScale;
27471 zoomable : function()
27473 var minScale = this.thumbEl.getWidth() / this.minWidth;
27475 if(this.minWidth < this.minHeight){
27476 minScale = this.thumbEl.getHeight() / this.minHeight;
27479 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27480 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27484 (this.rotate == 0 || this.rotate == 180) &&
27486 width > this.imageEl.OriginWidth ||
27487 height > this.imageEl.OriginHeight ||
27488 (width < this.minWidth && height < this.minHeight)
27496 (this.rotate == 90 || this.rotate == 270) &&
27498 width > this.imageEl.OriginWidth ||
27499 height > this.imageEl.OriginHeight ||
27500 (width < this.minHeight && height < this.minWidth)
27507 !this.isDocument &&
27508 (this.rotate == 0 || this.rotate == 180) &&
27510 width < this.minWidth ||
27511 width > this.imageEl.OriginWidth ||
27512 height < this.minHeight ||
27513 height > this.imageEl.OriginHeight
27520 !this.isDocument &&
27521 (this.rotate == 90 || this.rotate == 270) &&
27523 width < this.minHeight ||
27524 width > this.imageEl.OriginWidth ||
27525 height < this.minWidth ||
27526 height > this.imageEl.OriginHeight
27536 onRotateLeft : function(e)
27538 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27540 var minScale = this.thumbEl.getWidth() / this.minWidth;
27542 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27543 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27545 this.startScale = this.scale;
27547 while (this.getScaleLevel() < minScale){
27549 this.scale = this.scale + 1;
27551 if(!this.zoomable()){
27556 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27557 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27562 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27569 this.scale = this.startScale;
27571 this.onRotateFail();
27576 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27578 if(this.isDocument){
27579 this.setThumbBoxSize();
27580 this.setThumbBoxPosition();
27581 this.setCanvasPosition();
27586 this.fireEvent('rotate', this, 'left');
27590 onRotateRight : function(e)
27592 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27594 var minScale = this.thumbEl.getWidth() / this.minWidth;
27596 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27597 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27599 this.startScale = this.scale;
27601 while (this.getScaleLevel() < minScale){
27603 this.scale = this.scale + 1;
27605 if(!this.zoomable()){
27610 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27611 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27616 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27623 this.scale = this.startScale;
27625 this.onRotateFail();
27630 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27632 if(this.isDocument){
27633 this.setThumbBoxSize();
27634 this.setThumbBoxPosition();
27635 this.setCanvasPosition();
27640 this.fireEvent('rotate', this, 'right');
27643 onRotateFail : function()
27645 this.errorEl.show(true);
27649 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27654 this.previewEl.dom.innerHTML = '';
27656 var canvasEl = document.createElement("canvas");
27658 var contextEl = canvasEl.getContext("2d");
27660 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27661 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27662 var center = this.imageEl.OriginWidth / 2;
27664 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27665 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27666 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27667 center = this.imageEl.OriginHeight / 2;
27670 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27672 contextEl.translate(center, center);
27673 contextEl.rotate(this.rotate * Math.PI / 180);
27675 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27677 this.canvasEl = document.createElement("canvas");
27679 this.contextEl = this.canvasEl.getContext("2d");
27681 switch (this.rotate) {
27684 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27685 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27687 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27692 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27693 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27695 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27696 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);
27700 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27705 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27706 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27708 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27709 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);
27713 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);
27718 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27719 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27721 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27722 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27726 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);
27733 this.previewEl.appendChild(this.canvasEl);
27735 this.setCanvasPosition();
27740 if(!this.canvasLoaded){
27744 var imageCanvas = document.createElement("canvas");
27746 var imageContext = imageCanvas.getContext("2d");
27748 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27749 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27751 var center = imageCanvas.width / 2;
27753 imageContext.translate(center, center);
27755 imageContext.rotate(this.rotate * Math.PI / 180);
27757 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27759 var canvas = document.createElement("canvas");
27761 var context = canvas.getContext("2d");
27763 canvas.width = this.minWidth;
27764 canvas.height = this.minHeight;
27766 switch (this.rotate) {
27769 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27770 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27772 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27773 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27775 var targetWidth = this.minWidth - 2 * x;
27776 var targetHeight = this.minHeight - 2 * y;
27780 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27781 scale = targetWidth / width;
27784 if(x > 0 && y == 0){
27785 scale = targetHeight / height;
27788 if(x > 0 && y > 0){
27789 scale = targetWidth / width;
27791 if(width < height){
27792 scale = targetHeight / height;
27796 context.scale(scale, scale);
27798 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27799 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27801 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27802 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27804 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27809 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27810 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27812 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27813 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27815 var targetWidth = this.minWidth - 2 * x;
27816 var targetHeight = this.minHeight - 2 * y;
27820 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27821 scale = targetWidth / width;
27824 if(x > 0 && y == 0){
27825 scale = targetHeight / height;
27828 if(x > 0 && y > 0){
27829 scale = targetWidth / width;
27831 if(width < height){
27832 scale = targetHeight / height;
27836 context.scale(scale, scale);
27838 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27839 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27841 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27842 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27844 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27846 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27851 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27852 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27854 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27855 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27857 var targetWidth = this.minWidth - 2 * x;
27858 var targetHeight = this.minHeight - 2 * y;
27862 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27863 scale = targetWidth / width;
27866 if(x > 0 && y == 0){
27867 scale = targetHeight / height;
27870 if(x > 0 && y > 0){
27871 scale = targetWidth / width;
27873 if(width < height){
27874 scale = targetHeight / height;
27878 context.scale(scale, scale);
27880 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27881 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27883 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27884 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27886 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27887 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27889 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27894 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27895 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27897 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27898 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27900 var targetWidth = this.minWidth - 2 * x;
27901 var targetHeight = this.minHeight - 2 * y;
27905 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27906 scale = targetWidth / width;
27909 if(x > 0 && y == 0){
27910 scale = targetHeight / height;
27913 if(x > 0 && y > 0){
27914 scale = targetWidth / width;
27916 if(width < height){
27917 scale = targetHeight / height;
27921 context.scale(scale, scale);
27923 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27924 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27926 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27927 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27929 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27931 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27938 this.cropData = canvas.toDataURL(this.cropType);
27940 if(this.fireEvent('crop', this, this.cropData) !== false){
27941 this.process(this.file, this.cropData);
27948 setThumbBoxSize : function()
27952 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27953 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27954 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27956 this.minWidth = width;
27957 this.minHeight = height;
27959 if(this.rotate == 90 || this.rotate == 270){
27960 this.minWidth = height;
27961 this.minHeight = width;
27966 width = Math.ceil(this.minWidth * height / this.minHeight);
27968 if(this.minWidth > this.minHeight){
27970 height = Math.ceil(this.minHeight * width / this.minWidth);
27973 this.thumbEl.setStyle({
27974 width : width + 'px',
27975 height : height + 'px'
27982 setThumbBoxPosition : function()
27984 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27985 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27987 this.thumbEl.setLeft(x);
27988 this.thumbEl.setTop(y);
27992 baseRotateLevel : function()
27994 this.baseRotate = 1;
27997 typeof(this.exif) != 'undefined' &&
27998 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27999 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28001 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28004 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28008 baseScaleLevel : function()
28012 if(this.isDocument){
28014 if(this.baseRotate == 6 || this.baseRotate == 8){
28016 height = this.thumbEl.getHeight();
28017 this.baseScale = height / this.imageEl.OriginWidth;
28019 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28020 width = this.thumbEl.getWidth();
28021 this.baseScale = width / this.imageEl.OriginHeight;
28027 height = this.thumbEl.getHeight();
28028 this.baseScale = height / this.imageEl.OriginHeight;
28030 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28031 width = this.thumbEl.getWidth();
28032 this.baseScale = width / this.imageEl.OriginWidth;
28038 if(this.baseRotate == 6 || this.baseRotate == 8){
28040 width = this.thumbEl.getHeight();
28041 this.baseScale = width / this.imageEl.OriginHeight;
28043 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28044 height = this.thumbEl.getWidth();
28045 this.baseScale = height / this.imageEl.OriginHeight;
28048 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28049 height = this.thumbEl.getWidth();
28050 this.baseScale = height / this.imageEl.OriginHeight;
28052 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28053 width = this.thumbEl.getHeight();
28054 this.baseScale = width / this.imageEl.OriginWidth;
28061 width = this.thumbEl.getWidth();
28062 this.baseScale = width / this.imageEl.OriginWidth;
28064 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28065 height = this.thumbEl.getHeight();
28066 this.baseScale = height / this.imageEl.OriginHeight;
28069 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28071 height = this.thumbEl.getHeight();
28072 this.baseScale = height / this.imageEl.OriginHeight;
28074 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28075 width = this.thumbEl.getWidth();
28076 this.baseScale = width / this.imageEl.OriginWidth;
28084 getScaleLevel : function()
28086 return this.baseScale * Math.pow(1.1, this.scale);
28089 onTouchStart : function(e)
28091 if(!this.canvasLoaded){
28092 this.beforeSelectFile(e);
28096 var touches = e.browserEvent.touches;
28102 if(touches.length == 1){
28103 this.onMouseDown(e);
28107 if(touches.length != 2){
28113 for(var i = 0, finger; finger = touches[i]; i++){
28114 coords.push(finger.pageX, finger.pageY);
28117 var x = Math.pow(coords[0] - coords[2], 2);
28118 var y = Math.pow(coords[1] - coords[3], 2);
28120 this.startDistance = Math.sqrt(x + y);
28122 this.startScale = this.scale;
28124 this.pinching = true;
28125 this.dragable = false;
28129 onTouchMove : function(e)
28131 if(!this.pinching && !this.dragable){
28135 var touches = e.browserEvent.touches;
28142 this.onMouseMove(e);
28148 for(var i = 0, finger; finger = touches[i]; i++){
28149 coords.push(finger.pageX, finger.pageY);
28152 var x = Math.pow(coords[0] - coords[2], 2);
28153 var y = Math.pow(coords[1] - coords[3], 2);
28155 this.endDistance = Math.sqrt(x + y);
28157 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28159 if(!this.zoomable()){
28160 this.scale = this.startScale;
28168 onTouchEnd : function(e)
28170 this.pinching = false;
28171 this.dragable = false;
28175 process : function(file, crop)
28178 this.maskEl.mask(this.loadingText);
28181 this.xhr = new XMLHttpRequest();
28183 file.xhr = this.xhr;
28185 this.xhr.open(this.method, this.url, true);
28188 "Accept": "application/json",
28189 "Cache-Control": "no-cache",
28190 "X-Requested-With": "XMLHttpRequest"
28193 for (var headerName in headers) {
28194 var headerValue = headers[headerName];
28196 this.xhr.setRequestHeader(headerName, headerValue);
28202 this.xhr.onload = function()
28204 _this.xhrOnLoad(_this.xhr);
28207 this.xhr.onerror = function()
28209 _this.xhrOnError(_this.xhr);
28212 var formData = new FormData();
28214 formData.append('returnHTML', 'NO');
28217 formData.append('crop', crop);
28220 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28221 formData.append(this.paramName, file, file.name);
28224 if(typeof(file.filename) != 'undefined'){
28225 formData.append('filename', file.filename);
28228 if(typeof(file.mimetype) != 'undefined'){
28229 formData.append('mimetype', file.mimetype);
28232 if(this.fireEvent('arrange', this, formData) != false){
28233 this.xhr.send(formData);
28237 xhrOnLoad : function(xhr)
28240 this.maskEl.unmask();
28243 if (xhr.readyState !== 4) {
28244 this.fireEvent('exception', this, xhr);
28248 var response = Roo.decode(xhr.responseText);
28250 if(!response.success){
28251 this.fireEvent('exception', this, xhr);
28255 var response = Roo.decode(xhr.responseText);
28257 this.fireEvent('upload', this, response);
28261 xhrOnError : function()
28264 this.maskEl.unmask();
28267 Roo.log('xhr on error');
28269 var response = Roo.decode(xhr.responseText);
28275 prepare : function(file)
28278 this.maskEl.mask(this.loadingText);
28284 if(typeof(file) === 'string'){
28285 this.loadCanvas(file);
28289 if(!file || !this.urlAPI){
28294 this.cropType = file.type;
28298 if(this.fireEvent('prepare', this, this.file) != false){
28300 var reader = new FileReader();
28302 reader.onload = function (e) {
28303 if (e.target.error) {
28304 Roo.log(e.target.error);
28308 var buffer = e.target.result,
28309 dataView = new DataView(buffer),
28311 maxOffset = dataView.byteLength - 4,
28315 if (dataView.getUint16(0) === 0xffd8) {
28316 while (offset < maxOffset) {
28317 markerBytes = dataView.getUint16(offset);
28319 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28320 markerLength = dataView.getUint16(offset + 2) + 2;
28321 if (offset + markerLength > dataView.byteLength) {
28322 Roo.log('Invalid meta data: Invalid segment size.');
28326 if(markerBytes == 0xffe1){
28327 _this.parseExifData(
28334 offset += markerLength;
28344 var url = _this.urlAPI.createObjectURL(_this.file);
28346 _this.loadCanvas(url);
28351 reader.readAsArrayBuffer(this.file);
28357 parseExifData : function(dataView, offset, length)
28359 var tiffOffset = offset + 10,
28363 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28364 // No Exif data, might be XMP data instead
28368 // Check for the ASCII code for "Exif" (0x45786966):
28369 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28370 // No Exif data, might be XMP data instead
28373 if (tiffOffset + 8 > dataView.byteLength) {
28374 Roo.log('Invalid Exif data: Invalid segment size.');
28377 // Check for the two null bytes:
28378 if (dataView.getUint16(offset + 8) !== 0x0000) {
28379 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28382 // Check the byte alignment:
28383 switch (dataView.getUint16(tiffOffset)) {
28385 littleEndian = true;
28388 littleEndian = false;
28391 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28394 // Check for the TIFF tag marker (0x002A):
28395 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28396 Roo.log('Invalid Exif data: Missing TIFF marker.');
28399 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28400 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28402 this.parseExifTags(
28405 tiffOffset + dirOffset,
28410 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28415 if (dirOffset + 6 > dataView.byteLength) {
28416 Roo.log('Invalid Exif data: Invalid directory offset.');
28419 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28420 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28421 if (dirEndOffset + 4 > dataView.byteLength) {
28422 Roo.log('Invalid Exif data: Invalid directory size.');
28425 for (i = 0; i < tagsNumber; i += 1) {
28429 dirOffset + 2 + 12 * i, // tag offset
28433 // Return the offset to the next directory:
28434 return dataView.getUint32(dirEndOffset, littleEndian);
28437 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28439 var tag = dataView.getUint16(offset, littleEndian);
28441 this.exif[tag] = this.getExifValue(
28445 dataView.getUint16(offset + 2, littleEndian), // tag type
28446 dataView.getUint32(offset + 4, littleEndian), // tag length
28451 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28453 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28462 Roo.log('Invalid Exif data: Invalid tag type.');
28466 tagSize = tagType.size * length;
28467 // Determine if the value is contained in the dataOffset bytes,
28468 // or if the value at the dataOffset is a pointer to the actual data:
28469 dataOffset = tagSize > 4 ?
28470 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28471 if (dataOffset + tagSize > dataView.byteLength) {
28472 Roo.log('Invalid Exif data: Invalid data offset.');
28475 if (length === 1) {
28476 return tagType.getValue(dataView, dataOffset, littleEndian);
28479 for (i = 0; i < length; i += 1) {
28480 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28483 if (tagType.ascii) {
28485 // Concatenate the chars:
28486 for (i = 0; i < values.length; i += 1) {
28488 // Ignore the terminating NULL byte(s):
28489 if (c === '\u0000') {
28501 Roo.apply(Roo.bootstrap.UploadCropbox, {
28503 'Orientation': 0x0112
28507 1: 0, //'top-left',
28509 3: 180, //'bottom-right',
28510 // 4: 'bottom-left',
28512 6: 90, //'right-top',
28513 // 7: 'right-bottom',
28514 8: 270 //'left-bottom'
28518 // byte, 8-bit unsigned int:
28520 getValue: function (dataView, dataOffset) {
28521 return dataView.getUint8(dataOffset);
28525 // ascii, 8-bit byte:
28527 getValue: function (dataView, dataOffset) {
28528 return String.fromCharCode(dataView.getUint8(dataOffset));
28533 // short, 16 bit int:
28535 getValue: function (dataView, dataOffset, littleEndian) {
28536 return dataView.getUint16(dataOffset, littleEndian);
28540 // long, 32 bit int:
28542 getValue: function (dataView, dataOffset, littleEndian) {
28543 return dataView.getUint32(dataOffset, littleEndian);
28547 // rational = two long values, first is numerator, second is denominator:
28549 getValue: function (dataView, dataOffset, littleEndian) {
28550 return dataView.getUint32(dataOffset, littleEndian) /
28551 dataView.getUint32(dataOffset + 4, littleEndian);
28555 // slong, 32 bit signed int:
28557 getValue: function (dataView, dataOffset, littleEndian) {
28558 return dataView.getInt32(dataOffset, littleEndian);
28562 // srational, two slongs, first is numerator, second is denominator:
28564 getValue: function (dataView, dataOffset, littleEndian) {
28565 return dataView.getInt32(dataOffset, littleEndian) /
28566 dataView.getInt32(dataOffset + 4, littleEndian);
28576 cls : 'btn-group roo-upload-cropbox-rotate-left',
28577 action : 'rotate-left',
28581 cls : 'btn btn-default',
28582 html : '<i class="fa fa-undo"></i>'
28588 cls : 'btn-group roo-upload-cropbox-picture',
28589 action : 'picture',
28593 cls : 'btn btn-default',
28594 html : '<i class="fa fa-picture-o"></i>'
28600 cls : 'btn-group roo-upload-cropbox-rotate-right',
28601 action : 'rotate-right',
28605 cls : 'btn btn-default',
28606 html : '<i class="fa fa-repeat"></i>'
28614 cls : 'btn-group roo-upload-cropbox-rotate-left',
28615 action : 'rotate-left',
28619 cls : 'btn btn-default',
28620 html : '<i class="fa fa-undo"></i>'
28626 cls : 'btn-group roo-upload-cropbox-download',
28627 action : 'download',
28631 cls : 'btn btn-default',
28632 html : '<i class="fa fa-download"></i>'
28638 cls : 'btn-group roo-upload-cropbox-crop',
28643 cls : 'btn btn-default',
28644 html : '<i class="fa fa-crop"></i>'
28650 cls : 'btn-group roo-upload-cropbox-trash',
28655 cls : 'btn btn-default',
28656 html : '<i class="fa fa-trash"></i>'
28662 cls : 'btn-group roo-upload-cropbox-rotate-right',
28663 action : 'rotate-right',
28667 cls : 'btn btn-default',
28668 html : '<i class="fa fa-repeat"></i>'
28676 cls : 'btn-group roo-upload-cropbox-rotate-left',
28677 action : 'rotate-left',
28681 cls : 'btn btn-default',
28682 html : '<i class="fa fa-undo"></i>'
28688 cls : 'btn-group roo-upload-cropbox-rotate-right',
28689 action : 'rotate-right',
28693 cls : 'btn btn-default',
28694 html : '<i class="fa fa-repeat"></i>'
28707 * @class Roo.bootstrap.DocumentManager
28708 * @extends Roo.bootstrap.Component
28709 * Bootstrap DocumentManager class
28710 * @cfg {String} paramName default 'imageUpload'
28711 * @cfg {String} toolTipName default 'filename'
28712 * @cfg {String} method default POST
28713 * @cfg {String} url action url
28714 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28715 * @cfg {Boolean} multiple multiple upload default true
28716 * @cfg {Number} thumbSize default 300
28717 * @cfg {String} fieldLabel
28718 * @cfg {Number} labelWidth default 4
28719 * @cfg {String} labelAlign (left|top) default left
28720 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28721 * @cfg {Number} labellg set the width of label (1-12)
28722 * @cfg {Number} labelmd set the width of label (1-12)
28723 * @cfg {Number} labelsm set the width of label (1-12)
28724 * @cfg {Number} labelxs set the width of label (1-12)
28727 * Create a new DocumentManager
28728 * @param {Object} config The config object
28731 Roo.bootstrap.DocumentManager = function(config){
28732 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28735 this.delegates = [];
28740 * Fire when initial the DocumentManager
28741 * @param {Roo.bootstrap.DocumentManager} this
28746 * inspect selected file
28747 * @param {Roo.bootstrap.DocumentManager} this
28748 * @param {File} file
28753 * Fire when xhr load exception
28754 * @param {Roo.bootstrap.DocumentManager} this
28755 * @param {XMLHttpRequest} xhr
28757 "exception" : true,
28759 * @event afterupload
28760 * Fire when xhr load exception
28761 * @param {Roo.bootstrap.DocumentManager} this
28762 * @param {XMLHttpRequest} xhr
28764 "afterupload" : true,
28767 * prepare the form data
28768 * @param {Roo.bootstrap.DocumentManager} this
28769 * @param {Object} formData
28774 * Fire when remove the file
28775 * @param {Roo.bootstrap.DocumentManager} this
28776 * @param {Object} file
28781 * Fire after refresh the file
28782 * @param {Roo.bootstrap.DocumentManager} this
28787 * Fire after click the image
28788 * @param {Roo.bootstrap.DocumentManager} this
28789 * @param {Object} file
28794 * Fire when upload a image and editable set to true
28795 * @param {Roo.bootstrap.DocumentManager} this
28796 * @param {Object} file
28800 * @event beforeselectfile
28801 * Fire before select file
28802 * @param {Roo.bootstrap.DocumentManager} this
28804 "beforeselectfile" : true,
28807 * Fire before process file
28808 * @param {Roo.bootstrap.DocumentManager} this
28809 * @param {Object} file
28813 * @event previewrendered
28814 * Fire when preview rendered
28815 * @param {Roo.bootstrap.DocumentManager} this
28816 * @param {Object} file
28818 "previewrendered" : true,
28821 "previewResize" : true
28826 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28835 paramName : 'imageUpload',
28836 toolTipName : 'filename',
28839 labelAlign : 'left',
28849 getAutoCreate : function()
28851 var managerWidget = {
28853 cls : 'roo-document-manager',
28857 cls : 'roo-document-manager-selector',
28862 cls : 'roo-document-manager-uploader',
28866 cls : 'roo-document-manager-upload-btn',
28867 html : '<i class="fa fa-plus"></i>'
28878 cls : 'column col-md-12',
28883 if(this.fieldLabel.length){
28888 cls : 'column col-md-12',
28889 html : this.fieldLabel
28893 cls : 'column col-md-12',
28898 if(this.labelAlign == 'left'){
28903 html : this.fieldLabel
28912 if(this.labelWidth > 12){
28913 content[0].style = "width: " + this.labelWidth + 'px';
28916 if(this.labelWidth < 13 && this.labelmd == 0){
28917 this.labelmd = this.labelWidth;
28920 if(this.labellg > 0){
28921 content[0].cls += ' col-lg-' + this.labellg;
28922 content[1].cls += ' col-lg-' + (12 - this.labellg);
28925 if(this.labelmd > 0){
28926 content[0].cls += ' col-md-' + this.labelmd;
28927 content[1].cls += ' col-md-' + (12 - this.labelmd);
28930 if(this.labelsm > 0){
28931 content[0].cls += ' col-sm-' + this.labelsm;
28932 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28935 if(this.labelxs > 0){
28936 content[0].cls += ' col-xs-' + this.labelxs;
28937 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28945 cls : 'row clearfix',
28953 initEvents : function()
28955 this.managerEl = this.el.select('.roo-document-manager', true).first();
28956 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28958 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28959 this.selectorEl.hide();
28962 this.selectorEl.attr('multiple', 'multiple');
28965 this.selectorEl.on('change', this.onFileSelected, this);
28967 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28968 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28970 this.uploader.on('click', this.onUploaderClick, this);
28972 this.renderProgressDialog();
28976 window.addEventListener("resize", function() { _this.refresh(); } );
28978 this.fireEvent('initial', this);
28981 renderProgressDialog : function()
28985 this.progressDialog = new Roo.bootstrap.Modal({
28986 cls : 'roo-document-manager-progress-dialog',
28987 allow_close : false,
28997 btnclick : function() {
28998 _this.uploadCancel();
29004 this.progressDialog.render(Roo.get(document.body));
29006 this.progress = new Roo.bootstrap.Progress({
29007 cls : 'roo-document-manager-progress',
29012 this.progress.render(this.progressDialog.getChildContainer());
29014 this.progressBar = new Roo.bootstrap.ProgressBar({
29015 cls : 'roo-document-manager-progress-bar',
29018 aria_valuemax : 12,
29022 this.progressBar.render(this.progress.getChildContainer());
29025 onUploaderClick : function(e)
29027 e.preventDefault();
29029 if(this.fireEvent('beforeselectfile', this) != false){
29030 this.selectorEl.dom.click();
29035 onFileSelected : function(e)
29037 e.preventDefault();
29039 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29043 Roo.each(this.selectorEl.dom.files, function(file){
29044 if(this.fireEvent('inspect', this, file) != false){
29045 this.files.push(file);
29055 this.selectorEl.dom.value = '';
29057 if(!this.files || !this.files.length){
29061 if(this.boxes > 0 && this.files.length > this.boxes){
29062 this.files = this.files.slice(0, this.boxes);
29065 this.uploader.show();
29067 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29068 this.uploader.hide();
29077 Roo.each(this.files, function(file){
29079 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29080 var f = this.renderPreview(file);
29085 if(file.type.indexOf('image') != -1){
29086 this.delegates.push(
29088 _this.process(file);
29089 }).createDelegate(this)
29097 _this.process(file);
29098 }).createDelegate(this)
29103 this.files = files;
29105 this.delegates = this.delegates.concat(docs);
29107 if(!this.delegates.length){
29112 this.progressBar.aria_valuemax = this.delegates.length;
29119 arrange : function()
29121 if(!this.delegates.length){
29122 this.progressDialog.hide();
29127 var delegate = this.delegates.shift();
29129 this.progressDialog.show();
29131 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29133 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29138 refresh : function()
29140 this.uploader.show();
29142 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29143 this.uploader.hide();
29146 Roo.isTouch ? this.closable(false) : this.closable(true);
29148 this.fireEvent('refresh', this);
29151 onRemove : function(e, el, o)
29153 e.preventDefault();
29155 this.fireEvent('remove', this, o);
29159 remove : function(o)
29163 Roo.each(this.files, function(file){
29164 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29173 this.files = files;
29180 Roo.each(this.files, function(file){
29185 file.target.remove();
29194 onClick : function(e, el, o)
29196 e.preventDefault();
29198 this.fireEvent('click', this, o);
29202 closable : function(closable)
29204 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29206 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29218 xhrOnLoad : function(xhr)
29220 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29224 if (xhr.readyState !== 4) {
29226 this.fireEvent('exception', this, xhr);
29230 var response = Roo.decode(xhr.responseText);
29232 if(!response.success){
29234 this.fireEvent('exception', this, xhr);
29238 var file = this.renderPreview(response.data);
29240 this.files.push(file);
29244 this.fireEvent('afterupload', this, xhr);
29248 xhrOnError : function(xhr)
29250 Roo.log('xhr on error');
29252 var response = Roo.decode(xhr.responseText);
29259 process : function(file)
29261 if(this.fireEvent('process', this, file) !== false){
29262 if(this.editable && file.type.indexOf('image') != -1){
29263 this.fireEvent('edit', this, file);
29267 this.uploadStart(file, false);
29274 uploadStart : function(file, crop)
29276 this.xhr = new XMLHttpRequest();
29278 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29283 file.xhr = this.xhr;
29285 this.managerEl.createChild({
29287 cls : 'roo-document-manager-loading',
29291 tooltip : file.name,
29292 cls : 'roo-document-manager-thumb',
29293 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29299 this.xhr.open(this.method, this.url, true);
29302 "Accept": "application/json",
29303 "Cache-Control": "no-cache",
29304 "X-Requested-With": "XMLHttpRequest"
29307 for (var headerName in headers) {
29308 var headerValue = headers[headerName];
29310 this.xhr.setRequestHeader(headerName, headerValue);
29316 this.xhr.onload = function()
29318 _this.xhrOnLoad(_this.xhr);
29321 this.xhr.onerror = function()
29323 _this.xhrOnError(_this.xhr);
29326 var formData = new FormData();
29328 formData.append('returnHTML', 'NO');
29331 formData.append('crop', crop);
29334 formData.append(this.paramName, file, file.name);
29341 if(this.fireEvent('prepare', this, formData, options) != false){
29343 if(options.manually){
29347 this.xhr.send(formData);
29351 this.uploadCancel();
29354 uploadCancel : function()
29360 this.delegates = [];
29362 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29369 renderPreview : function(file)
29371 if(typeof(file.target) != 'undefined' && file.target){
29375 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29377 var previewEl = this.managerEl.createChild({
29379 cls : 'roo-document-manager-preview',
29383 tooltip : file[this.toolTipName],
29384 cls : 'roo-document-manager-thumb',
29385 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29390 html : '<i class="fa fa-times-circle"></i>'
29395 var close = previewEl.select('button.close', true).first();
29397 close.on('click', this.onRemove, this, file);
29399 file.target = previewEl;
29401 var image = previewEl.select('img', true).first();
29405 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29407 image.on('click', this.onClick, this, file);
29409 this.fireEvent('previewrendered', this, file);
29415 onPreviewLoad : function(file, image)
29417 if(typeof(file.target) == 'undefined' || !file.target){
29421 var width = image.dom.naturalWidth || image.dom.width;
29422 var height = image.dom.naturalHeight || image.dom.height;
29424 if(!this.previewResize) {
29428 if(width > height){
29429 file.target.addClass('wide');
29433 file.target.addClass('tall');
29438 uploadFromSource : function(file, crop)
29440 this.xhr = new XMLHttpRequest();
29442 this.managerEl.createChild({
29444 cls : 'roo-document-manager-loading',
29448 tooltip : file.name,
29449 cls : 'roo-document-manager-thumb',
29450 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29456 this.xhr.open(this.method, this.url, true);
29459 "Accept": "application/json",
29460 "Cache-Control": "no-cache",
29461 "X-Requested-With": "XMLHttpRequest"
29464 for (var headerName in headers) {
29465 var headerValue = headers[headerName];
29467 this.xhr.setRequestHeader(headerName, headerValue);
29473 this.xhr.onload = function()
29475 _this.xhrOnLoad(_this.xhr);
29478 this.xhr.onerror = function()
29480 _this.xhrOnError(_this.xhr);
29483 var formData = new FormData();
29485 formData.append('returnHTML', 'NO');
29487 formData.append('crop', crop);
29489 if(typeof(file.filename) != 'undefined'){
29490 formData.append('filename', file.filename);
29493 if(typeof(file.mimetype) != 'undefined'){
29494 formData.append('mimetype', file.mimetype);
29499 if(this.fireEvent('prepare', this, formData) != false){
29500 this.xhr.send(formData);
29510 * @class Roo.bootstrap.DocumentViewer
29511 * @extends Roo.bootstrap.Component
29512 * Bootstrap DocumentViewer class
29513 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29514 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29517 * Create a new DocumentViewer
29518 * @param {Object} config The config object
29521 Roo.bootstrap.DocumentViewer = function(config){
29522 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29527 * Fire after initEvent
29528 * @param {Roo.bootstrap.DocumentViewer} this
29534 * @param {Roo.bootstrap.DocumentViewer} this
29539 * Fire after download button
29540 * @param {Roo.bootstrap.DocumentViewer} this
29545 * Fire after trash button
29546 * @param {Roo.bootstrap.DocumentViewer} this
29553 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29555 showDownload : true,
29559 getAutoCreate : function()
29563 cls : 'roo-document-viewer',
29567 cls : 'roo-document-viewer-body',
29571 cls : 'roo-document-viewer-thumb',
29575 cls : 'roo-document-viewer-image'
29583 cls : 'roo-document-viewer-footer',
29586 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29590 cls : 'btn-group roo-document-viewer-download',
29594 cls : 'btn btn-default',
29595 html : '<i class="fa fa-download"></i>'
29601 cls : 'btn-group roo-document-viewer-trash',
29605 cls : 'btn btn-default',
29606 html : '<i class="fa fa-trash"></i>'
29619 initEvents : function()
29621 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29622 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29624 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29625 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29627 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29628 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29630 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29631 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29633 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29634 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29636 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29637 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29639 this.bodyEl.on('click', this.onClick, this);
29640 this.downloadBtn.on('click', this.onDownload, this);
29641 this.trashBtn.on('click', this.onTrash, this);
29643 this.downloadBtn.hide();
29644 this.trashBtn.hide();
29646 if(this.showDownload){
29647 this.downloadBtn.show();
29650 if(this.showTrash){
29651 this.trashBtn.show();
29654 if(!this.showDownload && !this.showTrash) {
29655 this.footerEl.hide();
29660 initial : function()
29662 this.fireEvent('initial', this);
29666 onClick : function(e)
29668 e.preventDefault();
29670 this.fireEvent('click', this);
29673 onDownload : function(e)
29675 e.preventDefault();
29677 this.fireEvent('download', this);
29680 onTrash : function(e)
29682 e.preventDefault();
29684 this.fireEvent('trash', this);
29696 * @class Roo.bootstrap.NavProgressBar
29697 * @extends Roo.bootstrap.Component
29698 * Bootstrap NavProgressBar class
29701 * Create a new nav progress bar
29702 * @param {Object} config The config object
29705 Roo.bootstrap.NavProgressBar = function(config){
29706 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29708 this.bullets = this.bullets || [];
29710 // Roo.bootstrap.NavProgressBar.register(this);
29714 * Fires when the active item changes
29715 * @param {Roo.bootstrap.NavProgressBar} this
29716 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29717 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29724 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29729 getAutoCreate : function()
29731 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29735 cls : 'roo-navigation-bar-group',
29739 cls : 'roo-navigation-top-bar'
29743 cls : 'roo-navigation-bullets-bar',
29747 cls : 'roo-navigation-bar'
29754 cls : 'roo-navigation-bottom-bar'
29764 initEvents: function()
29769 onRender : function(ct, position)
29771 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29773 if(this.bullets.length){
29774 Roo.each(this.bullets, function(b){
29783 addItem : function(cfg)
29785 var item = new Roo.bootstrap.NavProgressItem(cfg);
29787 item.parentId = this.id;
29788 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29791 var top = new Roo.bootstrap.Element({
29793 cls : 'roo-navigation-bar-text'
29796 var bottom = new Roo.bootstrap.Element({
29798 cls : 'roo-navigation-bar-text'
29801 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29802 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29804 var topText = new Roo.bootstrap.Element({
29806 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29809 var bottomText = new Roo.bootstrap.Element({
29811 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29814 topText.onRender(top.el, null);
29815 bottomText.onRender(bottom.el, null);
29818 item.bottomEl = bottom;
29821 this.barItems.push(item);
29826 getActive : function()
29828 var active = false;
29830 Roo.each(this.barItems, function(v){
29832 if (!v.isActive()) {
29844 setActiveItem : function(item)
29848 Roo.each(this.barItems, function(v){
29849 if (v.rid == item.rid) {
29853 if (v.isActive()) {
29854 v.setActive(false);
29859 item.setActive(true);
29861 this.fireEvent('changed', this, item, prev);
29864 getBarItem: function(rid)
29868 Roo.each(this.barItems, function(e) {
29869 if (e.rid != rid) {
29880 indexOfItem : function(item)
29884 Roo.each(this.barItems, function(v, i){
29886 if (v.rid != item.rid) {
29897 setActiveNext : function()
29899 var i = this.indexOfItem(this.getActive());
29901 if (i > this.barItems.length) {
29905 this.setActiveItem(this.barItems[i+1]);
29908 setActivePrev : function()
29910 var i = this.indexOfItem(this.getActive());
29916 this.setActiveItem(this.barItems[i-1]);
29919 format : function()
29921 if(!this.barItems.length){
29925 var width = 100 / this.barItems.length;
29927 Roo.each(this.barItems, function(i){
29928 i.el.setStyle('width', width + '%');
29929 i.topEl.el.setStyle('width', width + '%');
29930 i.bottomEl.el.setStyle('width', width + '%');
29939 * Nav Progress Item
29944 * @class Roo.bootstrap.NavProgressItem
29945 * @extends Roo.bootstrap.Component
29946 * Bootstrap NavProgressItem class
29947 * @cfg {String} rid the reference id
29948 * @cfg {Boolean} active (true|false) Is item active default false
29949 * @cfg {Boolean} disabled (true|false) Is item active default false
29950 * @cfg {String} html
29951 * @cfg {String} position (top|bottom) text position default bottom
29952 * @cfg {String} icon show icon instead of number
29955 * Create a new NavProgressItem
29956 * @param {Object} config The config object
29958 Roo.bootstrap.NavProgressItem = function(config){
29959 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29964 * The raw click event for the entire grid.
29965 * @param {Roo.bootstrap.NavProgressItem} this
29966 * @param {Roo.EventObject} e
29973 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29979 position : 'bottom',
29982 getAutoCreate : function()
29984 var iconCls = 'roo-navigation-bar-item-icon';
29986 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29990 cls: 'roo-navigation-bar-item',
30000 cfg.cls += ' active';
30003 cfg.cls += ' disabled';
30009 disable : function()
30011 this.setDisabled(true);
30014 enable : function()
30016 this.setDisabled(false);
30019 initEvents: function()
30021 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30023 this.iconEl.on('click', this.onClick, this);
30026 onClick : function(e)
30028 e.preventDefault();
30034 if(this.fireEvent('click', this, e) === false){
30038 this.parent().setActiveItem(this);
30041 isActive: function ()
30043 return this.active;
30046 setActive : function(state)
30048 if(this.active == state){
30052 this.active = state;
30055 this.el.addClass('active');
30059 this.el.removeClass('active');
30064 setDisabled : function(state)
30066 if(this.disabled == state){
30070 this.disabled = state;
30073 this.el.addClass('disabled');
30077 this.el.removeClass('disabled');
30080 tooltipEl : function()
30082 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30095 * @class Roo.bootstrap.FieldLabel
30096 * @extends Roo.bootstrap.Component
30097 * Bootstrap FieldLabel class
30098 * @cfg {String} html contents of the element
30099 * @cfg {String} tag tag of the element default label
30100 * @cfg {String} cls class of the element
30101 * @cfg {String} target label target
30102 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30103 * @cfg {String} invalidClass default "text-warning"
30104 * @cfg {String} validClass default "text-success"
30105 * @cfg {String} iconTooltip default "This field is required"
30106 * @cfg {String} indicatorpos (left|right) default left
30109 * Create a new FieldLabel
30110 * @param {Object} config The config object
30113 Roo.bootstrap.FieldLabel = function(config){
30114 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30119 * Fires after the field has been marked as invalid.
30120 * @param {Roo.form.FieldLabel} this
30121 * @param {String} msg The validation message
30126 * Fires after the field has been validated with no errors.
30127 * @param {Roo.form.FieldLabel} this
30133 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30140 invalidClass : 'has-warning',
30141 validClass : 'has-success',
30142 iconTooltip : 'This field is required',
30143 indicatorpos : 'left',
30145 getAutoCreate : function(){
30148 if (!this.allowBlank) {
30154 cls : 'roo-bootstrap-field-label ' + this.cls,
30159 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30160 tooltip : this.iconTooltip
30169 if(this.indicatorpos == 'right'){
30172 cls : 'roo-bootstrap-field-label ' + this.cls,
30181 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30182 tooltip : this.iconTooltip
30191 initEvents: function()
30193 Roo.bootstrap.Element.superclass.initEvents.call(this);
30195 this.indicator = this.indicatorEl();
30197 if(this.indicator){
30198 this.indicator.removeClass('visible');
30199 this.indicator.addClass('invisible');
30202 Roo.bootstrap.FieldLabel.register(this);
30205 indicatorEl : function()
30207 var indicator = this.el.select('i.roo-required-indicator',true).first();
30218 * Mark this field as valid
30220 markValid : function()
30222 if(this.indicator){
30223 this.indicator.removeClass('visible');
30224 this.indicator.addClass('invisible');
30227 this.el.removeClass(this.invalidClass);
30229 this.el.addClass(this.validClass);
30231 this.fireEvent('valid', this);
30235 * Mark this field as invalid
30236 * @param {String} msg The validation message
30238 markInvalid : function(msg)
30240 if(this.indicator){
30241 this.indicator.removeClass('invisible');
30242 this.indicator.addClass('visible');
30245 this.el.removeClass(this.validClass);
30247 this.el.addClass(this.invalidClass);
30249 this.fireEvent('invalid', this, msg);
30255 Roo.apply(Roo.bootstrap.FieldLabel, {
30260 * register a FieldLabel Group
30261 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30263 register : function(label)
30265 if(this.groups.hasOwnProperty(label.target)){
30269 this.groups[label.target] = label;
30273 * fetch a FieldLabel Group based on the target
30274 * @param {string} target
30275 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30277 get: function(target) {
30278 if (typeof(this.groups[target]) == 'undefined') {
30282 return this.groups[target] ;
30291 * page DateSplitField.
30297 * @class Roo.bootstrap.DateSplitField
30298 * @extends Roo.bootstrap.Component
30299 * Bootstrap DateSplitField class
30300 * @cfg {string} fieldLabel - the label associated
30301 * @cfg {Number} labelWidth set the width of label (0-12)
30302 * @cfg {String} labelAlign (top|left)
30303 * @cfg {Boolean} dayAllowBlank (true|false) default false
30304 * @cfg {Boolean} monthAllowBlank (true|false) default false
30305 * @cfg {Boolean} yearAllowBlank (true|false) default false
30306 * @cfg {string} dayPlaceholder
30307 * @cfg {string} monthPlaceholder
30308 * @cfg {string} yearPlaceholder
30309 * @cfg {string} dayFormat default 'd'
30310 * @cfg {string} monthFormat default 'm'
30311 * @cfg {string} yearFormat default 'Y'
30312 * @cfg {Number} labellg set the width of label (1-12)
30313 * @cfg {Number} labelmd set the width of label (1-12)
30314 * @cfg {Number} labelsm set the width of label (1-12)
30315 * @cfg {Number} labelxs set the width of label (1-12)
30319 * Create a new DateSplitField
30320 * @param {Object} config The config object
30323 Roo.bootstrap.DateSplitField = function(config){
30324 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30330 * getting the data of years
30331 * @param {Roo.bootstrap.DateSplitField} this
30332 * @param {Object} years
30337 * getting the data of days
30338 * @param {Roo.bootstrap.DateSplitField} this
30339 * @param {Object} days
30344 * Fires after the field has been marked as invalid.
30345 * @param {Roo.form.Field} this
30346 * @param {String} msg The validation message
30351 * Fires after the field has been validated with no errors.
30352 * @param {Roo.form.Field} this
30358 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30361 labelAlign : 'top',
30363 dayAllowBlank : false,
30364 monthAllowBlank : false,
30365 yearAllowBlank : false,
30366 dayPlaceholder : '',
30367 monthPlaceholder : '',
30368 yearPlaceholder : '',
30372 isFormField : true,
30378 getAutoCreate : function()
30382 cls : 'row roo-date-split-field-group',
30387 cls : 'form-hidden-field roo-date-split-field-group-value',
30393 var labelCls = 'col-md-12';
30394 var contentCls = 'col-md-4';
30396 if(this.fieldLabel){
30400 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30404 html : this.fieldLabel
30409 if(this.labelAlign == 'left'){
30411 if(this.labelWidth > 12){
30412 label.style = "width: " + this.labelWidth + 'px';
30415 if(this.labelWidth < 13 && this.labelmd == 0){
30416 this.labelmd = this.labelWidth;
30419 if(this.labellg > 0){
30420 labelCls = ' col-lg-' + this.labellg;
30421 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30424 if(this.labelmd > 0){
30425 labelCls = ' col-md-' + this.labelmd;
30426 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30429 if(this.labelsm > 0){
30430 labelCls = ' col-sm-' + this.labelsm;
30431 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30434 if(this.labelxs > 0){
30435 labelCls = ' col-xs-' + this.labelxs;
30436 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30440 label.cls += ' ' + labelCls;
30442 cfg.cn.push(label);
30445 Roo.each(['day', 'month', 'year'], function(t){
30448 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30455 inputEl: function ()
30457 return this.el.select('.roo-date-split-field-group-value', true).first();
30460 onRender : function(ct, position)
30464 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30466 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30468 this.dayField = new Roo.bootstrap.ComboBox({
30469 allowBlank : this.dayAllowBlank,
30470 alwaysQuery : true,
30471 displayField : 'value',
30474 forceSelection : true,
30476 placeholder : this.dayPlaceholder,
30477 selectOnFocus : true,
30478 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30479 triggerAction : 'all',
30481 valueField : 'value',
30482 store : new Roo.data.SimpleStore({
30483 data : (function() {
30485 _this.fireEvent('days', _this, days);
30488 fields : [ 'value' ]
30491 select : function (_self, record, index)
30493 _this.setValue(_this.getValue());
30498 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30500 this.monthField = new Roo.bootstrap.MonthField({
30501 after : '<i class=\"fa fa-calendar\"></i>',
30502 allowBlank : this.monthAllowBlank,
30503 placeholder : this.monthPlaceholder,
30506 render : function (_self)
30508 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30509 e.preventDefault();
30513 select : function (_self, oldvalue, newvalue)
30515 _this.setValue(_this.getValue());
30520 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30522 this.yearField = new Roo.bootstrap.ComboBox({
30523 allowBlank : this.yearAllowBlank,
30524 alwaysQuery : true,
30525 displayField : 'value',
30528 forceSelection : true,
30530 placeholder : this.yearPlaceholder,
30531 selectOnFocus : true,
30532 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30533 triggerAction : 'all',
30535 valueField : 'value',
30536 store : new Roo.data.SimpleStore({
30537 data : (function() {
30539 _this.fireEvent('years', _this, years);
30542 fields : [ 'value' ]
30545 select : function (_self, record, index)
30547 _this.setValue(_this.getValue());
30552 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30555 setValue : function(v, format)
30557 this.inputEl.dom.value = v;
30559 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30561 var d = Date.parseDate(v, f);
30568 this.setDay(d.format(this.dayFormat));
30569 this.setMonth(d.format(this.monthFormat));
30570 this.setYear(d.format(this.yearFormat));
30577 setDay : function(v)
30579 this.dayField.setValue(v);
30580 this.inputEl.dom.value = this.getValue();
30585 setMonth : function(v)
30587 this.monthField.setValue(v, true);
30588 this.inputEl.dom.value = this.getValue();
30593 setYear : function(v)
30595 this.yearField.setValue(v);
30596 this.inputEl.dom.value = this.getValue();
30601 getDay : function()
30603 return this.dayField.getValue();
30606 getMonth : function()
30608 return this.monthField.getValue();
30611 getYear : function()
30613 return this.yearField.getValue();
30616 getValue : function()
30618 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30620 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30630 this.inputEl.dom.value = '';
30635 validate : function()
30637 var d = this.dayField.validate();
30638 var m = this.monthField.validate();
30639 var y = this.yearField.validate();
30644 (!this.dayAllowBlank && !d) ||
30645 (!this.monthAllowBlank && !m) ||
30646 (!this.yearAllowBlank && !y)
30651 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30660 this.markInvalid();
30665 markValid : function()
30668 var label = this.el.select('label', true).first();
30669 var icon = this.el.select('i.fa-star', true).first();
30675 this.fireEvent('valid', this);
30679 * Mark this field as invalid
30680 * @param {String} msg The validation message
30682 markInvalid : function(msg)
30685 var label = this.el.select('label', true).first();
30686 var icon = this.el.select('i.fa-star', true).first();
30688 if(label && !icon){
30689 this.el.select('.roo-date-split-field-label', true).createChild({
30691 cls : 'text-danger fa fa-lg fa-star',
30692 tooltip : 'This field is required',
30693 style : 'margin-right:5px;'
30697 this.fireEvent('invalid', this, msg);
30700 clearInvalid : function()
30702 var label = this.el.select('label', true).first();
30703 var icon = this.el.select('i.fa-star', true).first();
30709 this.fireEvent('valid', this);
30712 getName: function()
30722 * http://masonry.desandro.com
30724 * The idea is to render all the bricks based on vertical width...
30726 * The original code extends 'outlayer' - we might need to use that....
30732 * @class Roo.bootstrap.LayoutMasonry
30733 * @extends Roo.bootstrap.Component
30734 * Bootstrap Layout Masonry class
30737 * Create a new Element
30738 * @param {Object} config The config object
30741 Roo.bootstrap.LayoutMasonry = function(config){
30743 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30747 Roo.bootstrap.LayoutMasonry.register(this);
30753 * Fire after layout the items
30754 * @param {Roo.bootstrap.LayoutMasonry} this
30755 * @param {Roo.EventObject} e
30762 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30765 * @cfg {Boolean} isLayoutInstant = no animation?
30767 isLayoutInstant : false, // needed?
30770 * @cfg {Number} boxWidth width of the columns
30775 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30780 * @cfg {Number} padWidth padding below box..
30785 * @cfg {Number} gutter gutter width..
30790 * @cfg {Number} maxCols maximum number of columns
30796 * @cfg {Boolean} isAutoInitial defalut true
30798 isAutoInitial : true,
30803 * @cfg {Boolean} isHorizontal defalut false
30805 isHorizontal : false,
30807 currentSize : null,
30813 bricks: null, //CompositeElement
30817 _isLayoutInited : false,
30819 // isAlternative : false, // only use for vertical layout...
30822 * @cfg {Number} alternativePadWidth padding below box..
30824 alternativePadWidth : 50,
30826 selectedBrick : [],
30828 getAutoCreate : function(){
30830 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30834 cls: 'blog-masonary-wrapper ' + this.cls,
30836 cls : 'mas-boxes masonary'
30843 getChildContainer: function( )
30845 if (this.boxesEl) {
30846 return this.boxesEl;
30849 this.boxesEl = this.el.select('.mas-boxes').first();
30851 return this.boxesEl;
30855 initEvents : function()
30859 if(this.isAutoInitial){
30860 Roo.log('hook children rendered');
30861 this.on('childrenrendered', function() {
30862 Roo.log('children rendered');
30868 initial : function()
30870 this.selectedBrick = [];
30872 this.currentSize = this.el.getBox(true);
30874 Roo.EventManager.onWindowResize(this.resize, this);
30876 if(!this.isAutoInitial){
30884 //this.layout.defer(500,this);
30888 resize : function()
30890 var cs = this.el.getBox(true);
30893 this.currentSize.width == cs.width &&
30894 this.currentSize.x == cs.x &&
30895 this.currentSize.height == cs.height &&
30896 this.currentSize.y == cs.y
30898 Roo.log("no change in with or X or Y");
30902 this.currentSize = cs;
30908 layout : function()
30910 this._resetLayout();
30912 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30914 this.layoutItems( isInstant );
30916 this._isLayoutInited = true;
30918 this.fireEvent('layout', this);
30922 _resetLayout : function()
30924 if(this.isHorizontal){
30925 this.horizontalMeasureColumns();
30929 this.verticalMeasureColumns();
30933 verticalMeasureColumns : function()
30935 this.getContainerWidth();
30937 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30938 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30942 var boxWidth = this.boxWidth + this.padWidth;
30944 if(this.containerWidth < this.boxWidth){
30945 boxWidth = this.containerWidth
30948 var containerWidth = this.containerWidth;
30950 var cols = Math.floor(containerWidth / boxWidth);
30952 this.cols = Math.max( cols, 1 );
30954 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30956 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30958 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30960 this.colWidth = boxWidth + avail - this.padWidth;
30962 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30963 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30966 horizontalMeasureColumns : function()
30968 this.getContainerWidth();
30970 var boxWidth = this.boxWidth;
30972 if(this.containerWidth < boxWidth){
30973 boxWidth = this.containerWidth;
30976 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30978 this.el.setHeight(boxWidth);
30982 getContainerWidth : function()
30984 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30987 layoutItems : function( isInstant )
30989 Roo.log(this.bricks);
30991 var items = Roo.apply([], this.bricks);
30993 if(this.isHorizontal){
30994 this._horizontalLayoutItems( items , isInstant );
30998 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30999 // this._verticalAlternativeLayoutItems( items , isInstant );
31003 this._verticalLayoutItems( items , isInstant );
31007 _verticalLayoutItems : function ( items , isInstant)
31009 if ( !items || !items.length ) {
31014 ['xs', 'xs', 'xs', 'tall'],
31015 ['xs', 'xs', 'tall'],
31016 ['xs', 'xs', 'sm'],
31017 ['xs', 'xs', 'xs'],
31023 ['sm', 'xs', 'xs'],
31027 ['tall', 'xs', 'xs', 'xs'],
31028 ['tall', 'xs', 'xs'],
31040 Roo.each(items, function(item, k){
31042 switch (item.size) {
31043 // these layouts take up a full box,
31054 boxes.push([item]);
31077 var filterPattern = function(box, length)
31085 var pattern = box.slice(0, length);
31089 Roo.each(pattern, function(i){
31090 format.push(i.size);
31093 Roo.each(standard, function(s){
31095 if(String(s) != String(format)){
31104 if(!match && length == 1){
31109 filterPattern(box, length - 1);
31113 queue.push(pattern);
31115 box = box.slice(length, box.length);
31117 filterPattern(box, 4);
31123 Roo.each(boxes, function(box, k){
31129 if(box.length == 1){
31134 filterPattern(box, 4);
31138 this._processVerticalLayoutQueue( queue, isInstant );
31142 // _verticalAlternativeLayoutItems : function( items , isInstant )
31144 // if ( !items || !items.length ) {
31148 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31152 _horizontalLayoutItems : function ( items , isInstant)
31154 if ( !items || !items.length || items.length < 3) {
31160 var eItems = items.slice(0, 3);
31162 items = items.slice(3, items.length);
31165 ['xs', 'xs', 'xs', 'wide'],
31166 ['xs', 'xs', 'wide'],
31167 ['xs', 'xs', 'sm'],
31168 ['xs', 'xs', 'xs'],
31174 ['sm', 'xs', 'xs'],
31178 ['wide', 'xs', 'xs', 'xs'],
31179 ['wide', 'xs', 'xs'],
31192 Roo.each(items, function(item, k){
31194 switch (item.size) {
31205 boxes.push([item]);
31229 var filterPattern = function(box, length)
31237 var pattern = box.slice(0, length);
31241 Roo.each(pattern, function(i){
31242 format.push(i.size);
31245 Roo.each(standard, function(s){
31247 if(String(s) != String(format)){
31256 if(!match && length == 1){
31261 filterPattern(box, length - 1);
31265 queue.push(pattern);
31267 box = box.slice(length, box.length);
31269 filterPattern(box, 4);
31275 Roo.each(boxes, function(box, k){
31281 if(box.length == 1){
31286 filterPattern(box, 4);
31293 var pos = this.el.getBox(true);
31297 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31299 var hit_end = false;
31301 Roo.each(queue, function(box){
31305 Roo.each(box, function(b){
31307 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31317 Roo.each(box, function(b){
31319 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31322 mx = Math.max(mx, b.x);
31326 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31330 Roo.each(box, function(b){
31332 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31346 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31349 /** Sets position of item in DOM
31350 * @param {Element} item
31351 * @param {Number} x - horizontal position
31352 * @param {Number} y - vertical position
31353 * @param {Boolean} isInstant - disables transitions
31355 _processVerticalLayoutQueue : function( queue, isInstant )
31357 var pos = this.el.getBox(true);
31362 for (var i = 0; i < this.cols; i++){
31366 Roo.each(queue, function(box, k){
31368 var col = k % this.cols;
31370 Roo.each(box, function(b,kk){
31372 b.el.position('absolute');
31374 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31375 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31377 if(b.size == 'md-left' || b.size == 'md-right'){
31378 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31379 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31382 b.el.setWidth(width);
31383 b.el.setHeight(height);
31385 b.el.select('iframe',true).setSize(width,height);
31389 for (var i = 0; i < this.cols; i++){
31391 if(maxY[i] < maxY[col]){
31396 col = Math.min(col, i);
31400 x = pos.x + col * (this.colWidth + this.padWidth);
31404 var positions = [];
31406 switch (box.length){
31408 positions = this.getVerticalOneBoxColPositions(x, y, box);
31411 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31414 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31417 positions = this.getVerticalFourBoxColPositions(x, y, box);
31423 Roo.each(box, function(b,kk){
31425 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31427 var sz = b.el.getSize();
31429 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31437 for (var i = 0; i < this.cols; i++){
31438 mY = Math.max(mY, maxY[i]);
31441 this.el.setHeight(mY - pos.y);
31445 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31447 // var pos = this.el.getBox(true);
31450 // var maxX = pos.right;
31452 // var maxHeight = 0;
31454 // Roo.each(items, function(item, k){
31458 // item.el.position('absolute');
31460 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31462 // item.el.setWidth(width);
31464 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31466 // item.el.setHeight(height);
31469 // item.el.setXY([x, y], isInstant ? false : true);
31471 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31474 // y = y + height + this.alternativePadWidth;
31476 // maxHeight = maxHeight + height + this.alternativePadWidth;
31480 // this.el.setHeight(maxHeight);
31484 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31486 var pos = this.el.getBox(true);
31491 var maxX = pos.right;
31493 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31495 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31497 Roo.each(queue, function(box, k){
31499 Roo.each(box, function(b, kk){
31501 b.el.position('absolute');
31503 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31504 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31506 if(b.size == 'md-left' || b.size == 'md-right'){
31507 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31508 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31511 b.el.setWidth(width);
31512 b.el.setHeight(height);
31520 var positions = [];
31522 switch (box.length){
31524 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31527 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31530 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31533 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31539 Roo.each(box, function(b,kk){
31541 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31543 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31551 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31553 Roo.each(eItems, function(b,k){
31555 b.size = (k == 0) ? 'sm' : 'xs';
31556 b.x = (k == 0) ? 2 : 1;
31557 b.y = (k == 0) ? 2 : 1;
31559 b.el.position('absolute');
31561 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31563 b.el.setWidth(width);
31565 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31567 b.el.setHeight(height);
31571 var positions = [];
31574 x : maxX - this.unitWidth * 2 - this.gutter,
31579 x : maxX - this.unitWidth,
31580 y : minY + (this.unitWidth + this.gutter) * 2
31584 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31588 Roo.each(eItems, function(b,k){
31590 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31596 getVerticalOneBoxColPositions : function(x, y, box)
31600 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31602 if(box[0].size == 'md-left'){
31606 if(box[0].size == 'md-right'){
31611 x : x + (this.unitWidth + this.gutter) * rand,
31618 getVerticalTwoBoxColPositions : function(x, y, box)
31622 if(box[0].size == 'xs'){
31626 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31630 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31644 x : x + (this.unitWidth + this.gutter) * 2,
31645 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31652 getVerticalThreeBoxColPositions : function(x, y, box)
31656 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31664 x : x + (this.unitWidth + this.gutter) * 1,
31669 x : x + (this.unitWidth + this.gutter) * 2,
31677 if(box[0].size == 'xs' && box[1].size == 'xs'){
31686 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31690 x : x + (this.unitWidth + this.gutter) * 1,
31704 x : x + (this.unitWidth + this.gutter) * 2,
31709 x : x + (this.unitWidth + this.gutter) * 2,
31710 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31717 getVerticalFourBoxColPositions : function(x, y, box)
31721 if(box[0].size == 'xs'){
31730 y : y + (this.unitHeight + this.gutter) * 1
31735 y : y + (this.unitHeight + this.gutter) * 2
31739 x : x + (this.unitWidth + this.gutter) * 1,
31753 x : x + (this.unitWidth + this.gutter) * 2,
31758 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31759 y : y + (this.unitHeight + this.gutter) * 1
31763 x : x + (this.unitWidth + this.gutter) * 2,
31764 y : y + (this.unitWidth + this.gutter) * 2
31771 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31775 if(box[0].size == 'md-left'){
31777 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31784 if(box[0].size == 'md-right'){
31786 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31787 y : minY + (this.unitWidth + this.gutter) * 1
31793 var rand = Math.floor(Math.random() * (4 - box[0].y));
31796 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31797 y : minY + (this.unitWidth + this.gutter) * rand
31804 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31808 if(box[0].size == 'xs'){
31811 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31816 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31817 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31825 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31830 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31831 y : minY + (this.unitWidth + this.gutter) * 2
31838 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31842 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31845 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31850 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31851 y : minY + (this.unitWidth + this.gutter) * 1
31855 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31856 y : minY + (this.unitWidth + this.gutter) * 2
31863 if(box[0].size == 'xs' && box[1].size == 'xs'){
31866 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31871 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31876 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31877 y : minY + (this.unitWidth + this.gutter) * 1
31885 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31890 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31891 y : minY + (this.unitWidth + this.gutter) * 2
31895 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31896 y : minY + (this.unitWidth + this.gutter) * 2
31903 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31907 if(box[0].size == 'xs'){
31910 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31915 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31920 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),
31925 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31926 y : minY + (this.unitWidth + this.gutter) * 1
31934 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31939 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31940 y : minY + (this.unitWidth + this.gutter) * 2
31944 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31945 y : minY + (this.unitWidth + this.gutter) * 2
31949 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),
31950 y : minY + (this.unitWidth + this.gutter) * 2
31958 * remove a Masonry Brick
31959 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31961 removeBrick : function(brick_id)
31967 for (var i = 0; i<this.bricks.length; i++) {
31968 if (this.bricks[i].id == brick_id) {
31969 this.bricks.splice(i,1);
31970 this.el.dom.removeChild(Roo.get(brick_id).dom);
31977 * adds a Masonry Brick
31978 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31980 addBrick : function(cfg)
31982 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31983 //this.register(cn);
31984 cn.parentId = this.id;
31985 cn.render(this.el);
31990 * register a Masonry Brick
31991 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31994 register : function(brick)
31996 this.bricks.push(brick);
31997 brick.masonryId = this.id;
32001 * clear all the Masonry Brick
32003 clearAll : function()
32006 //this.getChildContainer().dom.innerHTML = "";
32007 this.el.dom.innerHTML = '';
32010 getSelected : function()
32012 if (!this.selectedBrick) {
32016 return this.selectedBrick;
32020 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32024 * register a Masonry Layout
32025 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32028 register : function(layout)
32030 this.groups[layout.id] = layout;
32033 * fetch a Masonry Layout based on the masonry layout ID
32034 * @param {string} the masonry layout to add
32035 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32038 get: function(layout_id) {
32039 if (typeof(this.groups[layout_id]) == 'undefined') {
32042 return this.groups[layout_id] ;
32054 * http://masonry.desandro.com
32056 * The idea is to render all the bricks based on vertical width...
32058 * The original code extends 'outlayer' - we might need to use that....
32064 * @class Roo.bootstrap.LayoutMasonryAuto
32065 * @extends Roo.bootstrap.Component
32066 * Bootstrap Layout Masonry class
32069 * Create a new Element
32070 * @param {Object} config The config object
32073 Roo.bootstrap.LayoutMasonryAuto = function(config){
32074 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32077 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32080 * @cfg {Boolean} isFitWidth - resize the width..
32082 isFitWidth : false, // options..
32084 * @cfg {Boolean} isOriginLeft = left align?
32086 isOriginLeft : true,
32088 * @cfg {Boolean} isOriginTop = top align?
32090 isOriginTop : false,
32092 * @cfg {Boolean} isLayoutInstant = no animation?
32094 isLayoutInstant : false, // needed?
32096 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32098 isResizingContainer : true,
32100 * @cfg {Number} columnWidth width of the columns
32106 * @cfg {Number} maxCols maximum number of columns
32111 * @cfg {Number} padHeight padding below box..
32117 * @cfg {Boolean} isAutoInitial defalut true
32120 isAutoInitial : true,
32126 initialColumnWidth : 0,
32127 currentSize : null,
32129 colYs : null, // array.
32136 bricks: null, //CompositeElement
32137 cols : 0, // array?
32138 // element : null, // wrapped now this.el
32139 _isLayoutInited : null,
32142 getAutoCreate : function(){
32146 cls: 'blog-masonary-wrapper ' + this.cls,
32148 cls : 'mas-boxes masonary'
32155 getChildContainer: function( )
32157 if (this.boxesEl) {
32158 return this.boxesEl;
32161 this.boxesEl = this.el.select('.mas-boxes').first();
32163 return this.boxesEl;
32167 initEvents : function()
32171 if(this.isAutoInitial){
32172 Roo.log('hook children rendered');
32173 this.on('childrenrendered', function() {
32174 Roo.log('children rendered');
32181 initial : function()
32183 this.reloadItems();
32185 this.currentSize = this.el.getBox(true);
32187 /// was window resize... - let's see if this works..
32188 Roo.EventManager.onWindowResize(this.resize, this);
32190 if(!this.isAutoInitial){
32195 this.layout.defer(500,this);
32198 reloadItems: function()
32200 this.bricks = this.el.select('.masonry-brick', true);
32202 this.bricks.each(function(b) {
32203 //Roo.log(b.getSize());
32204 if (!b.attr('originalwidth')) {
32205 b.attr('originalwidth', b.getSize().width);
32210 Roo.log(this.bricks.elements.length);
32213 resize : function()
32216 var cs = this.el.getBox(true);
32218 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32219 Roo.log("no change in with or X");
32222 this.currentSize = cs;
32226 layout : function()
32229 this._resetLayout();
32230 //this._manageStamps();
32232 // don't animate first layout
32233 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32234 this.layoutItems( isInstant );
32236 // flag for initalized
32237 this._isLayoutInited = true;
32240 layoutItems : function( isInstant )
32242 //var items = this._getItemsForLayout( this.items );
32243 // original code supports filtering layout items.. we just ignore it..
32245 this._layoutItems( this.bricks , isInstant );
32247 this._postLayout();
32249 _layoutItems : function ( items , isInstant)
32251 //this.fireEvent( 'layout', this, items );
32254 if ( !items || !items.elements.length ) {
32255 // no items, emit event with empty array
32260 items.each(function(item) {
32261 Roo.log("layout item");
32263 // get x/y object from method
32264 var position = this._getItemLayoutPosition( item );
32266 position.item = item;
32267 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32268 queue.push( position );
32271 this._processLayoutQueue( queue );
32273 /** Sets position of item in DOM
32274 * @param {Element} item
32275 * @param {Number} x - horizontal position
32276 * @param {Number} y - vertical position
32277 * @param {Boolean} isInstant - disables transitions
32279 _processLayoutQueue : function( queue )
32281 for ( var i=0, len = queue.length; i < len; i++ ) {
32282 var obj = queue[i];
32283 obj.item.position('absolute');
32284 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32290 * Any logic you want to do after each layout,
32291 * i.e. size the container
32293 _postLayout : function()
32295 this.resizeContainer();
32298 resizeContainer : function()
32300 if ( !this.isResizingContainer ) {
32303 var size = this._getContainerSize();
32305 this.el.setSize(size.width,size.height);
32306 this.boxesEl.setSize(size.width,size.height);
32312 _resetLayout : function()
32314 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32315 this.colWidth = this.el.getWidth();
32316 //this.gutter = this.el.getWidth();
32318 this.measureColumns();
32324 this.colYs.push( 0 );
32330 measureColumns : function()
32332 this.getContainerWidth();
32333 // if columnWidth is 0, default to outerWidth of first item
32334 if ( !this.columnWidth ) {
32335 var firstItem = this.bricks.first();
32336 Roo.log(firstItem);
32337 this.columnWidth = this.containerWidth;
32338 if (firstItem && firstItem.attr('originalwidth') ) {
32339 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32341 // columnWidth fall back to item of first element
32342 Roo.log("set column width?");
32343 this.initialColumnWidth = this.columnWidth ;
32345 // if first elem has no width, default to size of container
32350 if (this.initialColumnWidth) {
32351 this.columnWidth = this.initialColumnWidth;
32356 // column width is fixed at the top - however if container width get's smaller we should
32359 // this bit calcs how man columns..
32361 var columnWidth = this.columnWidth += this.gutter;
32363 // calculate columns
32364 var containerWidth = this.containerWidth + this.gutter;
32366 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32367 // fix rounding errors, typically with gutters
32368 var excess = columnWidth - containerWidth % columnWidth;
32371 // if overshoot is less than a pixel, round up, otherwise floor it
32372 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32373 cols = Math[ mathMethod ]( cols );
32374 this.cols = Math.max( cols, 1 );
32375 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32377 // padding positioning..
32378 var totalColWidth = this.cols * this.columnWidth;
32379 var padavail = this.containerWidth - totalColWidth;
32380 // so for 2 columns - we need 3 'pads'
32382 var padNeeded = (1+this.cols) * this.padWidth;
32384 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32386 this.columnWidth += padExtra
32387 //this.padWidth = Math.floor(padavail / ( this.cols));
32389 // adjust colum width so that padding is fixed??
32391 // we have 3 columns ... total = width * 3
32392 // we have X left over... that should be used by
32394 //if (this.expandC) {
32402 getContainerWidth : function()
32404 /* // container is parent if fit width
32405 var container = this.isFitWidth ? this.element.parentNode : this.element;
32406 // check that this.size and size are there
32407 // IE8 triggers resize on body size change, so they might not be
32409 var size = getSize( container ); //FIXME
32410 this.containerWidth = size && size.innerWidth; //FIXME
32413 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32417 _getItemLayoutPosition : function( item ) // what is item?
32419 // we resize the item to our columnWidth..
32421 item.setWidth(this.columnWidth);
32422 item.autoBoxAdjust = false;
32424 var sz = item.getSize();
32426 // how many columns does this brick span
32427 var remainder = this.containerWidth % this.columnWidth;
32429 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32430 // round if off by 1 pixel, otherwise use ceil
32431 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32432 colSpan = Math.min( colSpan, this.cols );
32434 // normally this should be '1' as we dont' currently allow multi width columns..
32436 var colGroup = this._getColGroup( colSpan );
32437 // get the minimum Y value from the columns
32438 var minimumY = Math.min.apply( Math, colGroup );
32439 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32441 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32443 // position the brick
32445 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32446 y: this.currentSize.y + minimumY + this.padHeight
32450 // apply setHeight to necessary columns
32451 var setHeight = minimumY + sz.height + this.padHeight;
32452 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32454 var setSpan = this.cols + 1 - colGroup.length;
32455 for ( var i = 0; i < setSpan; i++ ) {
32456 this.colYs[ shortColIndex + i ] = setHeight ;
32463 * @param {Number} colSpan - number of columns the element spans
32464 * @returns {Array} colGroup
32466 _getColGroup : function( colSpan )
32468 if ( colSpan < 2 ) {
32469 // if brick spans only one column, use all the column Ys
32474 // how many different places could this brick fit horizontally
32475 var groupCount = this.cols + 1 - colSpan;
32476 // for each group potential horizontal position
32477 for ( var i = 0; i < groupCount; i++ ) {
32478 // make an array of colY values for that one group
32479 var groupColYs = this.colYs.slice( i, i + colSpan );
32480 // and get the max value of the array
32481 colGroup[i] = Math.max.apply( Math, groupColYs );
32486 _manageStamp : function( stamp )
32488 var stampSize = stamp.getSize();
32489 var offset = stamp.getBox();
32490 // get the columns that this stamp affects
32491 var firstX = this.isOriginLeft ? offset.x : offset.right;
32492 var lastX = firstX + stampSize.width;
32493 var firstCol = Math.floor( firstX / this.columnWidth );
32494 firstCol = Math.max( 0, firstCol );
32496 var lastCol = Math.floor( lastX / this.columnWidth );
32497 // lastCol should not go over if multiple of columnWidth #425
32498 lastCol -= lastX % this.columnWidth ? 0 : 1;
32499 lastCol = Math.min( this.cols - 1, lastCol );
32501 // set colYs to bottom of the stamp
32502 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32505 for ( var i = firstCol; i <= lastCol; i++ ) {
32506 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32511 _getContainerSize : function()
32513 this.maxY = Math.max.apply( Math, this.colYs );
32518 if ( this.isFitWidth ) {
32519 size.width = this._getContainerFitWidth();
32525 _getContainerFitWidth : function()
32527 var unusedCols = 0;
32528 // count unused columns
32531 if ( this.colYs[i] !== 0 ) {
32536 // fit container to columns that have been used
32537 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32540 needsResizeLayout : function()
32542 var previousWidth = this.containerWidth;
32543 this.getContainerWidth();
32544 return previousWidth !== this.containerWidth;
32559 * @class Roo.bootstrap.MasonryBrick
32560 * @extends Roo.bootstrap.Component
32561 * Bootstrap MasonryBrick class
32564 * Create a new MasonryBrick
32565 * @param {Object} config The config object
32568 Roo.bootstrap.MasonryBrick = function(config){
32570 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32572 Roo.bootstrap.MasonryBrick.register(this);
32578 * When a MasonryBrick is clcik
32579 * @param {Roo.bootstrap.MasonryBrick} this
32580 * @param {Roo.EventObject} e
32586 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32589 * @cfg {String} title
32593 * @cfg {String} html
32597 * @cfg {String} bgimage
32601 * @cfg {String} videourl
32605 * @cfg {String} cls
32609 * @cfg {String} href
32613 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32618 * @cfg {String} placetitle (center|bottom)
32623 * @cfg {Boolean} isFitContainer defalut true
32625 isFitContainer : true,
32628 * @cfg {Boolean} preventDefault defalut false
32630 preventDefault : false,
32633 * @cfg {Boolean} inverse defalut false
32635 maskInverse : false,
32637 getAutoCreate : function()
32639 if(!this.isFitContainer){
32640 return this.getSplitAutoCreate();
32643 var cls = 'masonry-brick masonry-brick-full';
32645 if(this.href.length){
32646 cls += ' masonry-brick-link';
32649 if(this.bgimage.length){
32650 cls += ' masonry-brick-image';
32653 if(this.maskInverse){
32654 cls += ' mask-inverse';
32657 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32658 cls += ' enable-mask';
32662 cls += ' masonry-' + this.size + '-brick';
32665 if(this.placetitle.length){
32667 switch (this.placetitle) {
32669 cls += ' masonry-center-title';
32672 cls += ' masonry-bottom-title';
32679 if(!this.html.length && !this.bgimage.length){
32680 cls += ' masonry-center-title';
32683 if(!this.html.length && this.bgimage.length){
32684 cls += ' masonry-bottom-title';
32689 cls += ' ' + this.cls;
32693 tag: (this.href.length) ? 'a' : 'div',
32698 cls: 'masonry-brick-mask'
32702 cls: 'masonry-brick-paragraph',
32708 if(this.href.length){
32709 cfg.href = this.href;
32712 var cn = cfg.cn[1].cn;
32714 if(this.title.length){
32717 cls: 'masonry-brick-title',
32722 if(this.html.length){
32725 cls: 'masonry-brick-text',
32730 if (!this.title.length && !this.html.length) {
32731 cfg.cn[1].cls += ' hide';
32734 if(this.bgimage.length){
32737 cls: 'masonry-brick-image-view',
32742 if(this.videourl.length){
32743 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32744 // youtube support only?
32747 cls: 'masonry-brick-image-view',
32750 allowfullscreen : true
32758 getSplitAutoCreate : function()
32760 var cls = 'masonry-brick masonry-brick-split';
32762 if(this.href.length){
32763 cls += ' masonry-brick-link';
32766 if(this.bgimage.length){
32767 cls += ' masonry-brick-image';
32771 cls += ' masonry-' + this.size + '-brick';
32774 switch (this.placetitle) {
32776 cls += ' masonry-center-title';
32779 cls += ' masonry-bottom-title';
32782 if(!this.bgimage.length){
32783 cls += ' masonry-center-title';
32786 if(this.bgimage.length){
32787 cls += ' masonry-bottom-title';
32793 cls += ' ' + this.cls;
32797 tag: (this.href.length) ? 'a' : 'div',
32802 cls: 'masonry-brick-split-head',
32806 cls: 'masonry-brick-paragraph',
32813 cls: 'masonry-brick-split-body',
32819 if(this.href.length){
32820 cfg.href = this.href;
32823 if(this.title.length){
32824 cfg.cn[0].cn[0].cn.push({
32826 cls: 'masonry-brick-title',
32831 if(this.html.length){
32832 cfg.cn[1].cn.push({
32834 cls: 'masonry-brick-text',
32839 if(this.bgimage.length){
32840 cfg.cn[0].cn.push({
32842 cls: 'masonry-brick-image-view',
32847 if(this.videourl.length){
32848 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32849 // youtube support only?
32850 cfg.cn[0].cn.cn.push({
32852 cls: 'masonry-brick-image-view',
32855 allowfullscreen : true
32862 initEvents: function()
32864 switch (this.size) {
32897 this.el.on('touchstart', this.onTouchStart, this);
32898 this.el.on('touchmove', this.onTouchMove, this);
32899 this.el.on('touchend', this.onTouchEnd, this);
32900 this.el.on('contextmenu', this.onContextMenu, this);
32902 this.el.on('mouseenter' ,this.enter, this);
32903 this.el.on('mouseleave', this.leave, this);
32904 this.el.on('click', this.onClick, this);
32907 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32908 this.parent().bricks.push(this);
32913 onClick: function(e, el)
32915 var time = this.endTimer - this.startTimer;
32916 // Roo.log(e.preventDefault());
32919 e.preventDefault();
32924 if(!this.preventDefault){
32928 e.preventDefault();
32930 if (this.activeClass != '') {
32931 this.selectBrick();
32934 this.fireEvent('click', this, e);
32937 enter: function(e, el)
32939 e.preventDefault();
32941 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32945 if(this.bgimage.length && this.html.length){
32946 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32950 leave: function(e, el)
32952 e.preventDefault();
32954 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32958 if(this.bgimage.length && this.html.length){
32959 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32963 onTouchStart: function(e, el)
32965 // e.preventDefault();
32967 this.touchmoved = false;
32969 if(!this.isFitContainer){
32973 if(!this.bgimage.length || !this.html.length){
32977 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32979 this.timer = new Date().getTime();
32983 onTouchMove: function(e, el)
32985 this.touchmoved = true;
32988 onContextMenu : function(e,el)
32990 e.preventDefault();
32991 e.stopPropagation();
32995 onTouchEnd: function(e, el)
32997 // e.preventDefault();
32999 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33006 if(!this.bgimage.length || !this.html.length){
33008 if(this.href.length){
33009 window.location.href = this.href;
33015 if(!this.isFitContainer){
33019 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33021 window.location.href = this.href;
33024 //selection on single brick only
33025 selectBrick : function() {
33027 if (!this.parentId) {
33031 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33032 var index = m.selectedBrick.indexOf(this.id);
33035 m.selectedBrick.splice(index,1);
33036 this.el.removeClass(this.activeClass);
33040 for(var i = 0; i < m.selectedBrick.length; i++) {
33041 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33042 b.el.removeClass(b.activeClass);
33045 m.selectedBrick = [];
33047 m.selectedBrick.push(this.id);
33048 this.el.addClass(this.activeClass);
33052 isSelected : function(){
33053 return this.el.hasClass(this.activeClass);
33058 Roo.apply(Roo.bootstrap.MasonryBrick, {
33061 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33063 * register a Masonry Brick
33064 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33067 register : function(brick)
33069 //this.groups[brick.id] = brick;
33070 this.groups.add(brick.id, brick);
33073 * fetch a masonry brick based on the masonry brick ID
33074 * @param {string} the masonry brick to add
33075 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33078 get: function(brick_id)
33080 // if (typeof(this.groups[brick_id]) == 'undefined') {
33083 // return this.groups[brick_id] ;
33085 if(this.groups.key(brick_id)) {
33086 return this.groups.key(brick_id);
33104 * @class Roo.bootstrap.Brick
33105 * @extends Roo.bootstrap.Component
33106 * Bootstrap Brick class
33109 * Create a new Brick
33110 * @param {Object} config The config object
33113 Roo.bootstrap.Brick = function(config){
33114 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33120 * When a Brick is click
33121 * @param {Roo.bootstrap.Brick} this
33122 * @param {Roo.EventObject} e
33128 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33131 * @cfg {String} title
33135 * @cfg {String} html
33139 * @cfg {String} bgimage
33143 * @cfg {String} cls
33147 * @cfg {String} href
33151 * @cfg {String} video
33155 * @cfg {Boolean} square
33159 getAutoCreate : function()
33161 var cls = 'roo-brick';
33163 if(this.href.length){
33164 cls += ' roo-brick-link';
33167 if(this.bgimage.length){
33168 cls += ' roo-brick-image';
33171 if(!this.html.length && !this.bgimage.length){
33172 cls += ' roo-brick-center-title';
33175 if(!this.html.length && this.bgimage.length){
33176 cls += ' roo-brick-bottom-title';
33180 cls += ' ' + this.cls;
33184 tag: (this.href.length) ? 'a' : 'div',
33189 cls: 'roo-brick-paragraph',
33195 if(this.href.length){
33196 cfg.href = this.href;
33199 var cn = cfg.cn[0].cn;
33201 if(this.title.length){
33204 cls: 'roo-brick-title',
33209 if(this.html.length){
33212 cls: 'roo-brick-text',
33219 if(this.bgimage.length){
33222 cls: 'roo-brick-image-view',
33230 initEvents: function()
33232 if(this.title.length || this.html.length){
33233 this.el.on('mouseenter' ,this.enter, this);
33234 this.el.on('mouseleave', this.leave, this);
33237 Roo.EventManager.onWindowResize(this.resize, this);
33239 if(this.bgimage.length){
33240 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33241 this.imageEl.on('load', this.onImageLoad, this);
33248 onImageLoad : function()
33253 resize : function()
33255 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33257 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33259 if(this.bgimage.length){
33260 var image = this.el.select('.roo-brick-image-view', true).first();
33262 image.setWidth(paragraph.getWidth());
33265 image.setHeight(paragraph.getWidth());
33268 this.el.setHeight(image.getHeight());
33269 paragraph.setHeight(image.getHeight());
33275 enter: function(e, el)
33277 e.preventDefault();
33279 if(this.bgimage.length){
33280 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33281 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33285 leave: function(e, el)
33287 e.preventDefault();
33289 if(this.bgimage.length){
33290 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33291 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33306 * @class Roo.bootstrap.NumberField
33307 * @extends Roo.bootstrap.Input
33308 * Bootstrap NumberField class
33314 * Create a new NumberField
33315 * @param {Object} config The config object
33318 Roo.bootstrap.NumberField = function(config){
33319 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33322 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33325 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33327 allowDecimals : true,
33329 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33331 decimalSeparator : ".",
33333 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33335 decimalPrecision : 2,
33337 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33339 allowNegative : true,
33342 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33346 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33348 minValue : Number.NEGATIVE_INFINITY,
33350 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33352 maxValue : Number.MAX_VALUE,
33354 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33356 minText : "The minimum value for this field is {0}",
33358 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33360 maxText : "The maximum value for this field is {0}",
33362 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33363 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33365 nanText : "{0} is not a valid number",
33367 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33369 thousandsDelimiter : false,
33371 * @cfg {String} valueAlign alignment of value
33373 valueAlign : "left",
33375 getAutoCreate : function()
33377 var hiddenInput = {
33381 cls: 'hidden-number-input'
33385 hiddenInput.name = this.name;
33390 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33392 this.name = hiddenInput.name;
33394 if(cfg.cn.length > 0) {
33395 cfg.cn.push(hiddenInput);
33402 initEvents : function()
33404 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33406 var allowed = "0123456789";
33408 if(this.allowDecimals){
33409 allowed += this.decimalSeparator;
33412 if(this.allowNegative){
33416 if(this.thousandsDelimiter) {
33420 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33422 var keyPress = function(e){
33424 var k = e.getKey();
33426 var c = e.getCharCode();
33429 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33430 allowed.indexOf(String.fromCharCode(c)) === -1
33436 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33440 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33445 this.el.on("keypress", keyPress, this);
33448 validateValue : function(value)
33451 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33455 var num = this.parseValue(value);
33458 this.markInvalid(String.format(this.nanText, value));
33462 if(num < this.minValue){
33463 this.markInvalid(String.format(this.minText, this.minValue));
33467 if(num > this.maxValue){
33468 this.markInvalid(String.format(this.maxText, this.maxValue));
33475 getValue : function()
33477 var v = this.hiddenEl().getValue();
33479 return this.fixPrecision(this.parseValue(v));
33482 parseValue : function(value)
33484 if(this.thousandsDelimiter) {
33486 r = new RegExp(",", "g");
33487 value = value.replace(r, "");
33490 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33491 return isNaN(value) ? '' : value;
33494 fixPrecision : function(value)
33496 if(this.thousandsDelimiter) {
33498 r = new RegExp(",", "g");
33499 value = value.replace(r, "");
33502 var nan = isNaN(value);
33504 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33505 return nan ? '' : value;
33507 return parseFloat(value).toFixed(this.decimalPrecision);
33510 setValue : function(v)
33512 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33518 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33520 this.inputEl().dom.value = (v == '') ? '' :
33521 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33523 if(!this.allowZero && v === '0') {
33524 this.hiddenEl().dom.value = '';
33525 this.inputEl().dom.value = '';
33532 decimalPrecisionFcn : function(v)
33534 return Math.floor(v);
33537 beforeBlur : function()
33539 var v = this.parseValue(this.getRawValue());
33541 if(v || v === 0 || v === ''){
33546 hiddenEl : function()
33548 return this.el.select('input.hidden-number-input',true).first();
33560 * @class Roo.bootstrap.DocumentSlider
33561 * @extends Roo.bootstrap.Component
33562 * Bootstrap DocumentSlider class
33565 * Create a new DocumentViewer
33566 * @param {Object} config The config object
33569 Roo.bootstrap.DocumentSlider = function(config){
33570 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33577 * Fire after initEvent
33578 * @param {Roo.bootstrap.DocumentSlider} this
33583 * Fire after update
33584 * @param {Roo.bootstrap.DocumentSlider} this
33590 * @param {Roo.bootstrap.DocumentSlider} this
33596 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33602 getAutoCreate : function()
33606 cls : 'roo-document-slider',
33610 cls : 'roo-document-slider-header',
33614 cls : 'roo-document-slider-header-title'
33620 cls : 'roo-document-slider-body',
33624 cls : 'roo-document-slider-prev',
33628 cls : 'fa fa-chevron-left'
33634 cls : 'roo-document-slider-thumb',
33638 cls : 'roo-document-slider-image'
33644 cls : 'roo-document-slider-next',
33648 cls : 'fa fa-chevron-right'
33660 initEvents : function()
33662 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33663 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33665 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33666 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33668 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33669 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33671 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33672 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33674 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33675 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33677 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33678 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33680 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33681 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33683 this.thumbEl.on('click', this.onClick, this);
33685 this.prevIndicator.on('click', this.prev, this);
33687 this.nextIndicator.on('click', this.next, this);
33691 initial : function()
33693 if(this.files.length){
33694 this.indicator = 1;
33698 this.fireEvent('initial', this);
33701 update : function()
33703 this.imageEl.attr('src', this.files[this.indicator - 1]);
33705 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33707 this.prevIndicator.show();
33709 if(this.indicator == 1){
33710 this.prevIndicator.hide();
33713 this.nextIndicator.show();
33715 if(this.indicator == this.files.length){
33716 this.nextIndicator.hide();
33719 this.thumbEl.scrollTo('top');
33721 this.fireEvent('update', this);
33724 onClick : function(e)
33726 e.preventDefault();
33728 this.fireEvent('click', this);
33733 e.preventDefault();
33735 this.indicator = Math.max(1, this.indicator - 1);
33742 e.preventDefault();
33744 this.indicator = Math.min(this.files.length, this.indicator + 1);
33758 * @class Roo.bootstrap.RadioSet
33759 * @extends Roo.bootstrap.Input
33760 * Bootstrap RadioSet class
33761 * @cfg {String} indicatorpos (left|right) default left
33762 * @cfg {Boolean} inline (true|false) inline the element (default true)
33763 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33765 * Create a new RadioSet
33766 * @param {Object} config The config object
33769 Roo.bootstrap.RadioSet = function(config){
33771 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33775 Roo.bootstrap.RadioSet.register(this);
33780 * Fires when the element is checked or unchecked.
33781 * @param {Roo.bootstrap.RadioSet} this This radio
33782 * @param {Roo.bootstrap.Radio} item The checked item
33787 * Fires when the element is click.
33788 * @param {Roo.bootstrap.RadioSet} this This radio set
33789 * @param {Roo.bootstrap.Radio} item The checked item
33790 * @param {Roo.EventObject} e The event object
33797 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33805 indicatorpos : 'left',
33807 getAutoCreate : function()
33811 cls : 'roo-radio-set-label',
33815 html : this.fieldLabel
33820 if(this.indicatorpos == 'left'){
33823 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33824 tooltip : 'This field is required'
33829 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33830 tooltip : 'This field is required'
33836 cls : 'roo-radio-set-items'
33839 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33841 if (align === 'left' && this.fieldLabel.length) {
33844 cls : "roo-radio-set-right",
33850 if(this.labelWidth > 12){
33851 label.style = "width: " + this.labelWidth + 'px';
33854 if(this.labelWidth < 13 && this.labelmd == 0){
33855 this.labelmd = this.labelWidth;
33858 if(this.labellg > 0){
33859 label.cls += ' col-lg-' + this.labellg;
33860 items.cls += ' col-lg-' + (12 - this.labellg);
33863 if(this.labelmd > 0){
33864 label.cls += ' col-md-' + this.labelmd;
33865 items.cls += ' col-md-' + (12 - this.labelmd);
33868 if(this.labelsm > 0){
33869 label.cls += ' col-sm-' + this.labelsm;
33870 items.cls += ' col-sm-' + (12 - this.labelsm);
33873 if(this.labelxs > 0){
33874 label.cls += ' col-xs-' + this.labelxs;
33875 items.cls += ' col-xs-' + (12 - this.labelxs);
33881 cls : 'roo-radio-set',
33885 cls : 'roo-radio-set-input',
33888 value : this.value ? this.value : ''
33895 if(this.weight.length){
33896 cfg.cls += ' roo-radio-' + this.weight;
33900 cfg.cls += ' roo-radio-set-inline';
33904 ['xs','sm','md','lg'].map(function(size){
33905 if (settings[size]) {
33906 cfg.cls += ' col-' + size + '-' + settings[size];
33914 initEvents : function()
33916 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33917 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33919 if(!this.fieldLabel.length){
33920 this.labelEl.hide();
33923 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33924 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33926 this.indicator = this.indicatorEl();
33928 if(this.indicator){
33929 this.indicator.addClass('invisible');
33932 this.originalValue = this.getValue();
33936 inputEl: function ()
33938 return this.el.select('.roo-radio-set-input', true).first();
33941 getChildContainer : function()
33943 return this.itemsEl;
33946 register : function(item)
33948 this.radioes.push(item);
33952 validate : function()
33954 if(this.getVisibilityEl().hasClass('hidden')){
33960 Roo.each(this.radioes, function(i){
33969 if(this.allowBlank) {
33973 if(this.disabled || valid){
33978 this.markInvalid();
33983 markValid : function()
33985 if(this.labelEl.isVisible(true)){
33986 this.indicatorEl().removeClass('visible');
33987 this.indicatorEl().addClass('invisible');
33990 this.el.removeClass([this.invalidClass, this.validClass]);
33991 this.el.addClass(this.validClass);
33993 this.fireEvent('valid', this);
33996 markInvalid : function(msg)
33998 if(this.allowBlank || this.disabled){
34002 if(this.labelEl.isVisible(true)){
34003 this.indicatorEl().removeClass('invisible');
34004 this.indicatorEl().addClass('visible');
34007 this.el.removeClass([this.invalidClass, this.validClass]);
34008 this.el.addClass(this.invalidClass);
34010 this.fireEvent('invalid', this, msg);
34014 setValue : function(v, suppressEvent)
34016 if(this.value === v){
34023 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34026 Roo.each(this.radioes, function(i){
34028 i.el.removeClass('checked');
34031 Roo.each(this.radioes, function(i){
34033 if(i.value === v || i.value.toString() === v.toString()){
34035 i.el.addClass('checked');
34037 if(suppressEvent !== true){
34038 this.fireEvent('check', this, i);
34049 clearInvalid : function(){
34051 if(!this.el || this.preventMark){
34055 this.el.removeClass([this.invalidClass]);
34057 this.fireEvent('valid', this);
34062 Roo.apply(Roo.bootstrap.RadioSet, {
34066 register : function(set)
34068 this.groups[set.name] = set;
34071 get: function(name)
34073 if (typeof(this.groups[name]) == 'undefined') {
34077 return this.groups[name] ;
34083 * Ext JS Library 1.1.1
34084 * Copyright(c) 2006-2007, Ext JS, LLC.
34086 * Originally Released Under LGPL - original licence link has changed is not relivant.
34089 * <script type="text/javascript">
34094 * @class Roo.bootstrap.SplitBar
34095 * @extends Roo.util.Observable
34096 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34100 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34101 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34102 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34103 split.minSize = 100;
34104 split.maxSize = 600;
34105 split.animate = true;
34106 split.on('moved', splitterMoved);
34109 * Create a new SplitBar
34110 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34111 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34112 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34113 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34114 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34115 position of the SplitBar).
34117 Roo.bootstrap.SplitBar = function(cfg){
34122 // dragElement : elm
34123 // resizingElement: el,
34125 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34126 // placement : Roo.bootstrap.SplitBar.LEFT ,
34127 // existingProxy ???
34130 this.el = Roo.get(cfg.dragElement, true);
34131 this.el.dom.unselectable = "on";
34133 this.resizingEl = Roo.get(cfg.resizingElement, true);
34137 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34138 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34141 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34144 * The minimum size of the resizing element. (Defaults to 0)
34150 * The maximum size of the resizing element. (Defaults to 2000)
34153 this.maxSize = 2000;
34156 * Whether to animate the transition to the new size
34159 this.animate = false;
34162 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34165 this.useShim = false;
34170 if(!cfg.existingProxy){
34172 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34174 this.proxy = Roo.get(cfg.existingProxy).dom;
34177 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34180 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34183 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34186 this.dragSpecs = {};
34189 * @private The adapter to use to positon and resize elements
34191 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34192 this.adapter.init(this);
34194 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34196 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34197 this.el.addClass("roo-splitbar-h");
34200 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34201 this.el.addClass("roo-splitbar-v");
34207 * Fires when the splitter is moved (alias for {@link #event-moved})
34208 * @param {Roo.bootstrap.SplitBar} this
34209 * @param {Number} newSize the new width or height
34214 * Fires when the splitter is moved
34215 * @param {Roo.bootstrap.SplitBar} this
34216 * @param {Number} newSize the new width or height
34220 * @event beforeresize
34221 * Fires before the splitter is dragged
34222 * @param {Roo.bootstrap.SplitBar} this
34224 "beforeresize" : true,
34226 "beforeapply" : true
34229 Roo.util.Observable.call(this);
34232 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34233 onStartProxyDrag : function(x, y){
34234 this.fireEvent("beforeresize", this);
34236 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34238 o.enableDisplayMode("block");
34239 // all splitbars share the same overlay
34240 Roo.bootstrap.SplitBar.prototype.overlay = o;
34242 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34243 this.overlay.show();
34244 Roo.get(this.proxy).setDisplayed("block");
34245 var size = this.adapter.getElementSize(this);
34246 this.activeMinSize = this.getMinimumSize();;
34247 this.activeMaxSize = this.getMaximumSize();;
34248 var c1 = size - this.activeMinSize;
34249 var c2 = Math.max(this.activeMaxSize - size, 0);
34250 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34251 this.dd.resetConstraints();
34252 this.dd.setXConstraint(
34253 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34254 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34256 this.dd.setYConstraint(0, 0);
34258 this.dd.resetConstraints();
34259 this.dd.setXConstraint(0, 0);
34260 this.dd.setYConstraint(
34261 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34262 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34265 this.dragSpecs.startSize = size;
34266 this.dragSpecs.startPoint = [x, y];
34267 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34271 * @private Called after the drag operation by the DDProxy
34273 onEndProxyDrag : function(e){
34274 Roo.get(this.proxy).setDisplayed(false);
34275 var endPoint = Roo.lib.Event.getXY(e);
34277 this.overlay.hide();
34280 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34281 newSize = this.dragSpecs.startSize +
34282 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34283 endPoint[0] - this.dragSpecs.startPoint[0] :
34284 this.dragSpecs.startPoint[0] - endPoint[0]
34287 newSize = this.dragSpecs.startSize +
34288 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34289 endPoint[1] - this.dragSpecs.startPoint[1] :
34290 this.dragSpecs.startPoint[1] - endPoint[1]
34293 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34294 if(newSize != this.dragSpecs.startSize){
34295 if(this.fireEvent('beforeapply', this, newSize) !== false){
34296 this.adapter.setElementSize(this, newSize);
34297 this.fireEvent("moved", this, newSize);
34298 this.fireEvent("resize", this, newSize);
34304 * Get the adapter this SplitBar uses
34305 * @return The adapter object
34307 getAdapter : function(){
34308 return this.adapter;
34312 * Set the adapter this SplitBar uses
34313 * @param {Object} adapter A SplitBar adapter object
34315 setAdapter : function(adapter){
34316 this.adapter = adapter;
34317 this.adapter.init(this);
34321 * Gets the minimum size for the resizing element
34322 * @return {Number} The minimum size
34324 getMinimumSize : function(){
34325 return this.minSize;
34329 * Sets the minimum size for the resizing element
34330 * @param {Number} minSize The minimum size
34332 setMinimumSize : function(minSize){
34333 this.minSize = minSize;
34337 * Gets the maximum size for the resizing element
34338 * @return {Number} The maximum size
34340 getMaximumSize : function(){
34341 return this.maxSize;
34345 * Sets the maximum size for the resizing element
34346 * @param {Number} maxSize The maximum size
34348 setMaximumSize : function(maxSize){
34349 this.maxSize = maxSize;
34353 * Sets the initialize size for the resizing element
34354 * @param {Number} size The initial size
34356 setCurrentSize : function(size){
34357 var oldAnimate = this.animate;
34358 this.animate = false;
34359 this.adapter.setElementSize(this, size);
34360 this.animate = oldAnimate;
34364 * Destroy this splitbar.
34365 * @param {Boolean} removeEl True to remove the element
34367 destroy : function(removeEl){
34369 this.shim.remove();
34372 this.proxy.parentNode.removeChild(this.proxy);
34380 * @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.
34382 Roo.bootstrap.SplitBar.createProxy = function(dir){
34383 var proxy = new Roo.Element(document.createElement("div"));
34384 proxy.unselectable();
34385 var cls = 'roo-splitbar-proxy';
34386 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34387 document.body.appendChild(proxy.dom);
34392 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34393 * Default Adapter. It assumes the splitter and resizing element are not positioned
34394 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34396 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34399 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34400 // do nothing for now
34401 init : function(s){
34405 * Called before drag operations to get the current size of the resizing element.
34406 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34408 getElementSize : function(s){
34409 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34410 return s.resizingEl.getWidth();
34412 return s.resizingEl.getHeight();
34417 * Called after drag operations to set the size of the resizing element.
34418 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34419 * @param {Number} newSize The new size to set
34420 * @param {Function} onComplete A function to be invoked when resizing is complete
34422 setElementSize : function(s, newSize, onComplete){
34423 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34425 s.resizingEl.setWidth(newSize);
34427 onComplete(s, newSize);
34430 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34435 s.resizingEl.setHeight(newSize);
34437 onComplete(s, newSize);
34440 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34447 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34448 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34449 * Adapter that moves the splitter element to align with the resized sizing element.
34450 * Used with an absolute positioned SplitBar.
34451 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34452 * document.body, make sure you assign an id to the body element.
34454 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34455 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34456 this.container = Roo.get(container);
34459 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34460 init : function(s){
34461 this.basic.init(s);
34464 getElementSize : function(s){
34465 return this.basic.getElementSize(s);
34468 setElementSize : function(s, newSize, onComplete){
34469 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34472 moveSplitter : function(s){
34473 var yes = Roo.bootstrap.SplitBar;
34474 switch(s.placement){
34476 s.el.setX(s.resizingEl.getRight());
34479 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34482 s.el.setY(s.resizingEl.getBottom());
34485 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34492 * Orientation constant - Create a vertical SplitBar
34496 Roo.bootstrap.SplitBar.VERTICAL = 1;
34499 * Orientation constant - Create a horizontal SplitBar
34503 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34506 * Placement constant - The resizing element is to the left of the splitter element
34510 Roo.bootstrap.SplitBar.LEFT = 1;
34513 * Placement constant - The resizing element is to the right of the splitter element
34517 Roo.bootstrap.SplitBar.RIGHT = 2;
34520 * Placement constant - The resizing element is positioned above the splitter element
34524 Roo.bootstrap.SplitBar.TOP = 3;
34527 * Placement constant - The resizing element is positioned under splitter element
34531 Roo.bootstrap.SplitBar.BOTTOM = 4;
34532 Roo.namespace("Roo.bootstrap.layout");/*
34534 * Ext JS Library 1.1.1
34535 * Copyright(c) 2006-2007, Ext JS, LLC.
34537 * Originally Released Under LGPL - original licence link has changed is not relivant.
34540 * <script type="text/javascript">
34544 * @class Roo.bootstrap.layout.Manager
34545 * @extends Roo.bootstrap.Component
34546 * Base class for layout managers.
34548 Roo.bootstrap.layout.Manager = function(config)
34550 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34556 /** false to disable window resize monitoring @type Boolean */
34557 this.monitorWindowResize = true;
34562 * Fires when a layout is performed.
34563 * @param {Roo.LayoutManager} this
34567 * @event regionresized
34568 * Fires when the user resizes a region.
34569 * @param {Roo.LayoutRegion} region The resized region
34570 * @param {Number} newSize The new size (width for east/west, height for north/south)
34572 "regionresized" : true,
34574 * @event regioncollapsed
34575 * Fires when a region is collapsed.
34576 * @param {Roo.LayoutRegion} region The collapsed region
34578 "regioncollapsed" : true,
34580 * @event regionexpanded
34581 * Fires when a region is expanded.
34582 * @param {Roo.LayoutRegion} region The expanded region
34584 "regionexpanded" : true
34586 this.updating = false;
34589 this.el = Roo.get(config.el);
34595 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34600 monitorWindowResize : true,
34606 onRender : function(ct, position)
34609 this.el = Roo.get(ct);
34612 //this.fireEvent('render',this);
34616 initEvents: function()
34620 // ie scrollbar fix
34621 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34622 document.body.scroll = "no";
34623 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34624 this.el.position('relative');
34626 this.id = this.el.id;
34627 this.el.addClass("roo-layout-container");
34628 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34629 if(this.el.dom != document.body ) {
34630 this.el.on('resize', this.layout,this);
34631 this.el.on('show', this.layout,this);
34637 * Returns true if this layout is currently being updated
34638 * @return {Boolean}
34640 isUpdating : function(){
34641 return this.updating;
34645 * Suspend the LayoutManager from doing auto-layouts while
34646 * making multiple add or remove calls
34648 beginUpdate : function(){
34649 this.updating = true;
34653 * Restore auto-layouts and optionally disable the manager from performing a layout
34654 * @param {Boolean} noLayout true to disable a layout update
34656 endUpdate : function(noLayout){
34657 this.updating = false;
34663 layout: function(){
34667 onRegionResized : function(region, newSize){
34668 this.fireEvent("regionresized", region, newSize);
34672 onRegionCollapsed : function(region){
34673 this.fireEvent("regioncollapsed", region);
34676 onRegionExpanded : function(region){
34677 this.fireEvent("regionexpanded", region);
34681 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34682 * performs box-model adjustments.
34683 * @return {Object} The size as an object {width: (the width), height: (the height)}
34685 getViewSize : function()
34688 if(this.el.dom != document.body){
34689 size = this.el.getSize();
34691 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34693 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34694 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34699 * Returns the Element this layout is bound to.
34700 * @return {Roo.Element}
34702 getEl : function(){
34707 * Returns the specified region.
34708 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34709 * @return {Roo.LayoutRegion}
34711 getRegion : function(target){
34712 return this.regions[target.toLowerCase()];
34715 onWindowResize : function(){
34716 if(this.monitorWindowResize){
34723 * Ext JS Library 1.1.1
34724 * Copyright(c) 2006-2007, Ext JS, LLC.
34726 * Originally Released Under LGPL - original licence link has changed is not relivant.
34729 * <script type="text/javascript">
34732 * @class Roo.bootstrap.layout.Border
34733 * @extends Roo.bootstrap.layout.Manager
34734 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34735 * please see: examples/bootstrap/nested.html<br><br>
34737 <b>The container the layout is rendered into can be either the body element or any other element.
34738 If it is not the body element, the container needs to either be an absolute positioned element,
34739 or you will need to add "position:relative" to the css of the container. You will also need to specify
34740 the container size if it is not the body element.</b>
34743 * Create a new Border
34744 * @param {Object} config Configuration options
34746 Roo.bootstrap.layout.Border = function(config){
34747 config = config || {};
34748 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34752 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34753 if(config[region]){
34754 config[region].region = region;
34755 this.addRegion(config[region]);
34761 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34763 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34765 * Creates and adds a new region if it doesn't already exist.
34766 * @param {String} target The target region key (north, south, east, west or center).
34767 * @param {Object} config The regions config object
34768 * @return {BorderLayoutRegion} The new region
34770 addRegion : function(config)
34772 if(!this.regions[config.region]){
34773 var r = this.factory(config);
34774 this.bindRegion(r);
34776 return this.regions[config.region];
34780 bindRegion : function(r){
34781 this.regions[r.config.region] = r;
34783 r.on("visibilitychange", this.layout, this);
34784 r.on("paneladded", this.layout, this);
34785 r.on("panelremoved", this.layout, this);
34786 r.on("invalidated", this.layout, this);
34787 r.on("resized", this.onRegionResized, this);
34788 r.on("collapsed", this.onRegionCollapsed, this);
34789 r.on("expanded", this.onRegionExpanded, this);
34793 * Performs a layout update.
34795 layout : function()
34797 if(this.updating) {
34801 // render all the rebions if they have not been done alreayd?
34802 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34803 if(this.regions[region] && !this.regions[region].bodyEl){
34804 this.regions[region].onRender(this.el)
34808 var size = this.getViewSize();
34809 var w = size.width;
34810 var h = size.height;
34815 //var x = 0, y = 0;
34817 var rs = this.regions;
34818 var north = rs["north"];
34819 var south = rs["south"];
34820 var west = rs["west"];
34821 var east = rs["east"];
34822 var center = rs["center"];
34823 //if(this.hideOnLayout){ // not supported anymore
34824 //c.el.setStyle("display", "none");
34826 if(north && north.isVisible()){
34827 var b = north.getBox();
34828 var m = north.getMargins();
34829 b.width = w - (m.left+m.right);
34832 centerY = b.height + b.y + m.bottom;
34833 centerH -= centerY;
34834 north.updateBox(this.safeBox(b));
34836 if(south && south.isVisible()){
34837 var b = south.getBox();
34838 var m = south.getMargins();
34839 b.width = w - (m.left+m.right);
34841 var totalHeight = (b.height + m.top + m.bottom);
34842 b.y = h - totalHeight + m.top;
34843 centerH -= totalHeight;
34844 south.updateBox(this.safeBox(b));
34846 if(west && west.isVisible()){
34847 var b = west.getBox();
34848 var m = west.getMargins();
34849 b.height = centerH - (m.top+m.bottom);
34851 b.y = centerY + m.top;
34852 var totalWidth = (b.width + m.left + m.right);
34853 centerX += totalWidth;
34854 centerW -= totalWidth;
34855 west.updateBox(this.safeBox(b));
34857 if(east && east.isVisible()){
34858 var b = east.getBox();
34859 var m = east.getMargins();
34860 b.height = centerH - (m.top+m.bottom);
34861 var totalWidth = (b.width + m.left + m.right);
34862 b.x = w - totalWidth + m.left;
34863 b.y = centerY + m.top;
34864 centerW -= totalWidth;
34865 east.updateBox(this.safeBox(b));
34868 var m = center.getMargins();
34870 x: centerX + m.left,
34871 y: centerY + m.top,
34872 width: centerW - (m.left+m.right),
34873 height: centerH - (m.top+m.bottom)
34875 //if(this.hideOnLayout){
34876 //center.el.setStyle("display", "block");
34878 center.updateBox(this.safeBox(centerBox));
34881 this.fireEvent("layout", this);
34885 safeBox : function(box){
34886 box.width = Math.max(0, box.width);
34887 box.height = Math.max(0, box.height);
34892 * Adds a ContentPanel (or subclass) to this layout.
34893 * @param {String} target The target region key (north, south, east, west or center).
34894 * @param {Roo.ContentPanel} panel The panel to add
34895 * @return {Roo.ContentPanel} The added panel
34897 add : function(target, panel){
34899 target = target.toLowerCase();
34900 return this.regions[target].add(panel);
34904 * Remove a ContentPanel (or subclass) to this layout.
34905 * @param {String} target The target region key (north, south, east, west or center).
34906 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34907 * @return {Roo.ContentPanel} The removed panel
34909 remove : function(target, panel){
34910 target = target.toLowerCase();
34911 return this.regions[target].remove(panel);
34915 * Searches all regions for a panel with the specified id
34916 * @param {String} panelId
34917 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34919 findPanel : function(panelId){
34920 var rs = this.regions;
34921 for(var target in rs){
34922 if(typeof rs[target] != "function"){
34923 var p = rs[target].getPanel(panelId);
34933 * Searches all regions for a panel with the specified id and activates (shows) it.
34934 * @param {String/ContentPanel} panelId The panels id or the panel itself
34935 * @return {Roo.ContentPanel} The shown panel or null
34937 showPanel : function(panelId) {
34938 var rs = this.regions;
34939 for(var target in rs){
34940 var r = rs[target];
34941 if(typeof r != "function"){
34942 if(r.hasPanel(panelId)){
34943 return r.showPanel(panelId);
34951 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34952 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34955 restoreState : function(provider){
34957 provider = Roo.state.Manager;
34959 var sm = new Roo.LayoutStateManager();
34960 sm.init(this, provider);
34966 * Adds a xtype elements to the layout.
34970 xtype : 'ContentPanel',
34977 xtype : 'NestedLayoutPanel',
34983 items : [ ... list of content panels or nested layout panels.. ]
34987 * @param {Object} cfg Xtype definition of item to add.
34989 addxtype : function(cfg)
34991 // basically accepts a pannel...
34992 // can accept a layout region..!?!?
34993 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34996 // theory? children can only be panels??
34998 //if (!cfg.xtype.match(/Panel$/)) {
35003 if (typeof(cfg.region) == 'undefined') {
35004 Roo.log("Failed to add Panel, region was not set");
35008 var region = cfg.region;
35014 xitems = cfg.items;
35021 case 'Content': // ContentPanel (el, cfg)
35022 case 'Scroll': // ContentPanel (el, cfg)
35024 cfg.autoCreate = true;
35025 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35027 // var el = this.el.createChild();
35028 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35031 this.add(region, ret);
35035 case 'TreePanel': // our new panel!
35036 cfg.el = this.el.createChild();
35037 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35038 this.add(region, ret);
35043 // create a new Layout (which is a Border Layout...
35045 var clayout = cfg.layout;
35046 clayout.el = this.el.createChild();
35047 clayout.items = clayout.items || [];
35051 // replace this exitems with the clayout ones..
35052 xitems = clayout.items;
35054 // force background off if it's in center...
35055 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35056 cfg.background = false;
35058 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35061 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35062 //console.log('adding nested layout panel ' + cfg.toSource());
35063 this.add(region, ret);
35064 nb = {}; /// find first...
35069 // needs grid and region
35071 //var el = this.getRegion(region).el.createChild();
35073 *var el = this.el.createChild();
35074 // create the grid first...
35075 cfg.grid.container = el;
35076 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35079 if (region == 'center' && this.active ) {
35080 cfg.background = false;
35083 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35085 this.add(region, ret);
35087 if (cfg.background) {
35088 // render grid on panel activation (if panel background)
35089 ret.on('activate', function(gp) {
35090 if (!gp.grid.rendered) {
35091 // gp.grid.render(el);
35095 // cfg.grid.render(el);
35101 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35102 // it was the old xcomponent building that caused this before.
35103 // espeically if border is the top element in the tree.
35113 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35115 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35116 this.add(region, ret);
35120 throw "Can not add '" + cfg.xtype + "' to Border";
35126 this.beginUpdate();
35130 Roo.each(xitems, function(i) {
35131 region = nb && i.region ? i.region : false;
35133 var add = ret.addxtype(i);
35136 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35137 if (!i.background) {
35138 abn[region] = nb[region] ;
35145 // make the last non-background panel active..
35146 //if (nb) { Roo.log(abn); }
35149 for(var r in abn) {
35150 region = this.getRegion(r);
35152 // tried using nb[r], but it does not work..
35154 region.showPanel(abn[r]);
35165 factory : function(cfg)
35168 var validRegions = Roo.bootstrap.layout.Border.regions;
35170 var target = cfg.region;
35173 var r = Roo.bootstrap.layout;
35177 return new r.North(cfg);
35179 return new r.South(cfg);
35181 return new r.East(cfg);
35183 return new r.West(cfg);
35185 return new r.Center(cfg);
35187 throw 'Layout region "'+target+'" not supported.';
35194 * Ext JS Library 1.1.1
35195 * Copyright(c) 2006-2007, Ext JS, LLC.
35197 * Originally Released Under LGPL - original licence link has changed is not relivant.
35200 * <script type="text/javascript">
35204 * @class Roo.bootstrap.layout.Basic
35205 * @extends Roo.util.Observable
35206 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35207 * and does not have a titlebar, tabs or any other features. All it does is size and position
35208 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35209 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35210 * @cfg {string} region the region that it inhabits..
35211 * @cfg {bool} skipConfig skip config?
35215 Roo.bootstrap.layout.Basic = function(config){
35217 this.mgr = config.mgr;
35219 this.position = config.region;
35221 var skipConfig = config.skipConfig;
35225 * @scope Roo.BasicLayoutRegion
35229 * @event beforeremove
35230 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35231 * @param {Roo.LayoutRegion} this
35232 * @param {Roo.ContentPanel} panel The panel
35233 * @param {Object} e The cancel event object
35235 "beforeremove" : true,
35237 * @event invalidated
35238 * Fires when the layout for this region is changed.
35239 * @param {Roo.LayoutRegion} this
35241 "invalidated" : true,
35243 * @event visibilitychange
35244 * Fires when this region is shown or hidden
35245 * @param {Roo.LayoutRegion} this
35246 * @param {Boolean} visibility true or false
35248 "visibilitychange" : true,
35250 * @event paneladded
35251 * Fires when a panel is added.
35252 * @param {Roo.LayoutRegion} this
35253 * @param {Roo.ContentPanel} panel The panel
35255 "paneladded" : true,
35257 * @event panelremoved
35258 * Fires when a panel is removed.
35259 * @param {Roo.LayoutRegion} this
35260 * @param {Roo.ContentPanel} panel The panel
35262 "panelremoved" : true,
35264 * @event beforecollapse
35265 * Fires when this region before collapse.
35266 * @param {Roo.LayoutRegion} this
35268 "beforecollapse" : true,
35271 * Fires when this region is collapsed.
35272 * @param {Roo.LayoutRegion} this
35274 "collapsed" : true,
35277 * Fires when this region is expanded.
35278 * @param {Roo.LayoutRegion} this
35283 * Fires when this region is slid into view.
35284 * @param {Roo.LayoutRegion} this
35286 "slideshow" : true,
35289 * Fires when this region slides out of view.
35290 * @param {Roo.LayoutRegion} this
35292 "slidehide" : true,
35294 * @event panelactivated
35295 * Fires when a panel is activated.
35296 * @param {Roo.LayoutRegion} this
35297 * @param {Roo.ContentPanel} panel The activated panel
35299 "panelactivated" : true,
35302 * Fires when the user resizes this region.
35303 * @param {Roo.LayoutRegion} this
35304 * @param {Number} newSize The new size (width for east/west, height for north/south)
35308 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35309 this.panels = new Roo.util.MixedCollection();
35310 this.panels.getKey = this.getPanelId.createDelegate(this);
35312 this.activePanel = null;
35313 // ensure listeners are added...
35315 if (config.listeners || config.events) {
35316 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35317 listeners : config.listeners || {},
35318 events : config.events || {}
35322 if(skipConfig !== true){
35323 this.applyConfig(config);
35327 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35329 getPanelId : function(p){
35333 applyConfig : function(config){
35334 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35335 this.config = config;
35340 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35341 * the width, for horizontal (north, south) the height.
35342 * @param {Number} newSize The new width or height
35344 resizeTo : function(newSize){
35345 var el = this.el ? this.el :
35346 (this.activePanel ? this.activePanel.getEl() : null);
35348 switch(this.position){
35351 el.setWidth(newSize);
35352 this.fireEvent("resized", this, newSize);
35356 el.setHeight(newSize);
35357 this.fireEvent("resized", this, newSize);
35363 getBox : function(){
35364 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35367 getMargins : function(){
35368 return this.margins;
35371 updateBox : function(box){
35373 var el = this.activePanel.getEl();
35374 el.dom.style.left = box.x + "px";
35375 el.dom.style.top = box.y + "px";
35376 this.activePanel.setSize(box.width, box.height);
35380 * Returns the container element for this region.
35381 * @return {Roo.Element}
35383 getEl : function(){
35384 return this.activePanel;
35388 * Returns true if this region is currently visible.
35389 * @return {Boolean}
35391 isVisible : function(){
35392 return this.activePanel ? true : false;
35395 setActivePanel : function(panel){
35396 panel = this.getPanel(panel);
35397 if(this.activePanel && this.activePanel != panel){
35398 this.activePanel.setActiveState(false);
35399 this.activePanel.getEl().setLeftTop(-10000,-10000);
35401 this.activePanel = panel;
35402 panel.setActiveState(true);
35404 panel.setSize(this.box.width, this.box.height);
35406 this.fireEvent("panelactivated", this, panel);
35407 this.fireEvent("invalidated");
35411 * Show the specified panel.
35412 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35413 * @return {Roo.ContentPanel} The shown panel or null
35415 showPanel : function(panel){
35416 panel = this.getPanel(panel);
35418 this.setActivePanel(panel);
35424 * Get the active panel for this region.
35425 * @return {Roo.ContentPanel} The active panel or null
35427 getActivePanel : function(){
35428 return this.activePanel;
35432 * Add the passed ContentPanel(s)
35433 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35434 * @return {Roo.ContentPanel} The panel added (if only one was added)
35436 add : function(panel){
35437 if(arguments.length > 1){
35438 for(var i = 0, len = arguments.length; i < len; i++) {
35439 this.add(arguments[i]);
35443 if(this.hasPanel(panel)){
35444 this.showPanel(panel);
35447 var el = panel.getEl();
35448 if(el.dom.parentNode != this.mgr.el.dom){
35449 this.mgr.el.dom.appendChild(el.dom);
35451 if(panel.setRegion){
35452 panel.setRegion(this);
35454 this.panels.add(panel);
35455 el.setStyle("position", "absolute");
35456 if(!panel.background){
35457 this.setActivePanel(panel);
35458 if(this.config.initialSize && this.panels.getCount()==1){
35459 this.resizeTo(this.config.initialSize);
35462 this.fireEvent("paneladded", this, panel);
35467 * Returns true if the panel is in this region.
35468 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35469 * @return {Boolean}
35471 hasPanel : function(panel){
35472 if(typeof panel == "object"){ // must be panel obj
35473 panel = panel.getId();
35475 return this.getPanel(panel) ? true : false;
35479 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35480 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35481 * @param {Boolean} preservePanel Overrides the config preservePanel option
35482 * @return {Roo.ContentPanel} The panel that was removed
35484 remove : function(panel, preservePanel){
35485 panel = this.getPanel(panel);
35490 this.fireEvent("beforeremove", this, panel, e);
35491 if(e.cancel === true){
35494 var panelId = panel.getId();
35495 this.panels.removeKey(panelId);
35500 * Returns the panel specified or null if it's not in this region.
35501 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35502 * @return {Roo.ContentPanel}
35504 getPanel : function(id){
35505 if(typeof id == "object"){ // must be panel obj
35508 return this.panels.get(id);
35512 * Returns this regions position (north/south/east/west/center).
35515 getPosition: function(){
35516 return this.position;
35520 * Ext JS Library 1.1.1
35521 * Copyright(c) 2006-2007, Ext JS, LLC.
35523 * Originally Released Under LGPL - original licence link has changed is not relivant.
35526 * <script type="text/javascript">
35530 * @class Roo.bootstrap.layout.Region
35531 * @extends Roo.bootstrap.layout.Basic
35532 * This class represents a region in a layout manager.
35534 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35535 * @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})
35536 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35537 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35538 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35539 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35540 * @cfg {String} title The title for the region (overrides panel titles)
35541 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35542 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35543 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35544 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35545 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35546 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35547 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35548 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35549 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35550 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35552 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35553 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35554 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35555 * @cfg {Number} width For East/West panels
35556 * @cfg {Number} height For North/South panels
35557 * @cfg {Boolean} split To show the splitter
35558 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35560 * @cfg {string} cls Extra CSS classes to add to region
35562 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35563 * @cfg {string} region the region that it inhabits..
35566 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35567 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35569 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35570 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35571 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35573 Roo.bootstrap.layout.Region = function(config)
35575 this.applyConfig(config);
35577 var mgr = config.mgr;
35578 var pos = config.region;
35579 config.skipConfig = true;
35580 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35583 this.onRender(mgr.el);
35586 this.visible = true;
35587 this.collapsed = false;
35588 this.unrendered_panels = [];
35591 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35593 position: '', // set by wrapper (eg. north/south etc..)
35594 unrendered_panels : null, // unrendered panels.
35595 createBody : function(){
35596 /** This region's body element
35597 * @type Roo.Element */
35598 this.bodyEl = this.el.createChild({
35600 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35604 onRender: function(ctr, pos)
35606 var dh = Roo.DomHelper;
35607 /** This region's container element
35608 * @type Roo.Element */
35609 this.el = dh.append(ctr.dom, {
35611 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35613 /** This region's title element
35614 * @type Roo.Element */
35616 this.titleEl = dh.append(this.el.dom,
35619 unselectable: "on",
35620 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35622 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35623 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35626 this.titleEl.enableDisplayMode();
35627 /** This region's title text element
35628 * @type HTMLElement */
35629 this.titleTextEl = this.titleEl.dom.firstChild;
35630 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35632 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35633 this.closeBtn.enableDisplayMode();
35634 this.closeBtn.on("click", this.closeClicked, this);
35635 this.closeBtn.hide();
35637 this.createBody(this.config);
35638 if(this.config.hideWhenEmpty){
35640 this.on("paneladded", this.validateVisibility, this);
35641 this.on("panelremoved", this.validateVisibility, this);
35643 if(this.autoScroll){
35644 this.bodyEl.setStyle("overflow", "auto");
35646 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35648 //if(c.titlebar !== false){
35649 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35650 this.titleEl.hide();
35652 this.titleEl.show();
35653 if(this.config.title){
35654 this.titleTextEl.innerHTML = this.config.title;
35658 if(this.config.collapsed){
35659 this.collapse(true);
35661 if(this.config.hidden){
35665 if (this.unrendered_panels && this.unrendered_panels.length) {
35666 for (var i =0;i< this.unrendered_panels.length; i++) {
35667 this.add(this.unrendered_panels[i]);
35669 this.unrendered_panels = null;
35675 applyConfig : function(c)
35678 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35679 var dh = Roo.DomHelper;
35680 if(c.titlebar !== false){
35681 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35682 this.collapseBtn.on("click", this.collapse, this);
35683 this.collapseBtn.enableDisplayMode();
35685 if(c.showPin === true || this.showPin){
35686 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35687 this.stickBtn.enableDisplayMode();
35688 this.stickBtn.on("click", this.expand, this);
35689 this.stickBtn.hide();
35694 /** This region's collapsed element
35695 * @type Roo.Element */
35698 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35699 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35702 if(c.floatable !== false){
35703 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35704 this.collapsedEl.on("click", this.collapseClick, this);
35707 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35708 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35709 id: "message", unselectable: "on", style:{"float":"left"}});
35710 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35712 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35713 this.expandBtn.on("click", this.expand, this);
35717 if(this.collapseBtn){
35718 this.collapseBtn.setVisible(c.collapsible == true);
35721 this.cmargins = c.cmargins || this.cmargins ||
35722 (this.position == "west" || this.position == "east" ?
35723 {top: 0, left: 2, right:2, bottom: 0} :
35724 {top: 2, left: 0, right:0, bottom: 2});
35726 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35729 this.bottomTabs = c.tabPosition != "top";
35731 this.autoScroll = c.autoScroll || false;
35736 this.duration = c.duration || .30;
35737 this.slideDuration = c.slideDuration || .45;
35742 * Returns true if this region is currently visible.
35743 * @return {Boolean}
35745 isVisible : function(){
35746 return this.visible;
35750 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35751 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35753 //setCollapsedTitle : function(title){
35754 // title = title || " ";
35755 // if(this.collapsedTitleTextEl){
35756 // this.collapsedTitleTextEl.innerHTML = title;
35760 getBox : function(){
35762 // if(!this.collapsed){
35763 b = this.el.getBox(false, true);
35765 // b = this.collapsedEl.getBox(false, true);
35770 getMargins : function(){
35771 return this.margins;
35772 //return this.collapsed ? this.cmargins : this.margins;
35775 highlight : function(){
35776 this.el.addClass("x-layout-panel-dragover");
35779 unhighlight : function(){
35780 this.el.removeClass("x-layout-panel-dragover");
35783 updateBox : function(box)
35785 if (!this.bodyEl) {
35786 return; // not rendered yet..
35790 if(!this.collapsed){
35791 this.el.dom.style.left = box.x + "px";
35792 this.el.dom.style.top = box.y + "px";
35793 this.updateBody(box.width, box.height);
35795 this.collapsedEl.dom.style.left = box.x + "px";
35796 this.collapsedEl.dom.style.top = box.y + "px";
35797 this.collapsedEl.setSize(box.width, box.height);
35800 this.tabs.autoSizeTabs();
35804 updateBody : function(w, h)
35807 this.el.setWidth(w);
35808 w -= this.el.getBorderWidth("rl");
35809 if(this.config.adjustments){
35810 w += this.config.adjustments[0];
35813 if(h !== null && h > 0){
35814 this.el.setHeight(h);
35815 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35816 h -= this.el.getBorderWidth("tb");
35817 if(this.config.adjustments){
35818 h += this.config.adjustments[1];
35820 this.bodyEl.setHeight(h);
35822 h = this.tabs.syncHeight(h);
35825 if(this.panelSize){
35826 w = w !== null ? w : this.panelSize.width;
35827 h = h !== null ? h : this.panelSize.height;
35829 if(this.activePanel){
35830 var el = this.activePanel.getEl();
35831 w = w !== null ? w : el.getWidth();
35832 h = h !== null ? h : el.getHeight();
35833 this.panelSize = {width: w, height: h};
35834 this.activePanel.setSize(w, h);
35836 if(Roo.isIE && this.tabs){
35837 this.tabs.el.repaint();
35842 * Returns the container element for this region.
35843 * @return {Roo.Element}
35845 getEl : function(){
35850 * Hides this region.
35853 //if(!this.collapsed){
35854 this.el.dom.style.left = "-2000px";
35857 // this.collapsedEl.dom.style.left = "-2000px";
35858 // this.collapsedEl.hide();
35860 this.visible = false;
35861 this.fireEvent("visibilitychange", this, false);
35865 * Shows this region if it was previously hidden.
35868 //if(!this.collapsed){
35871 // this.collapsedEl.show();
35873 this.visible = true;
35874 this.fireEvent("visibilitychange", this, true);
35877 closeClicked : function(){
35878 if(this.activePanel){
35879 this.remove(this.activePanel);
35883 collapseClick : function(e){
35885 e.stopPropagation();
35888 e.stopPropagation();
35894 * Collapses this region.
35895 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35898 collapse : function(skipAnim, skipCheck = false){
35899 if(this.collapsed) {
35903 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35905 this.collapsed = true;
35907 this.split.el.hide();
35909 if(this.config.animate && skipAnim !== true){
35910 this.fireEvent("invalidated", this);
35911 this.animateCollapse();
35913 this.el.setLocation(-20000,-20000);
35915 this.collapsedEl.show();
35916 this.fireEvent("collapsed", this);
35917 this.fireEvent("invalidated", this);
35923 animateCollapse : function(){
35928 * Expands this region if it was previously collapsed.
35929 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35930 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35933 expand : function(e, skipAnim){
35935 e.stopPropagation();
35937 if(!this.collapsed || this.el.hasActiveFx()) {
35941 this.afterSlideIn();
35944 this.collapsed = false;
35945 if(this.config.animate && skipAnim !== true){
35946 this.animateExpand();
35950 this.split.el.show();
35952 this.collapsedEl.setLocation(-2000,-2000);
35953 this.collapsedEl.hide();
35954 this.fireEvent("invalidated", this);
35955 this.fireEvent("expanded", this);
35959 animateExpand : function(){
35963 initTabs : function()
35965 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35967 var ts = new Roo.bootstrap.panel.Tabs({
35968 el: this.bodyEl.dom,
35969 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35970 disableTooltips: this.config.disableTabTips,
35971 toolbar : this.config.toolbar
35974 if(this.config.hideTabs){
35975 ts.stripWrap.setDisplayed(false);
35978 ts.resizeTabs = this.config.resizeTabs === true;
35979 ts.minTabWidth = this.config.minTabWidth || 40;
35980 ts.maxTabWidth = this.config.maxTabWidth || 250;
35981 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35982 ts.monitorResize = false;
35983 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35984 ts.bodyEl.addClass('roo-layout-tabs-body');
35985 this.panels.each(this.initPanelAsTab, this);
35988 initPanelAsTab : function(panel){
35989 var ti = this.tabs.addTab(
35993 this.config.closeOnTab && panel.isClosable(),
35996 if(panel.tabTip !== undefined){
35997 ti.setTooltip(panel.tabTip);
35999 ti.on("activate", function(){
36000 this.setActivePanel(panel);
36003 if(this.config.closeOnTab){
36004 ti.on("beforeclose", function(t, e){
36006 this.remove(panel);
36010 panel.tabItem = ti;
36015 updatePanelTitle : function(panel, title)
36017 if(this.activePanel == panel){
36018 this.updateTitle(title);
36021 var ti = this.tabs.getTab(panel.getEl().id);
36023 if(panel.tabTip !== undefined){
36024 ti.setTooltip(panel.tabTip);
36029 updateTitle : function(title){
36030 if(this.titleTextEl && !this.config.title){
36031 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36035 setActivePanel : function(panel)
36037 panel = this.getPanel(panel);
36038 if(this.activePanel && this.activePanel != panel){
36039 if(this.activePanel.setActiveState(false) === false){
36043 this.activePanel = panel;
36044 panel.setActiveState(true);
36045 if(this.panelSize){
36046 panel.setSize(this.panelSize.width, this.panelSize.height);
36049 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36051 this.updateTitle(panel.getTitle());
36053 this.fireEvent("invalidated", this);
36055 this.fireEvent("panelactivated", this, panel);
36059 * Shows the specified panel.
36060 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36061 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36063 showPanel : function(panel)
36065 panel = this.getPanel(panel);
36068 var tab = this.tabs.getTab(panel.getEl().id);
36069 if(tab.isHidden()){
36070 this.tabs.unhideTab(tab.id);
36074 this.setActivePanel(panel);
36081 * Get the active panel for this region.
36082 * @return {Roo.ContentPanel} The active panel or null
36084 getActivePanel : function(){
36085 return this.activePanel;
36088 validateVisibility : function(){
36089 if(this.panels.getCount() < 1){
36090 this.updateTitle(" ");
36091 this.closeBtn.hide();
36094 if(!this.isVisible()){
36101 * Adds the passed ContentPanel(s) to this region.
36102 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36103 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36105 add : function(panel)
36107 if(arguments.length > 1){
36108 for(var i = 0, len = arguments.length; i < len; i++) {
36109 this.add(arguments[i]);
36114 // if we have not been rendered yet, then we can not really do much of this..
36115 if (!this.bodyEl) {
36116 this.unrendered_panels.push(panel);
36123 if(this.hasPanel(panel)){
36124 this.showPanel(panel);
36127 panel.setRegion(this);
36128 this.panels.add(panel);
36129 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36130 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36131 // and hide them... ???
36132 this.bodyEl.dom.appendChild(panel.getEl().dom);
36133 if(panel.background !== true){
36134 this.setActivePanel(panel);
36136 this.fireEvent("paneladded", this, panel);
36143 this.initPanelAsTab(panel);
36147 if(panel.background !== true){
36148 this.tabs.activate(panel.getEl().id);
36150 this.fireEvent("paneladded", this, panel);
36155 * Hides the tab for the specified panel.
36156 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36158 hidePanel : function(panel){
36159 if(this.tabs && (panel = this.getPanel(panel))){
36160 this.tabs.hideTab(panel.getEl().id);
36165 * Unhides the tab for a previously hidden panel.
36166 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36168 unhidePanel : function(panel){
36169 if(this.tabs && (panel = this.getPanel(panel))){
36170 this.tabs.unhideTab(panel.getEl().id);
36174 clearPanels : function(){
36175 while(this.panels.getCount() > 0){
36176 this.remove(this.panels.first());
36181 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36182 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36183 * @param {Boolean} preservePanel Overrides the config preservePanel option
36184 * @return {Roo.ContentPanel} The panel that was removed
36186 remove : function(panel, preservePanel)
36188 panel = this.getPanel(panel);
36193 this.fireEvent("beforeremove", this, panel, e);
36194 if(e.cancel === true){
36197 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36198 var panelId = panel.getId();
36199 this.panels.removeKey(panelId);
36201 document.body.appendChild(panel.getEl().dom);
36204 this.tabs.removeTab(panel.getEl().id);
36205 }else if (!preservePanel){
36206 this.bodyEl.dom.removeChild(panel.getEl().dom);
36208 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36209 var p = this.panels.first();
36210 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36211 tempEl.appendChild(p.getEl().dom);
36212 this.bodyEl.update("");
36213 this.bodyEl.dom.appendChild(p.getEl().dom);
36215 this.updateTitle(p.getTitle());
36217 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36218 this.setActivePanel(p);
36220 panel.setRegion(null);
36221 if(this.activePanel == panel){
36222 this.activePanel = null;
36224 if(this.config.autoDestroy !== false && preservePanel !== true){
36225 try{panel.destroy();}catch(e){}
36227 this.fireEvent("panelremoved", this, panel);
36232 * Returns the TabPanel component used by this region
36233 * @return {Roo.TabPanel}
36235 getTabs : function(){
36239 createTool : function(parentEl, className){
36240 var btn = Roo.DomHelper.append(parentEl, {
36242 cls: "x-layout-tools-button",
36245 cls: "roo-layout-tools-button-inner " + className,
36249 btn.addClassOnOver("roo-layout-tools-button-over");
36254 * Ext JS Library 1.1.1
36255 * Copyright(c) 2006-2007, Ext JS, LLC.
36257 * Originally Released Under LGPL - original licence link has changed is not relivant.
36260 * <script type="text/javascript">
36266 * @class Roo.SplitLayoutRegion
36267 * @extends Roo.LayoutRegion
36268 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36270 Roo.bootstrap.layout.Split = function(config){
36271 this.cursor = config.cursor;
36272 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36275 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36277 splitTip : "Drag to resize.",
36278 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36279 useSplitTips : false,
36281 applyConfig : function(config){
36282 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36285 onRender : function(ctr,pos) {
36287 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36288 if(!this.config.split){
36293 var splitEl = Roo.DomHelper.append(ctr.dom, {
36295 id: this.el.id + "-split",
36296 cls: "roo-layout-split roo-layout-split-"+this.position,
36299 /** The SplitBar for this region
36300 * @type Roo.SplitBar */
36301 // does not exist yet...
36302 Roo.log([this.position, this.orientation]);
36304 this.split = new Roo.bootstrap.SplitBar({
36305 dragElement : splitEl,
36306 resizingElement: this.el,
36307 orientation : this.orientation
36310 this.split.on("moved", this.onSplitMove, this);
36311 this.split.useShim = this.config.useShim === true;
36312 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36313 if(this.useSplitTips){
36314 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36316 //if(config.collapsible){
36317 // this.split.el.on("dblclick", this.collapse, this);
36320 if(typeof this.config.minSize != "undefined"){
36321 this.split.minSize = this.config.minSize;
36323 if(typeof this.config.maxSize != "undefined"){
36324 this.split.maxSize = this.config.maxSize;
36326 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36327 this.hideSplitter();
36332 getHMaxSize : function(){
36333 var cmax = this.config.maxSize || 10000;
36334 var center = this.mgr.getRegion("center");
36335 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36338 getVMaxSize : function(){
36339 var cmax = this.config.maxSize || 10000;
36340 var center = this.mgr.getRegion("center");
36341 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36344 onSplitMove : function(split, newSize){
36345 this.fireEvent("resized", this, newSize);
36349 * Returns the {@link Roo.SplitBar} for this region.
36350 * @return {Roo.SplitBar}
36352 getSplitBar : function(){
36357 this.hideSplitter();
36358 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36361 hideSplitter : function(){
36363 this.split.el.setLocation(-2000,-2000);
36364 this.split.el.hide();
36370 this.split.el.show();
36372 Roo.bootstrap.layout.Split.superclass.show.call(this);
36375 beforeSlide: function(){
36376 if(Roo.isGecko){// firefox overflow auto bug workaround
36377 this.bodyEl.clip();
36379 this.tabs.bodyEl.clip();
36381 if(this.activePanel){
36382 this.activePanel.getEl().clip();
36384 if(this.activePanel.beforeSlide){
36385 this.activePanel.beforeSlide();
36391 afterSlide : function(){
36392 if(Roo.isGecko){// firefox overflow auto bug workaround
36393 this.bodyEl.unclip();
36395 this.tabs.bodyEl.unclip();
36397 if(this.activePanel){
36398 this.activePanel.getEl().unclip();
36399 if(this.activePanel.afterSlide){
36400 this.activePanel.afterSlide();
36406 initAutoHide : function(){
36407 if(this.autoHide !== false){
36408 if(!this.autoHideHd){
36409 var st = new Roo.util.DelayedTask(this.slideIn, this);
36410 this.autoHideHd = {
36411 "mouseout": function(e){
36412 if(!e.within(this.el, true)){
36416 "mouseover" : function(e){
36422 this.el.on(this.autoHideHd);
36426 clearAutoHide : function(){
36427 if(this.autoHide !== false){
36428 this.el.un("mouseout", this.autoHideHd.mouseout);
36429 this.el.un("mouseover", this.autoHideHd.mouseover);
36433 clearMonitor : function(){
36434 Roo.get(document).un("click", this.slideInIf, this);
36437 // these names are backwards but not changed for compat
36438 slideOut : function(){
36439 if(this.isSlid || this.el.hasActiveFx()){
36442 this.isSlid = true;
36443 if(this.collapseBtn){
36444 this.collapseBtn.hide();
36446 this.closeBtnState = this.closeBtn.getStyle('display');
36447 this.closeBtn.hide();
36449 this.stickBtn.show();
36452 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36453 this.beforeSlide();
36454 this.el.setStyle("z-index", 10001);
36455 this.el.slideIn(this.getSlideAnchor(), {
36456 callback: function(){
36458 this.initAutoHide();
36459 Roo.get(document).on("click", this.slideInIf, this);
36460 this.fireEvent("slideshow", this);
36467 afterSlideIn : function(){
36468 this.clearAutoHide();
36469 this.isSlid = false;
36470 this.clearMonitor();
36471 this.el.setStyle("z-index", "");
36472 if(this.collapseBtn){
36473 this.collapseBtn.show();
36475 this.closeBtn.setStyle('display', this.closeBtnState);
36477 this.stickBtn.hide();
36479 this.fireEvent("slidehide", this);
36482 slideIn : function(cb){
36483 if(!this.isSlid || this.el.hasActiveFx()){
36487 this.isSlid = false;
36488 this.beforeSlide();
36489 this.el.slideOut(this.getSlideAnchor(), {
36490 callback: function(){
36491 this.el.setLeftTop(-10000, -10000);
36493 this.afterSlideIn();
36501 slideInIf : function(e){
36502 if(!e.within(this.el)){
36507 animateCollapse : function(){
36508 this.beforeSlide();
36509 this.el.setStyle("z-index", 20000);
36510 var anchor = this.getSlideAnchor();
36511 this.el.slideOut(anchor, {
36512 callback : function(){
36513 this.el.setStyle("z-index", "");
36514 this.collapsedEl.slideIn(anchor, {duration:.3});
36516 this.el.setLocation(-10000,-10000);
36518 this.fireEvent("collapsed", this);
36525 animateExpand : function(){
36526 this.beforeSlide();
36527 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36528 this.el.setStyle("z-index", 20000);
36529 this.collapsedEl.hide({
36532 this.el.slideIn(this.getSlideAnchor(), {
36533 callback : function(){
36534 this.el.setStyle("z-index", "");
36537 this.split.el.show();
36539 this.fireEvent("invalidated", this);
36540 this.fireEvent("expanded", this);
36568 getAnchor : function(){
36569 return this.anchors[this.position];
36572 getCollapseAnchor : function(){
36573 return this.canchors[this.position];
36576 getSlideAnchor : function(){
36577 return this.sanchors[this.position];
36580 getAlignAdj : function(){
36581 var cm = this.cmargins;
36582 switch(this.position){
36598 getExpandAdj : function(){
36599 var c = this.collapsedEl, cm = this.cmargins;
36600 switch(this.position){
36602 return [-(cm.right+c.getWidth()+cm.left), 0];
36605 return [cm.right+c.getWidth()+cm.left, 0];
36608 return [0, -(cm.top+cm.bottom+c.getHeight())];
36611 return [0, cm.top+cm.bottom+c.getHeight()];
36617 * Ext JS Library 1.1.1
36618 * Copyright(c) 2006-2007, Ext JS, LLC.
36620 * Originally Released Under LGPL - original licence link has changed is not relivant.
36623 * <script type="text/javascript">
36626 * These classes are private internal classes
36628 Roo.bootstrap.layout.Center = function(config){
36629 config.region = "center";
36630 Roo.bootstrap.layout.Region.call(this, config);
36631 this.visible = true;
36632 this.minWidth = config.minWidth || 20;
36633 this.minHeight = config.minHeight || 20;
36636 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36638 // center panel can't be hidden
36642 // center panel can't be hidden
36645 getMinWidth: function(){
36646 return this.minWidth;
36649 getMinHeight: function(){
36650 return this.minHeight;
36663 Roo.bootstrap.layout.North = function(config)
36665 config.region = 'north';
36666 config.cursor = 'n-resize';
36668 Roo.bootstrap.layout.Split.call(this, config);
36672 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36673 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36674 this.split.el.addClass("roo-layout-split-v");
36676 var size = config.initialSize || config.height;
36677 if(typeof size != "undefined"){
36678 this.el.setHeight(size);
36681 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36683 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36687 getBox : function(){
36688 if(this.collapsed){
36689 return this.collapsedEl.getBox();
36691 var box = this.el.getBox();
36693 box.height += this.split.el.getHeight();
36698 updateBox : function(box){
36699 if(this.split && !this.collapsed){
36700 box.height -= this.split.el.getHeight();
36701 this.split.el.setLeft(box.x);
36702 this.split.el.setTop(box.y+box.height);
36703 this.split.el.setWidth(box.width);
36705 if(this.collapsed){
36706 this.updateBody(box.width, null);
36708 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36716 Roo.bootstrap.layout.South = function(config){
36717 config.region = 'south';
36718 config.cursor = 's-resize';
36719 Roo.bootstrap.layout.Split.call(this, config);
36721 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36722 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36723 this.split.el.addClass("roo-layout-split-v");
36725 var size = config.initialSize || config.height;
36726 if(typeof size != "undefined"){
36727 this.el.setHeight(size);
36731 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36732 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36733 getBox : function(){
36734 if(this.collapsed){
36735 return this.collapsedEl.getBox();
36737 var box = this.el.getBox();
36739 var sh = this.split.el.getHeight();
36746 updateBox : function(box){
36747 if(this.split && !this.collapsed){
36748 var sh = this.split.el.getHeight();
36751 this.split.el.setLeft(box.x);
36752 this.split.el.setTop(box.y-sh);
36753 this.split.el.setWidth(box.width);
36755 if(this.collapsed){
36756 this.updateBody(box.width, null);
36758 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36762 Roo.bootstrap.layout.East = function(config){
36763 config.region = "east";
36764 config.cursor = "e-resize";
36765 Roo.bootstrap.layout.Split.call(this, config);
36767 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36768 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36769 this.split.el.addClass("roo-layout-split-h");
36771 var size = config.initialSize || config.width;
36772 if(typeof size != "undefined"){
36773 this.el.setWidth(size);
36776 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36777 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36778 getBox : function(){
36779 if(this.collapsed){
36780 return this.collapsedEl.getBox();
36782 var box = this.el.getBox();
36784 var sw = this.split.el.getWidth();
36791 updateBox : function(box){
36792 if(this.split && !this.collapsed){
36793 var sw = this.split.el.getWidth();
36795 this.split.el.setLeft(box.x);
36796 this.split.el.setTop(box.y);
36797 this.split.el.setHeight(box.height);
36800 if(this.collapsed){
36801 this.updateBody(null, box.height);
36803 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36807 Roo.bootstrap.layout.West = function(config){
36808 config.region = "west";
36809 config.cursor = "w-resize";
36811 Roo.bootstrap.layout.Split.call(this, config);
36813 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36814 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36815 this.split.el.addClass("roo-layout-split-h");
36819 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36820 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36822 onRender: function(ctr, pos)
36824 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36825 var size = this.config.initialSize || this.config.width;
36826 if(typeof size != "undefined"){
36827 this.el.setWidth(size);
36831 getBox : function(){
36832 if(this.collapsed){
36833 return this.collapsedEl.getBox();
36835 var box = this.el.getBox();
36837 box.width += this.split.el.getWidth();
36842 updateBox : function(box){
36843 if(this.split && !this.collapsed){
36844 var sw = this.split.el.getWidth();
36846 this.split.el.setLeft(box.x+box.width);
36847 this.split.el.setTop(box.y);
36848 this.split.el.setHeight(box.height);
36850 if(this.collapsed){
36851 this.updateBody(null, box.height);
36853 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36856 Roo.namespace("Roo.bootstrap.panel");/*
36858 * Ext JS Library 1.1.1
36859 * Copyright(c) 2006-2007, Ext JS, LLC.
36861 * Originally Released Under LGPL - original licence link has changed is not relivant.
36864 * <script type="text/javascript">
36867 * @class Roo.ContentPanel
36868 * @extends Roo.util.Observable
36869 * A basic ContentPanel element.
36870 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36871 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36872 * @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
36873 * @cfg {Boolean} closable True if the panel can be closed/removed
36874 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36875 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36876 * @cfg {Toolbar} toolbar A toolbar for this panel
36877 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36878 * @cfg {String} title The title for this panel
36879 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36880 * @cfg {String} url Calls {@link #setUrl} with this value
36881 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36882 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36883 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36884 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36885 * @cfg {Boolean} badges render the badges
36888 * Create a new ContentPanel.
36889 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36890 * @param {String/Object} config A string to set only the title or a config object
36891 * @param {String} content (optional) Set the HTML content for this panel
36892 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36894 Roo.bootstrap.panel.Content = function( config){
36896 this.tpl = config.tpl || false;
36898 var el = config.el;
36899 var content = config.content;
36901 if(config.autoCreate){ // xtype is available if this is called from factory
36904 this.el = Roo.get(el);
36905 if(!this.el && config && config.autoCreate){
36906 if(typeof config.autoCreate == "object"){
36907 if(!config.autoCreate.id){
36908 config.autoCreate.id = config.id||el;
36910 this.el = Roo.DomHelper.append(document.body,
36911 config.autoCreate, true);
36913 var elcfg = { tag: "div",
36914 cls: "roo-layout-inactive-content",
36918 elcfg.html = config.html;
36922 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36925 this.closable = false;
36926 this.loaded = false;
36927 this.active = false;
36930 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36932 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36934 this.wrapEl = this.el; //this.el.wrap();
36936 if (config.toolbar.items) {
36937 ti = config.toolbar.items ;
36938 delete config.toolbar.items ;
36942 this.toolbar.render(this.wrapEl, 'before');
36943 for(var i =0;i < ti.length;i++) {
36944 // Roo.log(['add child', items[i]]);
36945 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36947 this.toolbar.items = nitems;
36948 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36949 delete config.toolbar;
36953 // xtype created footer. - not sure if will work as we normally have to render first..
36954 if (this.footer && !this.footer.el && this.footer.xtype) {
36955 if (!this.wrapEl) {
36956 this.wrapEl = this.el.wrap();
36959 this.footer.container = this.wrapEl.createChild();
36961 this.footer = Roo.factory(this.footer, Roo);
36966 if(typeof config == "string"){
36967 this.title = config;
36969 Roo.apply(this, config);
36973 this.resizeEl = Roo.get(this.resizeEl, true);
36975 this.resizeEl = this.el;
36977 // handle view.xtype
36985 * Fires when this panel is activated.
36986 * @param {Roo.ContentPanel} this
36990 * @event deactivate
36991 * Fires when this panel is activated.
36992 * @param {Roo.ContentPanel} this
36994 "deactivate" : true,
36998 * Fires when this panel is resized if fitToFrame is true.
36999 * @param {Roo.ContentPanel} this
37000 * @param {Number} width The width after any component adjustments
37001 * @param {Number} height The height after any component adjustments
37007 * Fires when this tab is created
37008 * @param {Roo.ContentPanel} this
37019 if(this.autoScroll){
37020 this.resizeEl.setStyle("overflow", "auto");
37022 // fix randome scrolling
37023 //this.el.on('scroll', function() {
37024 // Roo.log('fix random scolling');
37025 // this.scrollTo('top',0);
37028 content = content || this.content;
37030 this.setContent(content);
37032 if(config && config.url){
37033 this.setUrl(this.url, this.params, this.loadOnce);
37038 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37040 if (this.view && typeof(this.view.xtype) != 'undefined') {
37041 this.view.el = this.el.appendChild(document.createElement("div"));
37042 this.view = Roo.factory(this.view);
37043 this.view.render && this.view.render(false, '');
37047 this.fireEvent('render', this);
37050 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37054 setRegion : function(region){
37055 this.region = region;
37056 this.setActiveClass(region && !this.background);
37060 setActiveClass: function(state)
37063 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37064 this.el.setStyle('position','relative');
37066 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37067 this.el.setStyle('position', 'absolute');
37072 * Returns the toolbar for this Panel if one was configured.
37073 * @return {Roo.Toolbar}
37075 getToolbar : function(){
37076 return this.toolbar;
37079 setActiveState : function(active)
37081 this.active = active;
37082 this.setActiveClass(active);
37084 if(this.fireEvent("deactivate", this) === false){
37089 this.fireEvent("activate", this);
37093 * Updates this panel's element
37094 * @param {String} content The new content
37095 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37097 setContent : function(content, loadScripts){
37098 this.el.update(content, loadScripts);
37101 ignoreResize : function(w, h){
37102 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37105 this.lastSize = {width: w, height: h};
37110 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37111 * @return {Roo.UpdateManager} The UpdateManager
37113 getUpdateManager : function(){
37114 return this.el.getUpdateManager();
37117 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37118 * @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:
37121 url: "your-url.php",
37122 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37123 callback: yourFunction,
37124 scope: yourObject, //(optional scope)
37127 text: "Loading...",
37132 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37133 * 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.
37134 * @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}
37135 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37136 * @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.
37137 * @return {Roo.ContentPanel} this
37140 var um = this.el.getUpdateManager();
37141 um.update.apply(um, arguments);
37147 * 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.
37148 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37149 * @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)
37150 * @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)
37151 * @return {Roo.UpdateManager} The UpdateManager
37153 setUrl : function(url, params, loadOnce){
37154 if(this.refreshDelegate){
37155 this.removeListener("activate", this.refreshDelegate);
37157 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37158 this.on("activate", this.refreshDelegate);
37159 return this.el.getUpdateManager();
37162 _handleRefresh : function(url, params, loadOnce){
37163 if(!loadOnce || !this.loaded){
37164 var updater = this.el.getUpdateManager();
37165 updater.update(url, params, this._setLoaded.createDelegate(this));
37169 _setLoaded : function(){
37170 this.loaded = true;
37174 * Returns this panel's id
37177 getId : function(){
37182 * Returns this panel's element - used by regiosn to add.
37183 * @return {Roo.Element}
37185 getEl : function(){
37186 return this.wrapEl || this.el;
37191 adjustForComponents : function(width, height)
37193 //Roo.log('adjustForComponents ');
37194 if(this.resizeEl != this.el){
37195 width -= this.el.getFrameWidth('lr');
37196 height -= this.el.getFrameWidth('tb');
37199 var te = this.toolbar.getEl();
37200 te.setWidth(width);
37201 height -= te.getHeight();
37204 var te = this.footer.getEl();
37205 te.setWidth(width);
37206 height -= te.getHeight();
37210 if(this.adjustments){
37211 width += this.adjustments[0];
37212 height += this.adjustments[1];
37214 return {"width": width, "height": height};
37217 setSize : function(width, height){
37218 if(this.fitToFrame && !this.ignoreResize(width, height)){
37219 if(this.fitContainer && this.resizeEl != this.el){
37220 this.el.setSize(width, height);
37222 var size = this.adjustForComponents(width, height);
37223 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37224 this.fireEvent('resize', this, size.width, size.height);
37229 * Returns this panel's title
37232 getTitle : function(){
37234 if (typeof(this.title) != 'object') {
37239 for (var k in this.title) {
37240 if (!this.title.hasOwnProperty(k)) {
37244 if (k.indexOf('-') >= 0) {
37245 var s = k.split('-');
37246 for (var i = 0; i<s.length; i++) {
37247 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37250 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37257 * Set this panel's title
37258 * @param {String} title
37260 setTitle : function(title){
37261 this.title = title;
37263 this.region.updatePanelTitle(this, title);
37268 * Returns true is this panel was configured to be closable
37269 * @return {Boolean}
37271 isClosable : function(){
37272 return this.closable;
37275 beforeSlide : function(){
37277 this.resizeEl.clip();
37280 afterSlide : function(){
37282 this.resizeEl.unclip();
37286 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37287 * Will fail silently if the {@link #setUrl} method has not been called.
37288 * This does not activate the panel, just updates its content.
37290 refresh : function(){
37291 if(this.refreshDelegate){
37292 this.loaded = false;
37293 this.refreshDelegate();
37298 * Destroys this panel
37300 destroy : function(){
37301 this.el.removeAllListeners();
37302 var tempEl = document.createElement("span");
37303 tempEl.appendChild(this.el.dom);
37304 tempEl.innerHTML = "";
37310 * form - if the content panel contains a form - this is a reference to it.
37311 * @type {Roo.form.Form}
37315 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37316 * This contains a reference to it.
37322 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37332 * @param {Object} cfg Xtype definition of item to add.
37336 getChildContainer: function () {
37337 return this.getEl();
37342 var ret = new Roo.factory(cfg);
37347 if (cfg.xtype.match(/^Form$/)) {
37350 //if (this.footer) {
37351 // el = this.footer.container.insertSibling(false, 'before');
37353 el = this.el.createChild();
37356 this.form = new Roo.form.Form(cfg);
37359 if ( this.form.allItems.length) {
37360 this.form.render(el.dom);
37364 // should only have one of theses..
37365 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37366 // views.. should not be just added - used named prop 'view''
37368 cfg.el = this.el.appendChild(document.createElement("div"));
37371 var ret = new Roo.factory(cfg);
37373 ret.render && ret.render(false, ''); // render blank..
37383 * @class Roo.bootstrap.panel.Grid
37384 * @extends Roo.bootstrap.panel.Content
37386 * Create a new GridPanel.
37387 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37388 * @param {Object} config A the config object
37394 Roo.bootstrap.panel.Grid = function(config)
37398 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37399 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37401 config.el = this.wrapper;
37402 //this.el = this.wrapper;
37404 if (config.container) {
37405 // ctor'ed from a Border/panel.grid
37408 this.wrapper.setStyle("overflow", "hidden");
37409 this.wrapper.addClass('roo-grid-container');
37414 if(config.toolbar){
37415 var tool_el = this.wrapper.createChild();
37416 this.toolbar = Roo.factory(config.toolbar);
37418 if (config.toolbar.items) {
37419 ti = config.toolbar.items ;
37420 delete config.toolbar.items ;
37424 this.toolbar.render(tool_el);
37425 for(var i =0;i < ti.length;i++) {
37426 // Roo.log(['add child', items[i]]);
37427 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37429 this.toolbar.items = nitems;
37431 delete config.toolbar;
37434 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37435 config.grid.scrollBody = true;;
37436 config.grid.monitorWindowResize = false; // turn off autosizing
37437 config.grid.autoHeight = false;
37438 config.grid.autoWidth = false;
37440 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37442 if (config.background) {
37443 // render grid on panel activation (if panel background)
37444 this.on('activate', function(gp) {
37445 if (!gp.grid.rendered) {
37446 gp.grid.render(this.wrapper);
37447 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37452 this.grid.render(this.wrapper);
37453 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37456 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37457 // ??? needed ??? config.el = this.wrapper;
37462 // xtype created footer. - not sure if will work as we normally have to render first..
37463 if (this.footer && !this.footer.el && this.footer.xtype) {
37465 var ctr = this.grid.getView().getFooterPanel(true);
37466 this.footer.dataSource = this.grid.dataSource;
37467 this.footer = Roo.factory(this.footer, Roo);
37468 this.footer.render(ctr);
37478 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37479 getId : function(){
37480 return this.grid.id;
37484 * Returns the grid for this panel
37485 * @return {Roo.bootstrap.Table}
37487 getGrid : function(){
37491 setSize : function(width, height){
37492 if(!this.ignoreResize(width, height)){
37493 var grid = this.grid;
37494 var size = this.adjustForComponents(width, height);
37495 var gridel = grid.getGridEl();
37496 gridel.setSize(size.width, size.height);
37498 var thd = grid.getGridEl().select('thead',true).first();
37499 var tbd = grid.getGridEl().select('tbody', true).first();
37501 tbd.setSize(width, height - thd.getHeight());
37510 beforeSlide : function(){
37511 this.grid.getView().scroller.clip();
37514 afterSlide : function(){
37515 this.grid.getView().scroller.unclip();
37518 destroy : function(){
37519 this.grid.destroy();
37521 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37526 * @class Roo.bootstrap.panel.Nest
37527 * @extends Roo.bootstrap.panel.Content
37529 * Create a new Panel, that can contain a layout.Border.
37532 * @param {Roo.BorderLayout} layout The layout for this panel
37533 * @param {String/Object} config A string to set only the title or a config object
37535 Roo.bootstrap.panel.Nest = function(config)
37537 // construct with only one argument..
37538 /* FIXME - implement nicer consturctors
37539 if (layout.layout) {
37541 layout = config.layout;
37542 delete config.layout;
37544 if (layout.xtype && !layout.getEl) {
37545 // then layout needs constructing..
37546 layout = Roo.factory(layout, Roo);
37550 config.el = config.layout.getEl();
37552 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37554 config.layout.monitorWindowResize = false; // turn off autosizing
37555 this.layout = config.layout;
37556 this.layout.getEl().addClass("roo-layout-nested-layout");
37563 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37565 setSize : function(width, height){
37566 if(!this.ignoreResize(width, height)){
37567 var size = this.adjustForComponents(width, height);
37568 var el = this.layout.getEl();
37569 if (size.height < 1) {
37570 el.setWidth(size.width);
37572 el.setSize(size.width, size.height);
37574 var touch = el.dom.offsetWidth;
37575 this.layout.layout();
37576 // ie requires a double layout on the first pass
37577 if(Roo.isIE && !this.initialized){
37578 this.initialized = true;
37579 this.layout.layout();
37584 // activate all subpanels if not currently active..
37586 setActiveState : function(active){
37587 this.active = active;
37588 this.setActiveClass(active);
37591 this.fireEvent("deactivate", this);
37595 this.fireEvent("activate", this);
37596 // not sure if this should happen before or after..
37597 if (!this.layout) {
37598 return; // should not happen..
37601 for (var r in this.layout.regions) {
37602 reg = this.layout.getRegion(r);
37603 if (reg.getActivePanel()) {
37604 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37605 reg.setActivePanel(reg.getActivePanel());
37608 if (!reg.panels.length) {
37611 reg.showPanel(reg.getPanel(0));
37620 * Returns the nested BorderLayout for this panel
37621 * @return {Roo.BorderLayout}
37623 getLayout : function(){
37624 return this.layout;
37628 * Adds a xtype elements to the layout of the nested panel
37632 xtype : 'ContentPanel',
37639 xtype : 'NestedLayoutPanel',
37645 items : [ ... list of content panels or nested layout panels.. ]
37649 * @param {Object} cfg Xtype definition of item to add.
37651 addxtype : function(cfg) {
37652 return this.layout.addxtype(cfg);
37657 * Ext JS Library 1.1.1
37658 * Copyright(c) 2006-2007, Ext JS, LLC.
37660 * Originally Released Under LGPL - original licence link has changed is not relivant.
37663 * <script type="text/javascript">
37666 * @class Roo.TabPanel
37667 * @extends Roo.util.Observable
37668 * A lightweight tab container.
37672 // basic tabs 1, built from existing content
37673 var tabs = new Roo.TabPanel("tabs1");
37674 tabs.addTab("script", "View Script");
37675 tabs.addTab("markup", "View Markup");
37676 tabs.activate("script");
37678 // more advanced tabs, built from javascript
37679 var jtabs = new Roo.TabPanel("jtabs");
37680 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37682 // set up the UpdateManager
37683 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37684 var updater = tab2.getUpdateManager();
37685 updater.setDefaultUrl("ajax1.htm");
37686 tab2.on('activate', updater.refresh, updater, true);
37688 // Use setUrl for Ajax loading
37689 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37690 tab3.setUrl("ajax2.htm", null, true);
37693 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37696 jtabs.activate("jtabs-1");
37699 * Create a new TabPanel.
37700 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37701 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37703 Roo.bootstrap.panel.Tabs = function(config){
37705 * The container element for this TabPanel.
37706 * @type Roo.Element
37708 this.el = Roo.get(config.el);
37711 if(typeof config == "boolean"){
37712 this.tabPosition = config ? "bottom" : "top";
37714 Roo.apply(this, config);
37718 if(this.tabPosition == "bottom"){
37719 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37720 this.el.addClass("roo-tabs-bottom");
37722 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37723 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37724 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37726 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37728 if(this.tabPosition != "bottom"){
37729 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37730 * @type Roo.Element
37732 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37733 this.el.addClass("roo-tabs-top");
37737 this.bodyEl.setStyle("position", "relative");
37739 this.active = null;
37740 this.activateDelegate = this.activate.createDelegate(this);
37745 * Fires when the active tab changes
37746 * @param {Roo.TabPanel} this
37747 * @param {Roo.TabPanelItem} activePanel The new active tab
37751 * @event beforetabchange
37752 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37753 * @param {Roo.TabPanel} this
37754 * @param {Object} e Set cancel to true on this object to cancel the tab change
37755 * @param {Roo.TabPanelItem} tab The tab being changed to
37757 "beforetabchange" : true
37760 Roo.EventManager.onWindowResize(this.onResize, this);
37761 this.cpad = this.el.getPadding("lr");
37762 this.hiddenCount = 0;
37765 // toolbar on the tabbar support...
37766 if (this.toolbar) {
37767 alert("no toolbar support yet");
37768 this.toolbar = false;
37770 var tcfg = this.toolbar;
37771 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37772 this.toolbar = new Roo.Toolbar(tcfg);
37773 if (Roo.isSafari) {
37774 var tbl = tcfg.container.child('table', true);
37775 tbl.setAttribute('width', '100%');
37783 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37786 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37788 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37790 tabPosition : "top",
37792 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37794 currentTabWidth : 0,
37796 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37800 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37804 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37806 preferredTabWidth : 175,
37808 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37810 resizeTabs : false,
37812 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37814 monitorResize : true,
37816 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37821 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37822 * @param {String} id The id of the div to use <b>or create</b>
37823 * @param {String} text The text for the tab
37824 * @param {String} content (optional) Content to put in the TabPanelItem body
37825 * @param {Boolean} closable (optional) True to create a close icon on the tab
37826 * @return {Roo.TabPanelItem} The created TabPanelItem
37828 addTab : function(id, text, content, closable, tpl)
37830 var item = new Roo.bootstrap.panel.TabItem({
37834 closable : closable,
37837 this.addTabItem(item);
37839 item.setContent(content);
37845 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37846 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37847 * @return {Roo.TabPanelItem}
37849 getTab : function(id){
37850 return this.items[id];
37854 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37855 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37857 hideTab : function(id){
37858 var t = this.items[id];
37861 this.hiddenCount++;
37862 this.autoSizeTabs();
37867 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37868 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37870 unhideTab : function(id){
37871 var t = this.items[id];
37873 t.setHidden(false);
37874 this.hiddenCount--;
37875 this.autoSizeTabs();
37880 * Adds an existing {@link Roo.TabPanelItem}.
37881 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37883 addTabItem : function(item){
37884 this.items[item.id] = item;
37885 this.items.push(item);
37886 // if(this.resizeTabs){
37887 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37888 // this.autoSizeTabs();
37890 // item.autoSize();
37895 * Removes a {@link Roo.TabPanelItem}.
37896 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37898 removeTab : function(id){
37899 var items = this.items;
37900 var tab = items[id];
37901 if(!tab) { return; }
37902 var index = items.indexOf(tab);
37903 if(this.active == tab && items.length > 1){
37904 var newTab = this.getNextAvailable(index);
37909 this.stripEl.dom.removeChild(tab.pnode.dom);
37910 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37911 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37913 items.splice(index, 1);
37914 delete this.items[tab.id];
37915 tab.fireEvent("close", tab);
37916 tab.purgeListeners();
37917 this.autoSizeTabs();
37920 getNextAvailable : function(start){
37921 var items = this.items;
37923 // look for a next tab that will slide over to
37924 // replace the one being removed
37925 while(index < items.length){
37926 var item = items[++index];
37927 if(item && !item.isHidden()){
37931 // if one isn't found select the previous tab (on the left)
37934 var item = items[--index];
37935 if(item && !item.isHidden()){
37943 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37944 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37946 disableTab : function(id){
37947 var tab = this.items[id];
37948 if(tab && this.active != tab){
37954 * Enables a {@link Roo.TabPanelItem} that is disabled.
37955 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37957 enableTab : function(id){
37958 var tab = this.items[id];
37963 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37964 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37965 * @return {Roo.TabPanelItem} The TabPanelItem.
37967 activate : function(id){
37968 var tab = this.items[id];
37972 if(tab == this.active || tab.disabled){
37976 this.fireEvent("beforetabchange", this, e, tab);
37977 if(e.cancel !== true && !tab.disabled){
37979 this.active.hide();
37981 this.active = this.items[id];
37982 this.active.show();
37983 this.fireEvent("tabchange", this, this.active);
37989 * Gets the active {@link Roo.TabPanelItem}.
37990 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37992 getActiveTab : function(){
37993 return this.active;
37997 * Updates the tab body element to fit the height of the container element
37998 * for overflow scrolling
37999 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38001 syncHeight : function(targetHeight){
38002 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38003 var bm = this.bodyEl.getMargins();
38004 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38005 this.bodyEl.setHeight(newHeight);
38009 onResize : function(){
38010 if(this.monitorResize){
38011 this.autoSizeTabs();
38016 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38018 beginUpdate : function(){
38019 this.updating = true;
38023 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38025 endUpdate : function(){
38026 this.updating = false;
38027 this.autoSizeTabs();
38031 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38033 autoSizeTabs : function(){
38034 var count = this.items.length;
38035 var vcount = count - this.hiddenCount;
38036 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38039 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38040 var availWidth = Math.floor(w / vcount);
38041 var b = this.stripBody;
38042 if(b.getWidth() > w){
38043 var tabs = this.items;
38044 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38045 if(availWidth < this.minTabWidth){
38046 /*if(!this.sleft){ // incomplete scrolling code
38047 this.createScrollButtons();
38050 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38053 if(this.currentTabWidth < this.preferredTabWidth){
38054 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38060 * Returns the number of tabs in this TabPanel.
38063 getCount : function(){
38064 return this.items.length;
38068 * Resizes all the tabs to the passed width
38069 * @param {Number} The new width
38071 setTabWidth : function(width){
38072 this.currentTabWidth = width;
38073 for(var i = 0, len = this.items.length; i < len; i++) {
38074 if(!this.items[i].isHidden()) {
38075 this.items[i].setWidth(width);
38081 * Destroys this TabPanel
38082 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38084 destroy : function(removeEl){
38085 Roo.EventManager.removeResizeListener(this.onResize, this);
38086 for(var i = 0, len = this.items.length; i < len; i++){
38087 this.items[i].purgeListeners();
38089 if(removeEl === true){
38090 this.el.update("");
38095 createStrip : function(container)
38097 var strip = document.createElement("nav");
38098 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38099 container.appendChild(strip);
38103 createStripList : function(strip)
38105 // div wrapper for retard IE
38106 // returns the "tr" element.
38107 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38108 //'<div class="x-tabs-strip-wrap">'+
38109 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38110 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38111 return strip.firstChild; //.firstChild.firstChild.firstChild;
38113 createBody : function(container)
38115 var body = document.createElement("div");
38116 Roo.id(body, "tab-body");
38117 //Roo.fly(body).addClass("x-tabs-body");
38118 Roo.fly(body).addClass("tab-content");
38119 container.appendChild(body);
38122 createItemBody :function(bodyEl, id){
38123 var body = Roo.getDom(id);
38125 body = document.createElement("div");
38128 //Roo.fly(body).addClass("x-tabs-item-body");
38129 Roo.fly(body).addClass("tab-pane");
38130 bodyEl.insertBefore(body, bodyEl.firstChild);
38134 createStripElements : function(stripEl, text, closable, tpl)
38136 var td = document.createElement("li"); // was td..
38139 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38142 stripEl.appendChild(td);
38144 td.className = "x-tabs-closable";
38145 if(!this.closeTpl){
38146 this.closeTpl = new Roo.Template(
38147 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38148 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38149 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38152 var el = this.closeTpl.overwrite(td, {"text": text});
38153 var close = el.getElementsByTagName("div")[0];
38154 var inner = el.getElementsByTagName("em")[0];
38155 return {"el": el, "close": close, "inner": inner};
38158 // not sure what this is..
38159 // if(!this.tabTpl){
38160 //this.tabTpl = new Roo.Template(
38161 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38162 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38164 // this.tabTpl = new Roo.Template(
38165 // '<a href="#">' +
38166 // '<span unselectable="on"' +
38167 // (this.disableTooltips ? '' : ' title="{text}"') +
38168 // ' >{text}</span></a>'
38174 var template = tpl || this.tabTpl || false;
38178 template = new Roo.Template(
38180 '<span unselectable="on"' +
38181 (this.disableTooltips ? '' : ' title="{text}"') +
38182 ' >{text}</span></a>'
38186 switch (typeof(template)) {
38190 template = new Roo.Template(template);
38196 var el = template.overwrite(td, {"text": text});
38198 var inner = el.getElementsByTagName("span")[0];
38200 return {"el": el, "inner": inner};
38208 * @class Roo.TabPanelItem
38209 * @extends Roo.util.Observable
38210 * Represents an individual item (tab plus body) in a TabPanel.
38211 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38212 * @param {String} id The id of this TabPanelItem
38213 * @param {String} text The text for the tab of this TabPanelItem
38214 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38216 Roo.bootstrap.panel.TabItem = function(config){
38218 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38219 * @type Roo.TabPanel
38221 this.tabPanel = config.panel;
38223 * The id for this TabPanelItem
38226 this.id = config.id;
38228 this.disabled = false;
38230 this.text = config.text;
38232 this.loaded = false;
38233 this.closable = config.closable;
38236 * The body element for this TabPanelItem.
38237 * @type Roo.Element
38239 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38240 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38241 this.bodyEl.setStyle("display", "block");
38242 this.bodyEl.setStyle("zoom", "1");
38243 //this.hideAction();
38245 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38247 this.el = Roo.get(els.el);
38248 this.inner = Roo.get(els.inner, true);
38249 this.textEl = Roo.get(this.el.dom.firstChild, true);
38250 this.pnode = Roo.get(els.el.parentNode, true);
38251 // this.el.on("mousedown", this.onTabMouseDown, this);
38252 this.el.on("click", this.onTabClick, this);
38254 if(config.closable){
38255 var c = Roo.get(els.close, true);
38256 c.dom.title = this.closeText;
38257 c.addClassOnOver("close-over");
38258 c.on("click", this.closeClick, this);
38264 * Fires when this tab becomes the active tab.
38265 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38266 * @param {Roo.TabPanelItem} this
38270 * @event beforeclose
38271 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38272 * @param {Roo.TabPanelItem} this
38273 * @param {Object} e Set cancel to true on this object to cancel the close.
38275 "beforeclose": true,
38278 * Fires when this tab is closed.
38279 * @param {Roo.TabPanelItem} this
38283 * @event deactivate
38284 * Fires when this tab is no longer the active tab.
38285 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38286 * @param {Roo.TabPanelItem} this
38288 "deactivate" : true
38290 this.hidden = false;
38292 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38295 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38297 purgeListeners : function(){
38298 Roo.util.Observable.prototype.purgeListeners.call(this);
38299 this.el.removeAllListeners();
38302 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38305 this.pnode.addClass("active");
38308 this.tabPanel.stripWrap.repaint();
38310 this.fireEvent("activate", this.tabPanel, this);
38314 * Returns true if this tab is the active tab.
38315 * @return {Boolean}
38317 isActive : function(){
38318 return this.tabPanel.getActiveTab() == this;
38322 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38325 this.pnode.removeClass("active");
38327 this.fireEvent("deactivate", this.tabPanel, this);
38330 hideAction : function(){
38331 this.bodyEl.hide();
38332 this.bodyEl.setStyle("position", "absolute");
38333 this.bodyEl.setLeft("-20000px");
38334 this.bodyEl.setTop("-20000px");
38337 showAction : function(){
38338 this.bodyEl.setStyle("position", "relative");
38339 this.bodyEl.setTop("");
38340 this.bodyEl.setLeft("");
38341 this.bodyEl.show();
38345 * Set the tooltip for the tab.
38346 * @param {String} tooltip The tab's tooltip
38348 setTooltip : function(text){
38349 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38350 this.textEl.dom.qtip = text;
38351 this.textEl.dom.removeAttribute('title');
38353 this.textEl.dom.title = text;
38357 onTabClick : function(e){
38358 e.preventDefault();
38359 this.tabPanel.activate(this.id);
38362 onTabMouseDown : function(e){
38363 e.preventDefault();
38364 this.tabPanel.activate(this.id);
38367 getWidth : function(){
38368 return this.inner.getWidth();
38371 setWidth : function(width){
38372 var iwidth = width - this.pnode.getPadding("lr");
38373 this.inner.setWidth(iwidth);
38374 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38375 this.pnode.setWidth(width);
38379 * Show or hide the tab
38380 * @param {Boolean} hidden True to hide or false to show.
38382 setHidden : function(hidden){
38383 this.hidden = hidden;
38384 this.pnode.setStyle("display", hidden ? "none" : "");
38388 * Returns true if this tab is "hidden"
38389 * @return {Boolean}
38391 isHidden : function(){
38392 return this.hidden;
38396 * Returns the text for this tab
38399 getText : function(){
38403 autoSize : function(){
38404 //this.el.beginMeasure();
38405 this.textEl.setWidth(1);
38407 * #2804 [new] Tabs in Roojs
38408 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38410 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38411 //this.el.endMeasure();
38415 * Sets the text for the tab (Note: this also sets the tooltip text)
38416 * @param {String} text The tab's text and tooltip
38418 setText : function(text){
38420 this.textEl.update(text);
38421 this.setTooltip(text);
38422 //if(!this.tabPanel.resizeTabs){
38423 // this.autoSize();
38427 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38429 activate : function(){
38430 this.tabPanel.activate(this.id);
38434 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38436 disable : function(){
38437 if(this.tabPanel.active != this){
38438 this.disabled = true;
38439 this.pnode.addClass("disabled");
38444 * Enables this TabPanelItem if it was previously disabled.
38446 enable : function(){
38447 this.disabled = false;
38448 this.pnode.removeClass("disabled");
38452 * Sets the content for this TabPanelItem.
38453 * @param {String} content The content
38454 * @param {Boolean} loadScripts true to look for and load scripts
38456 setContent : function(content, loadScripts){
38457 this.bodyEl.update(content, loadScripts);
38461 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38462 * @return {Roo.UpdateManager} The UpdateManager
38464 getUpdateManager : function(){
38465 return this.bodyEl.getUpdateManager();
38469 * Set a URL to be used to load the content for this TabPanelItem.
38470 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38471 * @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)
38472 * @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)
38473 * @return {Roo.UpdateManager} The UpdateManager
38475 setUrl : function(url, params, loadOnce){
38476 if(this.refreshDelegate){
38477 this.un('activate', this.refreshDelegate);
38479 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38480 this.on("activate", this.refreshDelegate);
38481 return this.bodyEl.getUpdateManager();
38485 _handleRefresh : function(url, params, loadOnce){
38486 if(!loadOnce || !this.loaded){
38487 var updater = this.bodyEl.getUpdateManager();
38488 updater.update(url, params, this._setLoaded.createDelegate(this));
38493 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38494 * Will fail silently if the setUrl method has not been called.
38495 * This does not activate the panel, just updates its content.
38497 refresh : function(){
38498 if(this.refreshDelegate){
38499 this.loaded = false;
38500 this.refreshDelegate();
38505 _setLoaded : function(){
38506 this.loaded = true;
38510 closeClick : function(e){
38513 this.fireEvent("beforeclose", this, o);
38514 if(o.cancel !== true){
38515 this.tabPanel.removeTab(this.id);
38519 * The text displayed in the tooltip for the close icon.
38522 closeText : "Close this tab"
38525 * This script refer to:
38526 * Title: International Telephone Input
38527 * Author: Jack O'Connor
38528 * Code version: v12.1.12
38529 * Availability: https://github.com/jackocnr/intl-tel-input.git
38532 Roo.bootstrap.PhoneInputData = function() {
38535 "Afghanistan (افغانستان)",
38540 "Albania (Shqipëri)",
38545 "Algeria (الجزائر)",
38570 "Antigua and Barbuda",
38580 "Armenia (Հայաստան)",
38596 "Austria (Österreich)",
38601 "Azerbaijan (Azərbaycan)",
38611 "Bahrain (البحرين)",
38616 "Bangladesh (বাংলাদেশ)",
38626 "Belarus (Беларусь)",
38631 "Belgium (België)",
38661 "Bosnia and Herzegovina (Босна и Херцеговина)",
38676 "British Indian Ocean Territory",
38681 "British Virgin Islands",
38691 "Bulgaria (България)",
38701 "Burundi (Uburundi)",
38706 "Cambodia (កម្ពុជា)",
38711 "Cameroon (Cameroun)",
38720 ["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"]
38723 "Cape Verde (Kabu Verdi)",
38728 "Caribbean Netherlands",
38739 "Central African Republic (République centrafricaine)",
38759 "Christmas Island",
38765 "Cocos (Keeling) Islands",
38776 "Comoros (جزر القمر)",
38781 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38786 "Congo (Republic) (Congo-Brazzaville)",
38806 "Croatia (Hrvatska)",
38827 "Czech Republic (Česká republika)",
38832 "Denmark (Danmark)",
38847 "Dominican Republic (República Dominicana)",
38851 ["809", "829", "849"]
38869 "Equatorial Guinea (Guinea Ecuatorial)",
38889 "Falkland Islands (Islas Malvinas)",
38894 "Faroe Islands (Føroyar)",
38915 "French Guiana (Guyane française)",
38920 "French Polynesia (Polynésie française)",
38935 "Georgia (საქართველო)",
38940 "Germany (Deutschland)",
38960 "Greenland (Kalaallit Nunaat)",
38997 "Guinea-Bissau (Guiné Bissau)",
39022 "Hungary (Magyarország)",
39027 "Iceland (Ísland)",
39047 "Iraq (العراق)",
39063 "Israel (ישראל)",
39090 "Jordan (الأردن)",
39095 "Kazakhstan (Казахстан)",
39116 "Kuwait (الكويت)",
39121 "Kyrgyzstan (Кыргызстан)",
39131 "Latvia (Latvija)",
39136 "Lebanon (لبنان)",
39151 "Libya (ليبيا)",
39161 "Lithuania (Lietuva)",
39176 "Macedonia (FYROM) (Македонија)",
39181 "Madagascar (Madagasikara)",
39211 "Marshall Islands",
39221 "Mauritania (موريتانيا)",
39226 "Mauritius (Moris)",
39247 "Moldova (Republica Moldova)",
39257 "Mongolia (Монгол)",
39262 "Montenegro (Crna Gora)",
39272 "Morocco (المغرب)",
39278 "Mozambique (Moçambique)",
39283 "Myanmar (Burma) (မြန်မာ)",
39288 "Namibia (Namibië)",
39303 "Netherlands (Nederland)",
39308 "New Caledonia (Nouvelle-Calédonie)",
39343 "North Korea (조선 민주주의 인민 공화국)",
39348 "Northern Mariana Islands",
39364 "Pakistan (پاکستان)",
39374 "Palestine (فلسطين)",
39384 "Papua New Guinea",
39426 "Réunion (La Réunion)",
39432 "Romania (România)",
39448 "Saint Barthélemy",
39459 "Saint Kitts and Nevis",
39469 "Saint Martin (Saint-Martin (partie française))",
39475 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39480 "Saint Vincent and the Grenadines",
39495 "São Tomé and Príncipe (São Tomé e Príncipe)",
39500 "Saudi Arabia (المملكة العربية السعودية)",
39505 "Senegal (Sénégal)",
39535 "Slovakia (Slovensko)",
39540 "Slovenia (Slovenija)",
39550 "Somalia (Soomaaliya)",
39560 "South Korea (대한민국)",
39565 "South Sudan (جنوب السودان)",
39575 "Sri Lanka (ශ්රී ලංකාව)",
39580 "Sudan (السودان)",
39590 "Svalbard and Jan Mayen",
39601 "Sweden (Sverige)",
39606 "Switzerland (Schweiz)",
39611 "Syria (سوريا)",
39656 "Trinidad and Tobago",
39661 "Tunisia (تونس)",
39666 "Turkey (Türkiye)",
39676 "Turks and Caicos Islands",
39686 "U.S. Virgin Islands",
39696 "Ukraine (Україна)",
39701 "United Arab Emirates (الإمارات العربية المتحدة)",
39723 "Uzbekistan (Oʻzbekiston)",
39733 "Vatican City (Città del Vaticano)",
39744 "Vietnam (Việt Nam)",
39749 "Wallis and Futuna (Wallis-et-Futuna)",
39754 "Western Sahara (الصحراء الغربية)",
39760 "Yemen (اليمن)",
39784 * This script refer to:
39785 * Title: International Telephone Input
39786 * Author: Jack O'Connor
39787 * Code version: v12.1.12
39788 * Availability: https://github.com/jackocnr/intl-tel-input.git
39792 * @class Roo.bootstrap.PhoneInput
39793 * @extends Roo.bootstrap.TriggerField
39794 * An input with International dial-code selection
39796 * @cfg {String} defaultDialCode default '+852'
39797 * @cfg {Array} preferedCountries default []
39800 * Create a new PhoneInput.
39801 * @param {Object} config Configuration options
39804 Roo.bootstrap.PhoneInput = function(config) {
39805 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39808 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39810 listWidth: undefined,
39812 selectedClass: 'active',
39814 invalidClass : "has-warning",
39816 validClass: 'has-success',
39818 allowed: '0123456789',
39821 * @cfg {String} defaultDialCode The default dial code when initializing the input
39823 defaultDialCode: '+852',
39826 * @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
39828 preferedCountries: false,
39830 getAutoCreate : function()
39832 var data = Roo.bootstrap.PhoneInputData();
39833 var align = this.labelAlign || this.parentLabelAlign();
39836 this.allCountries = [];
39837 this.dialCodeMapping = [];
39839 for (var i = 0; i < data.length; i++) {
39841 this.allCountries[i] = {
39845 priority: c[3] || 0,
39846 areaCodes: c[4] || null
39848 this.dialCodeMapping[c[2]] = {
39851 priority: c[3] || 0,
39852 areaCodes: c[4] || null
39864 cls : 'form-control tel-input',
39865 autocomplete: 'new-password'
39868 var hiddenInput = {
39871 cls: 'hidden-tel-input'
39875 hiddenInput.name = this.name;
39878 if (this.disabled) {
39879 input.disabled = true;
39882 var flag_container = {
39899 cls: this.hasFeedback ? 'has-feedback' : '',
39905 cls: 'dial-code-holder',
39912 cls: 'roo-select2-container input-group',
39919 if (this.fieldLabel.length) {
39922 tooltip: 'This field is required'
39928 cls: 'control-label',
39934 html: this.fieldLabel
39937 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39943 if(this.indicatorpos == 'right') {
39944 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39951 if(align == 'left') {
39959 if(this.labelWidth > 12){
39960 label.style = "width: " + this.labelWidth + 'px';
39962 if(this.labelWidth < 13 && this.labelmd == 0){
39963 this.labelmd = this.labelWidth;
39965 if(this.labellg > 0){
39966 label.cls += ' col-lg-' + this.labellg;
39967 input.cls += ' col-lg-' + (12 - this.labellg);
39969 if(this.labelmd > 0){
39970 label.cls += ' col-md-' + this.labelmd;
39971 container.cls += ' col-md-' + (12 - this.labelmd);
39973 if(this.labelsm > 0){
39974 label.cls += ' col-sm-' + this.labelsm;
39975 container.cls += ' col-sm-' + (12 - this.labelsm);
39977 if(this.labelxs > 0){
39978 label.cls += ' col-xs-' + this.labelxs;
39979 container.cls += ' col-xs-' + (12 - this.labelxs);
39989 var settings = this;
39991 ['xs','sm','md','lg'].map(function(size){
39992 if (settings[size]) {
39993 cfg.cls += ' col-' + size + '-' + settings[size];
39997 this.store = new Roo.data.Store({
39998 proxy : new Roo.data.MemoryProxy({}),
39999 reader : new Roo.data.JsonReader({
40010 'name' : 'dialCode',
40014 'name' : 'priority',
40018 'name' : 'areaCodes',
40025 if(!this.preferedCountries) {
40026 this.preferedCountries = [
40033 var p = this.preferedCountries.reverse();
40036 for (var i = 0; i < p.length; i++) {
40037 for (var j = 0; j < this.allCountries.length; j++) {
40038 if(this.allCountries[j].iso2 == p[i]) {
40039 var t = this.allCountries[j];
40040 this.allCountries.splice(j,1);
40041 this.allCountries.unshift(t);
40047 this.store.proxy.data = {
40049 data: this.allCountries
40055 initEvents : function()
40058 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40060 this.indicator = this.indicatorEl();
40061 this.flag = this.flagEl();
40062 this.dialCodeHolder = this.dialCodeHolderEl();
40064 this.trigger = this.el.select('div.flag-box',true).first();
40065 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40070 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40071 _this.list.setWidth(lw);
40074 this.list.on('mouseover', this.onViewOver, this);
40075 this.list.on('mousemove', this.onViewMove, this);
40076 this.inputEl().on("keyup", this.onKeyUp, this);
40078 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40080 this.view = new Roo.View(this.list, this.tpl, {
40081 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40084 this.view.on('click', this.onViewClick, this);
40085 this.setValue(this.defaultDialCode);
40088 onTriggerClick : function(e)
40090 Roo.log('trigger click');
40095 if(this.isExpanded()){
40097 this.hasFocus = false;
40099 this.store.load({});
40100 this.hasFocus = true;
40105 isExpanded : function()
40107 return this.list.isVisible();
40110 collapse : function()
40112 if(!this.isExpanded()){
40116 Roo.get(document).un('mousedown', this.collapseIf, this);
40117 Roo.get(document).un('mousewheel', this.collapseIf, this);
40118 this.fireEvent('collapse', this);
40122 expand : function()
40126 if(this.isExpanded() || !this.hasFocus){
40130 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40131 this.list.setWidth(lw);
40134 this.restrictHeight();
40136 Roo.get(document).on('mousedown', this.collapseIf, this);
40137 Roo.get(document).on('mousewheel', this.collapseIf, this);
40139 this.fireEvent('expand', this);
40142 restrictHeight : function()
40144 this.list.alignTo(this.inputEl(), this.listAlign);
40145 this.list.alignTo(this.inputEl(), this.listAlign);
40148 onViewOver : function(e, t)
40150 if(this.inKeyMode){
40153 var item = this.view.findItemFromChild(t);
40156 var index = this.view.indexOf(item);
40157 this.select(index, false);
40162 onViewClick : function(view, doFocus, el, e)
40164 var index = this.view.getSelectedIndexes()[0];
40166 var r = this.store.getAt(index);
40169 this.onSelect(r, index);
40171 if(doFocus !== false && !this.blockFocus){
40172 this.inputEl().focus();
40176 onViewMove : function(e, t)
40178 this.inKeyMode = false;
40181 select : function(index, scrollIntoView)
40183 this.selectedIndex = index;
40184 this.view.select(index);
40185 if(scrollIntoView !== false){
40186 var el = this.view.getNode(index);
40188 this.list.scrollChildIntoView(el, false);
40193 createList : function()
40195 this.list = Roo.get(document.body).createChild({
40197 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40198 style: 'display:none'
40201 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40204 collapseIf : function(e)
40206 var in_combo = e.within(this.el);
40207 var in_list = e.within(this.list);
40208 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40210 if (in_combo || in_list || is_list) {
40216 onSelect : function(record, index)
40218 if(this.fireEvent('beforeselect', this, record, index) !== false){
40220 this.setFlagClass(record.data.iso2);
40221 this.setDialCode(record.data.dialCode);
40222 this.hasFocus = false;
40224 this.fireEvent('select', this, record, index);
40228 flagEl : function()
40230 var flag = this.el.select('div.flag',true).first();
40237 dialCodeHolderEl : function()
40239 var d = this.el.select('input.dial-code-holder',true).first();
40246 setDialCode : function(v)
40248 this.dialCodeHolder.dom.value = '+'+v;
40251 setFlagClass : function(n)
40253 this.flag.dom.className = 'flag '+n;
40256 getValue : function()
40258 var v = this.inputEl().getValue();
40259 if(this.dialCodeHolder) {
40260 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40265 setValue : function(v)
40267 var d = this.getDialCode(v);
40269 //invalid dial code
40270 if(v.length == 0 || !d || d.length == 0) {
40272 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40273 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40279 this.setFlagClass(this.dialCodeMapping[d].iso2);
40280 this.setDialCode(d);
40281 this.inputEl().dom.value = v.replace('+'+d,'');
40282 this.hiddenEl().dom.value = this.getValue();
40287 getDialCode : function(v)
40291 if (v.length == 0) {
40292 return this.dialCodeHolder.dom.value;
40296 if (v.charAt(0) != "+") {
40299 var numericChars = "";
40300 for (var i = 1; i < v.length; i++) {
40301 var c = v.charAt(i);
40304 if (this.dialCodeMapping[numericChars]) {
40305 dialCode = v.substr(1, i);
40307 if (numericChars.length == 4) {
40317 this.setValue(this.defaultDialCode);
40321 hiddenEl : function()
40323 return this.el.select('input.hidden-tel-input',true).first();
40326 onKeyUp : function(e){
40328 var k = e.getKey();
40329 var c = e.getCharCode();
40332 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40333 this.allowed.indexOf(String.fromCharCode(c)) === -1
40338 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40341 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40345 this.setValue(this.getValue());
40350 * @class Roo.bootstrap.MoneyField
40351 * @extends Roo.bootstrap.ComboBox
40352 * Bootstrap MoneyField class
40355 * Create a new MoneyField.
40356 * @param {Object} config Configuration options
40359 Roo.bootstrap.MoneyField = function(config) {
40361 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40365 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40368 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40370 allowDecimals : true,
40372 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40374 decimalSeparator : ".",
40376 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40378 decimalPrecision : 0,
40380 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40382 allowNegative : true,
40384 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40388 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40390 minValue : Number.NEGATIVE_INFINITY,
40392 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40394 maxValue : Number.MAX_VALUE,
40396 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40398 minText : "The minimum value for this field is {0}",
40400 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40402 maxText : "The maximum value for this field is {0}",
40404 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40405 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40407 nanText : "{0} is not a valid number",
40409 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40413 * @cfg {String} defaults currency of the MoneyField
40414 * value should be in lkey
40416 defaultCurrency : false,
40418 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40420 thousandsDelimiter : false,
40430 getAutoCreate : function()
40432 var align = this.labelAlign || this.parentLabelAlign();
40444 cls : 'form-control roo-money-amount-input',
40445 autocomplete: 'new-password'
40448 var hiddenInput = {
40452 cls: 'hidden-number-input'
40456 hiddenInput.name = this.name;
40459 if (this.disabled) {
40460 input.disabled = true;
40463 var clg = 12 - this.inputlg;
40464 var cmd = 12 - this.inputmd;
40465 var csm = 12 - this.inputsm;
40466 var cxs = 12 - this.inputxs;
40470 cls : 'row roo-money-field',
40474 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40478 cls: 'roo-select2-container input-group',
40482 cls : 'form-control roo-money-currency-input',
40483 autocomplete: 'new-password',
40485 name : this.currencyName
40489 cls : 'input-group-addon',
40503 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40507 cls: this.hasFeedback ? 'has-feedback' : '',
40518 if (this.fieldLabel.length) {
40521 tooltip: 'This field is required'
40527 cls: 'control-label',
40533 html: this.fieldLabel
40536 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40542 if(this.indicatorpos == 'right') {
40543 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40550 if(align == 'left') {
40558 if(this.labelWidth > 12){
40559 label.style = "width: " + this.labelWidth + 'px';
40561 if(this.labelWidth < 13 && this.labelmd == 0){
40562 this.labelmd = this.labelWidth;
40564 if(this.labellg > 0){
40565 label.cls += ' col-lg-' + this.labellg;
40566 input.cls += ' col-lg-' + (12 - this.labellg);
40568 if(this.labelmd > 0){
40569 label.cls += ' col-md-' + this.labelmd;
40570 container.cls += ' col-md-' + (12 - this.labelmd);
40572 if(this.labelsm > 0){
40573 label.cls += ' col-sm-' + this.labelsm;
40574 container.cls += ' col-sm-' + (12 - this.labelsm);
40576 if(this.labelxs > 0){
40577 label.cls += ' col-xs-' + this.labelxs;
40578 container.cls += ' col-xs-' + (12 - this.labelxs);
40589 var settings = this;
40591 ['xs','sm','md','lg'].map(function(size){
40592 if (settings[size]) {
40593 cfg.cls += ' col-' + size + '-' + settings[size];
40600 initEvents : function()
40602 this.indicator = this.indicatorEl();
40604 this.initCurrencyEvent();
40606 this.initNumberEvent();
40609 initCurrencyEvent : function()
40612 throw "can not find store for combo";
40615 this.store = Roo.factory(this.store, Roo.data);
40616 this.store.parent = this;
40620 this.triggerEl = this.el.select('.input-group-addon', true).first();
40622 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40627 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40628 _this.list.setWidth(lw);
40631 this.list.on('mouseover', this.onViewOver, this);
40632 this.list.on('mousemove', this.onViewMove, this);
40633 this.list.on('scroll', this.onViewScroll, this);
40636 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40639 this.view = new Roo.View(this.list, this.tpl, {
40640 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40643 this.view.on('click', this.onViewClick, this);
40645 this.store.on('beforeload', this.onBeforeLoad, this);
40646 this.store.on('load', this.onLoad, this);
40647 this.store.on('loadexception', this.onLoadException, this);
40649 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40650 "up" : function(e){
40651 this.inKeyMode = true;
40655 "down" : function(e){
40656 if(!this.isExpanded()){
40657 this.onTriggerClick();
40659 this.inKeyMode = true;
40664 "enter" : function(e){
40667 if(this.fireEvent("specialkey", this, e)){
40668 this.onViewClick(false);
40674 "esc" : function(e){
40678 "tab" : function(e){
40681 if(this.fireEvent("specialkey", this, e)){
40682 this.onViewClick(false);
40690 doRelay : function(foo, bar, hname){
40691 if(hname == 'down' || this.scope.isExpanded()){
40692 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40700 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40704 initNumberEvent : function(e)
40706 this.inputEl().on("keydown" , this.fireKey, this);
40707 this.inputEl().on("focus", this.onFocus, this);
40708 this.inputEl().on("blur", this.onBlur, this);
40710 this.inputEl().relayEvent('keyup', this);
40712 if(this.indicator){
40713 this.indicator.addClass('invisible');
40716 this.originalValue = this.getValue();
40718 if(this.validationEvent == 'keyup'){
40719 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40720 this.inputEl().on('keyup', this.filterValidation, this);
40722 else if(this.validationEvent !== false){
40723 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40726 if(this.selectOnFocus){
40727 this.on("focus", this.preFocus, this);
40730 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40731 this.inputEl().on("keypress", this.filterKeys, this);
40733 this.inputEl().relayEvent('keypress', this);
40736 var allowed = "0123456789";
40738 if(this.allowDecimals){
40739 allowed += this.decimalSeparator;
40742 if(this.allowNegative){
40746 if(this.thousandsDelimiter) {
40750 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40752 var keyPress = function(e){
40754 var k = e.getKey();
40756 var c = e.getCharCode();
40759 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40760 allowed.indexOf(String.fromCharCode(c)) === -1
40766 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40770 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40775 this.inputEl().on("keypress", keyPress, this);
40779 onTriggerClick : function(e)
40786 this.loadNext = false;
40788 if(this.isExpanded()){
40793 this.hasFocus = true;
40795 if(this.triggerAction == 'all') {
40796 this.doQuery(this.allQuery, true);
40800 this.doQuery(this.getRawValue());
40803 getCurrency : function()
40805 var v = this.currencyEl().getValue();
40810 restrictHeight : function()
40812 this.list.alignTo(this.currencyEl(), this.listAlign);
40813 this.list.alignTo(this.currencyEl(), this.listAlign);
40816 onViewClick : function(view, doFocus, el, e)
40818 var index = this.view.getSelectedIndexes()[0];
40820 var r = this.store.getAt(index);
40823 this.onSelect(r, index);
40827 onSelect : function(record, index){
40829 if(this.fireEvent('beforeselect', this, record, index) !== false){
40831 this.setFromCurrencyData(index > -1 ? record.data : false);
40835 this.fireEvent('select', this, record, index);
40839 setFromCurrencyData : function(o)
40843 this.lastCurrency = o;
40845 if (this.currencyField) {
40846 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40848 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40851 this.lastSelectionText = currency;
40853 //setting default currency
40854 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40855 this.setCurrency(this.defaultCurrency);
40859 this.setCurrency(currency);
40862 setFromData : function(o)
40866 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40868 this.setFromCurrencyData(c);
40873 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40875 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40878 this.setValue(value);
40882 setCurrency : function(v)
40884 this.currencyValue = v;
40887 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40892 setValue : function(v)
40894 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40900 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40902 this.inputEl().dom.value = (v == '') ? '' :
40903 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40905 if(!this.allowZero && v === '0') {
40906 this.hiddenEl().dom.value = '';
40907 this.inputEl().dom.value = '';
40914 getRawValue : function()
40916 var v = this.inputEl().getValue();
40921 getValue : function()
40923 return this.fixPrecision(this.parseValue(this.getRawValue()));
40926 parseValue : function(value)
40928 if(this.thousandsDelimiter) {
40930 r = new RegExp(",", "g");
40931 value = value.replace(r, "");
40934 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40935 return isNaN(value) ? '' : value;
40939 fixPrecision : function(value)
40941 if(this.thousandsDelimiter) {
40943 r = new RegExp(",", "g");
40944 value = value.replace(r, "");
40947 var nan = isNaN(value);
40949 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40950 return nan ? '' : value;
40952 return parseFloat(value).toFixed(this.decimalPrecision);
40955 decimalPrecisionFcn : function(v)
40957 return Math.floor(v);
40960 validateValue : function(value)
40962 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40966 var num = this.parseValue(value);
40969 this.markInvalid(String.format(this.nanText, value));
40973 if(num < this.minValue){
40974 this.markInvalid(String.format(this.minText, this.minValue));
40978 if(num > this.maxValue){
40979 this.markInvalid(String.format(this.maxText, this.maxValue));
40986 validate : function()
40988 if(this.disabled || this.allowBlank){
40993 var currency = this.getCurrency();
40995 if(this.validateValue(this.getRawValue()) && currency.length){
41000 this.markInvalid();
41004 getName: function()
41009 beforeBlur : function()
41015 var v = this.parseValue(this.getRawValue());
41022 onBlur : function()
41026 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41027 //this.el.removeClass(this.focusClass);
41030 this.hasFocus = false;
41032 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41036 var v = this.getValue();
41038 if(String(v) !== String(this.startValue)){
41039 this.fireEvent('change', this, v, this.startValue);
41042 this.fireEvent("blur", this);
41045 inputEl : function()
41047 return this.el.select('.roo-money-amount-input', true).first();
41050 currencyEl : function()
41052 return this.el.select('.roo-money-currency-input', true).first();
41055 hiddenEl : function()
41057 return this.el.select('input.hidden-number-input',true).first();