4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2652 onRender : function(ct, position)
2654 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2657 var cfg = Roo.apply({}, this.getAutoCreate());
2660 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2662 //if (!cfg.name.length) {
2666 cfg.cls += ' ' + this.cls;
2669 cfg.style = this.style;
2671 this.el = Roo.get(document.body).createChild(cfg, position);
2673 //var type = this.el.dom.type;
2676 if(this.tabIndex !== undefined){
2677 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2680 this.dialogEl = this.el.select('.modal-dialog',true).first();
2681 this.bodyEl = this.el.select('.modal-body',true).first();
2682 this.closeEl = this.el.select('.modal-header .close', true).first();
2683 this.headerEl = this.el.select('.modal-header',true).first();
2684 this.titleEl = this.el.select('.modal-title',true).first();
2685 this.footerEl = this.el.select('.modal-footer',true).first();
2687 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2689 //this.el.addClass("x-dlg-modal");
2691 if (this.buttons.length) {
2692 Roo.each(this.buttons, function(bb) {
2693 var b = Roo.apply({}, bb);
2694 b.xns = b.xns || Roo.bootstrap;
2695 b.xtype = b.xtype || 'Button';
2696 if (typeof(b.listeners) == 'undefined') {
2697 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2700 var btn = Roo.factory(b);
2702 btn.render(this.el.select('.modal-footer div').first());
2706 // render the children.
2709 if(typeof(this.items) != 'undefined'){
2710 var items = this.items;
2713 for(var i =0;i < items.length;i++) {
2714 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2718 this.items = nitems;
2720 // where are these used - they used to be body/close/footer
2724 //this.el.addClass([this.fieldClass, this.cls]);
2728 getAutoCreate : function()
2732 html : this.html || ''
2737 cls : 'modal-title',
2741 if(this.specificTitle){
2747 if (this.allow_close) {
2759 if(this.size.length){
2760 size = 'modal-' + this.size;
2767 cls: "modal-dialog " + size,
2770 cls : "modal-content",
2773 cls : 'modal-header',
2778 cls : 'modal-footer',
2782 cls: 'btn-' + this.buttonPosition
2799 modal.cls += ' fade';
2805 getChildContainer : function() {
2810 getButtonContainer : function() {
2811 return this.el.select('.modal-footer div',true).first();
2814 initEvents : function()
2816 if (this.allow_close) {
2817 this.closeEl.on('click', this.hide, this);
2819 Roo.EventManager.onWindowResize(this.resize, this, true);
2826 this.maskEl.setSize(
2827 Roo.lib.Dom.getViewWidth(true),
2828 Roo.lib.Dom.getViewHeight(true)
2831 if (this.fitwindow) {
2833 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2834 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2839 if(this.max_width !== 0) {
2841 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2844 this.setSize(w, this.height);
2848 if(this.max_height) {
2849 this.setSize(w,Math.min(
2851 Roo.lib.Dom.getViewportHeight(true) - 60
2857 if(!this.fit_content) {
2858 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2862 this.setSize(w, Math.min(
2864 this.headerEl.getHeight() +
2865 this.footerEl.getHeight() +
2866 this.getChildHeight(this.bodyEl.dom.childNodes),
2867 Roo.lib.Dom.getViewportHeight(true) - 60)
2873 setSize : function(w,h)
2884 if (!this.rendered) {
2888 //this.el.setStyle('display', 'block');
2889 this.el.removeClass('hideing');
2890 this.el.addClass('show');
2892 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2895 this.el.addClass('in');
2898 this.el.addClass('in');
2901 // not sure how we can show data in here..
2903 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2906 Roo.get(document.body).addClass("x-body-masked");
2908 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2909 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910 this.maskEl.addClass('show');
2914 this.fireEvent('show', this);
2916 // set zindex here - otherwise it appears to be ignored...
2917 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2920 this.items.forEach( function(e) {
2921 e.layout ? e.layout() : false;
2929 if(this.fireEvent("beforehide", this) !== false){
2930 this.maskEl.removeClass('show');
2931 Roo.get(document.body).removeClass("x-body-masked");
2932 this.el.removeClass('in');
2933 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2935 if(this.animate){ // why
2936 this.el.addClass('hideing');
2938 if (!this.el.hasClass('hideing')) {
2939 return; // it's been shown again...
2941 this.el.removeClass('show');
2942 this.el.removeClass('hideing');
2946 this.el.removeClass('show');
2948 this.fireEvent('hide', this);
2951 isVisible : function()
2954 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2958 addButton : function(str, cb)
2962 var b = Roo.apply({}, { html : str } );
2963 b.xns = b.xns || Roo.bootstrap;
2964 b.xtype = b.xtype || 'Button';
2965 if (typeof(b.listeners) == 'undefined') {
2966 b.listeners = { click : cb.createDelegate(this) };
2969 var btn = Roo.factory(b);
2971 btn.render(this.el.select('.modal-footer div').first());
2977 setDefaultButton : function(btn)
2979 //this.el.select('.modal-footer').()
2983 resizeTo: function(w,h)
2987 this.dialogEl.setWidth(w);
2988 if (this.diff === false) {
2989 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2992 this.bodyEl.setHeight(h - this.diff);
2994 this.fireEvent('resize', this);
2997 setContentSize : function(w, h)
3001 onButtonClick: function(btn,e)
3004 this.fireEvent('btnclick', btn.name, e);
3007 * Set the title of the Dialog
3008 * @param {String} str new Title
3010 setTitle: function(str) {
3011 this.titleEl.dom.innerHTML = str;
3014 * Set the body of the Dialog
3015 * @param {String} str new Title
3017 setBody: function(str) {
3018 this.bodyEl.dom.innerHTML = str;
3021 * Set the body of the Dialog using the template
3022 * @param {Obj} data - apply this data to the template and replace the body contents.
3024 applyBody: function(obj)
3027 Roo.log("Error - using apply Body without a template");
3030 this.tmpl.overwrite(this.bodyEl, obj);
3033 getChildHeight : function(child_nodes)
3037 child_nodes.length == 0
3042 var child_height = 0;
3044 for(var i = 0; i < child_nodes.length; i++) {
3047 * for modal with tabs...
3048 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3050 var layout_childs = child_nodes[i].childNodes;
3052 for(var j = 0; j < layout_childs.length; j++) {
3054 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3056 var layout_body_childs = layout_childs[j].childNodes;
3058 for(var k = 0; k < layout_body_childs.length; k++) {
3060 if(layout_body_childs[k].classList.contains('navbar')) {
3061 child_height += layout_body_childs[k].offsetHeight;
3065 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3067 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3069 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3071 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3072 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3087 child_height += child_nodes[i].offsetHeight;
3088 // Roo.log(child_nodes[i].offsetHeight);
3091 return child_height;
3097 Roo.apply(Roo.bootstrap.Modal, {
3099 * Button config that displays a single OK button
3108 * Button config that displays Yes and No buttons
3124 * Button config that displays OK and Cancel buttons
3139 * Button config that displays Yes, No and Cancel buttons
3163 * messagebox - can be used as a replace
3167 * @class Roo.MessageBox
3168 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3172 Roo.Msg.alert('Status', 'Changes saved successfully.');
3174 // Prompt for user data:
3175 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3177 // process text value...
3181 // Show a dialog using config options:
3183 title:'Save Changes?',
3184 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3185 buttons: Roo.Msg.YESNOCANCEL,
3192 Roo.bootstrap.MessageBox = function(){
3193 var dlg, opt, mask, waitTimer;
3194 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3195 var buttons, activeTextEl, bwidth;
3199 var handleButton = function(button){
3201 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3205 var handleHide = function(){
3207 dlg.el.removeClass(opt.cls);
3210 // Roo.TaskMgr.stop(waitTimer);
3211 // waitTimer = null;
3216 var updateButtons = function(b){
3219 buttons["ok"].hide();
3220 buttons["cancel"].hide();
3221 buttons["yes"].hide();
3222 buttons["no"].hide();
3223 //dlg.footer.dom.style.display = 'none';
3226 dlg.footerEl.dom.style.display = '';
3227 for(var k in buttons){
3228 if(typeof buttons[k] != "function"){
3231 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3232 width += buttons[k].el.getWidth()+15;
3242 var handleEsc = function(d, k, e){
3243 if(opt && opt.closable !== false){
3253 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3254 * @return {Roo.BasicDialog} The BasicDialog element
3256 getDialog : function(){
3258 dlg = new Roo.bootstrap.Modal( {
3261 //constraintoviewport:false,
3263 //collapsible : false,
3268 //buttonAlign:"center",
3269 closeClick : function(){
3270 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3273 handleButton("cancel");
3278 dlg.on("hide", handleHide);
3280 //dlg.addKeyListener(27, handleEsc);
3282 this.buttons = buttons;
3283 var bt = this.buttonText;
3284 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3285 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3286 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3287 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3289 bodyEl = dlg.bodyEl.createChild({
3291 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3292 '<textarea class="roo-mb-textarea"></textarea>' +
3293 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3295 msgEl = bodyEl.dom.firstChild;
3296 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3297 textboxEl.enableDisplayMode();
3298 textboxEl.addKeyListener([10,13], function(){
3299 if(dlg.isVisible() && opt && opt.buttons){
3302 }else if(opt.buttons.yes){
3303 handleButton("yes");
3307 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3308 textareaEl.enableDisplayMode();
3309 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3310 progressEl.enableDisplayMode();
3312 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3313 var pf = progressEl.dom.firstChild;
3315 pp = Roo.get(pf.firstChild);
3316 pp.setHeight(pf.offsetHeight);
3324 * Updates the message box body text
3325 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3326 * the XHTML-compliant non-breaking space character '&#160;')
3327 * @return {Roo.MessageBox} This message box
3329 updateText : function(text)
3331 if(!dlg.isVisible() && !opt.width){
3332 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3333 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3335 msgEl.innerHTML = text || ' ';
3337 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3338 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3340 Math.min(opt.width || cw , this.maxWidth),
3341 Math.max(opt.minWidth || this.minWidth, bwidth)
3344 activeTextEl.setWidth(w);
3346 if(dlg.isVisible()){
3347 dlg.fixedcenter = false;
3349 // to big, make it scroll. = But as usual stupid IE does not support
3352 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3353 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3354 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3356 bodyEl.dom.style.height = '';
3357 bodyEl.dom.style.overflowY = '';
3360 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3362 bodyEl.dom.style.overflowX = '';
3365 dlg.setContentSize(w, bodyEl.getHeight());
3366 if(dlg.isVisible()){
3367 dlg.fixedcenter = true;
3373 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3374 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3375 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3376 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3377 * @return {Roo.MessageBox} This message box
3379 updateProgress : function(value, text){
3381 this.updateText(text);
3384 if (pp) { // weird bug on my firefox - for some reason this is not defined
3385 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3386 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3392 * Returns true if the message box is currently displayed
3393 * @return {Boolean} True if the message box is visible, else false
3395 isVisible : function(){
3396 return dlg && dlg.isVisible();
3400 * Hides the message box if it is displayed
3403 if(this.isVisible()){
3409 * Displays a new message box, or reinitializes an existing message box, based on the config options
3410 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3411 * The following config object properties are supported:
3413 Property Type Description
3414 ---------- --------------- ------------------------------------------------------------------------------------
3415 animEl String/Element An id or Element from which the message box should animate as it opens and
3416 closes (defaults to undefined)
3417 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3418 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3419 closable Boolean False to hide the top-right close button (defaults to true). Note that
3420 progress and wait dialogs will ignore this property and always hide the
3421 close button as they can only be closed programmatically.
3422 cls String A custom CSS class to apply to the message box element
3423 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3424 displayed (defaults to 75)
3425 fn Function A callback function to execute after closing the dialog. The arguments to the
3426 function will be btn (the name of the button that was clicked, if applicable,
3427 e.g. "ok"), and text (the value of the active text field, if applicable).
3428 Progress and wait dialogs will ignore this option since they do not respond to
3429 user actions and can only be closed programmatically, so any required function
3430 should be called by the same code after it closes the dialog.
3431 icon String A CSS class that provides a background image to be used as an icon for
3432 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3433 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3434 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3435 modal Boolean False to allow user interaction with the page while the message box is
3436 displayed (defaults to true)
3437 msg String A string that will replace the existing message box body text (defaults
3438 to the XHTML-compliant non-breaking space character ' ')
3439 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3440 progress Boolean True to display a progress bar (defaults to false)
3441 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3442 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3443 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3444 title String The title text
3445 value String The string value to set into the active textbox element if displayed
3446 wait Boolean True to display a progress bar (defaults to false)
3447 width Number The width of the dialog in pixels
3454 msg: 'Please enter your address:',
3456 buttons: Roo.MessageBox.OKCANCEL,
3459 animEl: 'addAddressBtn'
3462 * @param {Object} config Configuration options
3463 * @return {Roo.MessageBox} This message box
3465 show : function(options)
3468 // this causes nightmares if you show one dialog after another
3469 // especially on callbacks..
3471 if(this.isVisible()){
3474 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3475 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3476 Roo.log("New Dialog Message:" + options.msg )
3477 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3478 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3481 var d = this.getDialog();
3483 d.setTitle(opt.title || " ");
3484 d.closeEl.setDisplayed(opt.closable !== false);
3485 activeTextEl = textboxEl;
3486 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3491 textareaEl.setHeight(typeof opt.multiline == "number" ?
3492 opt.multiline : this.defaultTextHeight);
3493 activeTextEl = textareaEl;
3502 progressEl.setDisplayed(opt.progress === true);
3503 this.updateProgress(0);
3504 activeTextEl.dom.value = opt.value || "";
3506 dlg.setDefaultButton(activeTextEl);
3508 var bs = opt.buttons;
3512 }else if(bs && bs.yes){
3513 db = buttons["yes"];
3515 dlg.setDefaultButton(db);
3517 bwidth = updateButtons(opt.buttons);
3518 this.updateText(opt.msg);
3520 d.el.addClass(opt.cls);
3522 d.proxyDrag = opt.proxyDrag === true;
3523 d.modal = opt.modal !== false;
3524 d.mask = opt.modal !== false ? mask : false;
3526 // force it to the end of the z-index stack so it gets a cursor in FF
3527 document.body.appendChild(dlg.el.dom);
3528 d.animateTarget = null;
3529 d.show(options.animEl);
3535 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3536 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3537 * and closing the message box when the process is complete.
3538 * @param {String} title The title bar text
3539 * @param {String} msg The message box body text
3540 * @return {Roo.MessageBox} This message box
3542 progress : function(title, msg){
3549 minWidth: this.minProgressWidth,
3556 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3557 * If a callback function is passed it will be called after the user clicks the button, and the
3558 * id of the button that was clicked will be passed as the only parameter to the callback
3559 * (could also be the top-right close button).
3560 * @param {String} title The title bar text
3561 * @param {String} msg The message box body text
3562 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3563 * @param {Object} scope (optional) The scope of the callback function
3564 * @return {Roo.MessageBox} This message box
3566 alert : function(title, msg, fn, scope)
3581 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3582 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3583 * You are responsible for closing the message box when the process is complete.
3584 * @param {String} msg The message box body text
3585 * @param {String} title (optional) The title bar text
3586 * @return {Roo.MessageBox} This message box
3588 wait : function(msg, title){
3599 waitTimer = Roo.TaskMgr.start({
3601 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3609 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3610 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3611 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3612 * @param {String} title The title bar text
3613 * @param {String} msg The message box body text
3614 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3615 * @param {Object} scope (optional) The scope of the callback function
3616 * @return {Roo.MessageBox} This message box
3618 confirm : function(title, msg, fn, scope){
3622 buttons: this.YESNO,
3631 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3632 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3633 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3634 * (could also be the top-right close button) and the text that was entered will be passed as the two
3635 * parameters to the callback.
3636 * @param {String} title The title bar text
3637 * @param {String} msg The message box body text
3638 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3639 * @param {Object} scope (optional) The scope of the callback function
3640 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3641 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3642 * @return {Roo.MessageBox} This message box
3644 prompt : function(title, msg, fn, scope, multiline){
3648 buttons: this.OKCANCEL,
3653 multiline: multiline,
3660 * Button config that displays a single OK button
3665 * Button config that displays Yes and No buttons
3668 YESNO : {yes:true, no:true},
3670 * Button config that displays OK and Cancel buttons
3673 OKCANCEL : {ok:true, cancel:true},
3675 * Button config that displays Yes, No and Cancel buttons
3678 YESNOCANCEL : {yes:true, no:true, cancel:true},
3681 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3684 defaultTextHeight : 75,
3686 * The maximum width in pixels of the message box (defaults to 600)
3691 * The minimum width in pixels of the message box (defaults to 100)
3696 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3697 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3700 minProgressWidth : 250,
3702 * An object containing the default button text strings that can be overriden for localized language support.
3703 * Supported properties are: ok, cancel, yes and no.
3704 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3717 * Shorthand for {@link Roo.MessageBox}
3719 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3720 Roo.Msg = Roo.Msg || Roo.MessageBox;
3729 * @class Roo.bootstrap.Navbar
3730 * @extends Roo.bootstrap.Component
3731 * Bootstrap Navbar class
3734 * Create a new Navbar
3735 * @param {Object} config The config object
3739 Roo.bootstrap.Navbar = function(config){
3740 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3744 * @event beforetoggle
3745 * Fire before toggle the menu
3746 * @param {Roo.EventObject} e
3748 "beforetoggle" : true
3752 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3761 getAutoCreate : function(){
3764 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3768 initEvents :function ()
3770 //Roo.log(this.el.select('.navbar-toggle',true));
3771 this.el.select('.navbar-toggle',true).on('click', function() {
3772 if(this.fireEvent('beforetoggle', this) !== false){
3773 this.el.select('.navbar-collapse',true).toggleClass('in');
3783 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3785 var size = this.el.getSize();
3786 this.maskEl.setSize(size.width, size.height);
3787 this.maskEl.enableDisplayMode("block");
3796 getChildContainer : function()
3798 if (this.el.select('.collapse').getCount()) {
3799 return this.el.select('.collapse',true).first();
3832 * @class Roo.bootstrap.NavSimplebar
3833 * @extends Roo.bootstrap.Navbar
3834 * Bootstrap Sidebar class
3836 * @cfg {Boolean} inverse is inverted color
3838 * @cfg {String} type (nav | pills | tabs)
3839 * @cfg {Boolean} arrangement stacked | justified
3840 * @cfg {String} align (left | right) alignment
3842 * @cfg {Boolean} main (true|false) main nav bar? default false
3843 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3845 * @cfg {String} tag (header|footer|nav|div) default is nav
3851 * Create a new Sidebar
3852 * @param {Object} config The config object
3856 Roo.bootstrap.NavSimplebar = function(config){
3857 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3860 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3876 getAutoCreate : function(){
3880 tag : this.tag || 'div',
3893 this.type = this.type || 'nav';
3894 if (['tabs','pills'].indexOf(this.type)!==-1) {
3895 cfg.cn[0].cls += ' nav-' + this.type
3899 if (this.type!=='nav') {
3900 Roo.log('nav type must be nav/tabs/pills')
3902 cfg.cn[0].cls += ' navbar-nav'
3908 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3909 cfg.cn[0].cls += ' nav-' + this.arrangement;
3913 if (this.align === 'right') {
3914 cfg.cn[0].cls += ' navbar-right';
3918 cfg.cls += ' navbar-inverse';
3945 * @class Roo.bootstrap.NavHeaderbar
3946 * @extends Roo.bootstrap.NavSimplebar
3947 * Bootstrap Sidebar class
3949 * @cfg {String} brand what is brand
3950 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3951 * @cfg {String} brand_href href of the brand
3952 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3953 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3954 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3955 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3958 * Create a new Sidebar
3959 * @param {Object} config The config object
3963 Roo.bootstrap.NavHeaderbar = function(config){
3964 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3968 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3975 desktopCenter : false,
3978 getAutoCreate : function(){
3981 tag: this.nav || 'nav',
3988 if (this.desktopCenter) {
3989 cn.push({cls : 'container', cn : []});
3996 cls: 'navbar-header',
4001 cls: 'navbar-toggle',
4002 'data-toggle': 'collapse',
4007 html: 'Toggle navigation'
4029 cls: 'collapse navbar-collapse',
4033 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4035 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4036 cfg.cls += ' navbar-' + this.position;
4038 // tag can override this..
4040 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4043 if (this.brand !== '') {
4046 href: this.brand_href ? this.brand_href : '#',
4047 cls: 'navbar-brand',
4055 cfg.cls += ' main-nav';
4063 getHeaderChildContainer : function()
4065 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4066 return this.el.select('.navbar-header',true).first();
4069 return this.getChildContainer();
4073 initEvents : function()
4075 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4077 if (this.autohide) {
4082 Roo.get(document).on('scroll',function(e) {
4083 var ns = Roo.get(document).getScroll().top;
4084 var os = prevScroll;
4088 ft.removeClass('slideDown');
4089 ft.addClass('slideUp');
4092 ft.removeClass('slideUp');
4093 ft.addClass('slideDown');
4114 * @class Roo.bootstrap.NavSidebar
4115 * @extends Roo.bootstrap.Navbar
4116 * Bootstrap Sidebar class
4119 * Create a new Sidebar
4120 * @param {Object} config The config object
4124 Roo.bootstrap.NavSidebar = function(config){
4125 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4128 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4130 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4132 getAutoCreate : function(){
4137 cls: 'sidebar sidebar-nav'
4159 * @class Roo.bootstrap.NavGroup
4160 * @extends Roo.bootstrap.Component
4161 * Bootstrap NavGroup class
4162 * @cfg {String} align (left|right)
4163 * @cfg {Boolean} inverse
4164 * @cfg {String} type (nav|pills|tab) default nav
4165 * @cfg {String} navId - reference Id for navbar.
4169 * Create a new nav group
4170 * @param {Object} config The config object
4173 Roo.bootstrap.NavGroup = function(config){
4174 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4177 Roo.bootstrap.NavGroup.register(this);
4181 * Fires when the active item changes
4182 * @param {Roo.bootstrap.NavGroup} this
4183 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4184 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4191 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4202 getAutoCreate : function()
4204 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4211 if (['tabs','pills'].indexOf(this.type)!==-1) {
4212 cfg.cls += ' nav-' + this.type
4214 if (this.type!=='nav') {
4215 Roo.log('nav type must be nav/tabs/pills')
4217 cfg.cls += ' navbar-nav'
4220 if (this.parent() && this.parent().sidebar) {
4223 cls: 'dashboard-menu sidebar-menu'
4229 if (this.form === true) {
4235 if (this.align === 'right') {
4236 cfg.cls += ' navbar-right';
4238 cfg.cls += ' navbar-left';
4242 if (this.align === 'right') {
4243 cfg.cls += ' navbar-right';
4247 cfg.cls += ' navbar-inverse';
4255 * sets the active Navigation item
4256 * @param {Roo.bootstrap.NavItem} the new current navitem
4258 setActiveItem : function(item)
4261 Roo.each(this.navItems, function(v){
4266 v.setActive(false, true);
4273 item.setActive(true, true);
4274 this.fireEvent('changed', this, item, prev);
4279 * gets the active Navigation item
4280 * @return {Roo.bootstrap.NavItem} the current navitem
4282 getActive : function()
4286 Roo.each(this.navItems, function(v){
4297 indexOfNav : function()
4301 Roo.each(this.navItems, function(v,i){
4312 * adds a Navigation item
4313 * @param {Roo.bootstrap.NavItem} the navitem to add
4315 addItem : function(cfg)
4317 var cn = new Roo.bootstrap.NavItem(cfg);
4319 cn.parentId = this.id;
4320 cn.onRender(this.el, null);
4324 * register a Navigation item
4325 * @param {Roo.bootstrap.NavItem} the navitem to add
4327 register : function(item)
4329 this.navItems.push( item);
4330 item.navId = this.navId;
4335 * clear all the Navigation item
4338 clearAll : function()
4341 this.el.dom.innerHTML = '';
4344 getNavItem: function(tabId)
4347 Roo.each(this.navItems, function(e) {
4348 if (e.tabId == tabId) {
4358 setActiveNext : function()
4360 var i = this.indexOfNav(this.getActive());
4361 if (i > this.navItems.length) {
4364 this.setActiveItem(this.navItems[i+1]);
4366 setActivePrev : function()
4368 var i = this.indexOfNav(this.getActive());
4372 this.setActiveItem(this.navItems[i-1]);
4374 clearWasActive : function(except) {
4375 Roo.each(this.navItems, function(e) {
4376 if (e.tabId != except.tabId && e.was_active) {
4377 e.was_active = false;
4384 getWasActive : function ()
4387 Roo.each(this.navItems, function(e) {
4402 Roo.apply(Roo.bootstrap.NavGroup, {
4406 * register a Navigation Group
4407 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4409 register : function(navgrp)
4411 this.groups[navgrp.navId] = navgrp;
4415 * fetch a Navigation Group based on the navigation ID
4416 * @param {string} the navgroup to add
4417 * @returns {Roo.bootstrap.NavGroup} the navgroup
4419 get: function(navId) {
4420 if (typeof(this.groups[navId]) == 'undefined') {
4422 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4424 return this.groups[navId] ;
4439 * @class Roo.bootstrap.NavItem
4440 * @extends Roo.bootstrap.Component
4441 * Bootstrap Navbar.NavItem class
4442 * @cfg {String} href link to
4443 * @cfg {String} html content of button
4444 * @cfg {String} badge text inside badge
4445 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4446 * @cfg {String} glyphicon name of glyphicon
4447 * @cfg {String} icon name of font awesome icon
4448 * @cfg {Boolean} active Is item active
4449 * @cfg {Boolean} disabled Is item disabled
4451 * @cfg {Boolean} preventDefault (true | false) default false
4452 * @cfg {String} tabId the tab that this item activates.
4453 * @cfg {String} tagtype (a|span) render as a href or span?
4454 * @cfg {Boolean} animateRef (true|false) link to element default false
4457 * Create a new Navbar Item
4458 * @param {Object} config The config object
4460 Roo.bootstrap.NavItem = function(config){
4461 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4466 * The raw click event for the entire grid.
4467 * @param {Roo.EventObject} e
4472 * Fires when the active item active state changes
4473 * @param {Roo.bootstrap.NavItem} this
4474 * @param {boolean} state the new state
4480 * Fires when scroll to element
4481 * @param {Roo.bootstrap.NavItem} this
4482 * @param {Object} options
4483 * @param {Roo.EventObject} e
4491 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4499 preventDefault : false,
4506 getAutoCreate : function(){
4515 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4517 if (this.disabled) {
4518 cfg.cls += ' disabled';
4521 if (this.href || this.html || this.glyphicon || this.icon) {
4525 href : this.href || "#",
4526 html: this.html || ''
4531 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4534 if(this.glyphicon) {
4535 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4540 cfg.cn[0].html += " <span class='caret'></span>";
4544 if (this.badge !== '') {
4546 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4554 initEvents: function()
4556 if (typeof (this.menu) != 'undefined') {
4557 this.menu.parentType = this.xtype;
4558 this.menu.triggerEl = this.el;
4559 this.menu = this.addxtype(Roo.apply({}, this.menu));
4562 this.el.select('a',true).on('click', this.onClick, this);
4564 if(this.tagtype == 'span'){
4565 this.el.select('span',true).on('click', this.onClick, this);
4568 // at this point parent should be available..
4569 this.parent().register(this);
4572 onClick : function(e)
4574 if (e.getTarget('.dropdown-menu-item')) {
4575 // did you click on a menu itemm.... - then don't trigger onclick..
4580 this.preventDefault ||
4583 Roo.log("NavItem - prevent Default?");
4587 if (this.disabled) {
4591 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4592 if (tg && tg.transition) {
4593 Roo.log("waiting for the transitionend");
4599 //Roo.log("fire event clicked");
4600 if(this.fireEvent('click', this, e) === false){
4604 if(this.tagtype == 'span'){
4608 //Roo.log(this.href);
4609 var ael = this.el.select('a',true).first();
4612 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4613 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4614 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4615 return; // ignore... - it's a 'hash' to another page.
4617 Roo.log("NavItem - prevent Default?");
4619 this.scrollToElement(e);
4623 var p = this.parent();
4625 if (['tabs','pills'].indexOf(p.type)!==-1) {
4626 if (typeof(p.setActiveItem) !== 'undefined') {
4627 p.setActiveItem(this);
4631 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4632 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4633 // remove the collapsed menu expand...
4634 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4638 isActive: function () {
4641 setActive : function(state, fire, is_was_active)
4643 if (this.active && !state && this.navId) {
4644 this.was_active = true;
4645 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4647 nv.clearWasActive(this);
4651 this.active = state;
4654 this.el.removeClass('active');
4655 } else if (!this.el.hasClass('active')) {
4656 this.el.addClass('active');
4659 this.fireEvent('changed', this, state);
4662 // show a panel if it's registered and related..
4664 if (!this.navId || !this.tabId || !state || is_was_active) {
4668 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4672 var pan = tg.getPanelByName(this.tabId);
4676 // if we can not flip to new panel - go back to old nav highlight..
4677 if (false == tg.showPanel(pan)) {
4678 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4680 var onav = nv.getWasActive();
4682 onav.setActive(true, false, true);
4691 // this should not be here...
4692 setDisabled : function(state)
4694 this.disabled = state;
4696 this.el.removeClass('disabled');
4697 } else if (!this.el.hasClass('disabled')) {
4698 this.el.addClass('disabled');
4704 * Fetch the element to display the tooltip on.
4705 * @return {Roo.Element} defaults to this.el
4707 tooltipEl : function()
4709 return this.el.select('' + this.tagtype + '', true).first();
4712 scrollToElement : function(e)
4714 var c = document.body;
4717 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4719 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4720 c = document.documentElement;
4723 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4729 var o = target.calcOffsetsTo(c);
4736 this.fireEvent('scrollto', this, options, e);
4738 Roo.get(c).scrollTo('top', options.value, true);
4751 * <span> icon </span>
4752 * <span> text </span>
4753 * <span>badge </span>
4757 * @class Roo.bootstrap.NavSidebarItem
4758 * @extends Roo.bootstrap.NavItem
4759 * Bootstrap Navbar.NavSidebarItem class
4760 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4761 * {Boolean} open is the menu open
4762 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4763 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4764 * {String} buttonSize (sm|md|lg)the extra classes for the button
4765 * {Boolean} showArrow show arrow next to the text (default true)
4767 * Create a new Navbar Button
4768 * @param {Object} config The config object
4770 Roo.bootstrap.NavSidebarItem = function(config){
4771 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4776 * The raw click event for the entire grid.
4777 * @param {Roo.EventObject} e
4782 * Fires when the active item active state changes
4783 * @param {Roo.bootstrap.NavSidebarItem} this
4784 * @param {boolean} state the new state
4792 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4794 badgeWeight : 'default',
4800 buttonWeight : 'default',
4806 getAutoCreate : function(){
4811 href : this.href || '#',
4817 if(this.buttonView){
4820 href : this.href || '#',
4821 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4834 cfg.cls += ' active';
4837 if (this.disabled) {
4838 cfg.cls += ' disabled';
4841 cfg.cls += ' open x-open';
4844 if (this.glyphicon || this.icon) {
4845 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4846 a.cn.push({ tag : 'i', cls : c }) ;
4849 if(!this.buttonView){
4852 html : this.html || ''
4859 if (this.badge !== '') {
4860 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4866 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4869 a.cls += ' dropdown-toggle treeview' ;
4875 initEvents : function()
4877 if (typeof (this.menu) != 'undefined') {
4878 this.menu.parentType = this.xtype;
4879 this.menu.triggerEl = this.el;
4880 this.menu = this.addxtype(Roo.apply({}, this.menu));
4883 this.el.on('click', this.onClick, this);
4885 if(this.badge !== ''){
4886 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4891 onClick : function(e)
4898 if(this.preventDefault){
4902 this.fireEvent('click', this);
4905 disable : function()
4907 this.setDisabled(true);
4912 this.setDisabled(false);
4915 setDisabled : function(state)
4917 if(this.disabled == state){
4921 this.disabled = state;
4924 this.el.addClass('disabled');
4928 this.el.removeClass('disabled');
4933 setActive : function(state)
4935 if(this.active == state){
4939 this.active = state;
4942 this.el.addClass('active');
4946 this.el.removeClass('active');
4951 isActive: function ()
4956 setBadge : function(str)
4962 this.badgeEl.dom.innerHTML = str;
4979 * @class Roo.bootstrap.Row
4980 * @extends Roo.bootstrap.Component
4981 * Bootstrap Row class (contains columns...)
4985 * @param {Object} config The config object
4988 Roo.bootstrap.Row = function(config){
4989 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4992 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4994 getAutoCreate : function(){
5013 * @class Roo.bootstrap.Element
5014 * @extends Roo.bootstrap.Component
5015 * Bootstrap Element class
5016 * @cfg {String} html contents of the element
5017 * @cfg {String} tag tag of the element
5018 * @cfg {String} cls class of the element
5019 * @cfg {Boolean} preventDefault (true|false) default false
5020 * @cfg {Boolean} clickable (true|false) default false
5023 * Create a new Element
5024 * @param {Object} config The config object
5027 Roo.bootstrap.Element = function(config){
5028 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5034 * When a element is chick
5035 * @param {Roo.bootstrap.Element} this
5036 * @param {Roo.EventObject} e
5042 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5047 preventDefault: false,
5050 getAutoCreate : function(){
5054 // cls: this.cls, double assign in parent class Component.js :: onRender
5061 initEvents: function()
5063 Roo.bootstrap.Element.superclass.initEvents.call(this);
5066 this.el.on('click', this.onClick, this);
5071 onClick : function(e)
5073 if(this.preventDefault){
5077 this.fireEvent('click', this, e);
5080 getValue : function()
5082 return this.el.dom.innerHTML;
5085 setValue : function(value)
5087 this.el.dom.innerHTML = value;
5102 * @class Roo.bootstrap.Pagination
5103 * @extends Roo.bootstrap.Component
5104 * Bootstrap Pagination class
5105 * @cfg {String} size xs | sm | md | lg
5106 * @cfg {Boolean} inverse false | true
5109 * Create a new Pagination
5110 * @param {Object} config The config object
5113 Roo.bootstrap.Pagination = function(config){
5114 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5117 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5123 getAutoCreate : function(){
5129 cfg.cls += ' inverse';
5135 cfg.cls += " " + this.cls;
5153 * @class Roo.bootstrap.PaginationItem
5154 * @extends Roo.bootstrap.Component
5155 * Bootstrap PaginationItem class
5156 * @cfg {String} html text
5157 * @cfg {String} href the link
5158 * @cfg {Boolean} preventDefault (true | false) default true
5159 * @cfg {Boolean} active (true | false) default false
5160 * @cfg {Boolean} disabled default false
5164 * Create a new PaginationItem
5165 * @param {Object} config The config object
5169 Roo.bootstrap.PaginationItem = function(config){
5170 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5175 * The raw click event for the entire grid.
5176 * @param {Roo.EventObject} e
5182 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5186 preventDefault: true,
5191 getAutoCreate : function(){
5197 href : this.href ? this.href : '#',
5198 html : this.html ? this.html : ''
5208 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5212 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5218 initEvents: function() {
5220 this.el.on('click', this.onClick, this);
5223 onClick : function(e)
5225 Roo.log('PaginationItem on click ');
5226 if(this.preventDefault){
5234 this.fireEvent('click', this, e);
5250 * @class Roo.bootstrap.Slider
5251 * @extends Roo.bootstrap.Component
5252 * Bootstrap Slider class
5255 * Create a new Slider
5256 * @param {Object} config The config object
5259 Roo.bootstrap.Slider = function(config){
5260 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5263 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5265 getAutoCreate : function(){
5269 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5273 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5285 * Ext JS Library 1.1.1
5286 * Copyright(c) 2006-2007, Ext JS, LLC.
5288 * Originally Released Under LGPL - original licence link has changed is not relivant.
5291 * <script type="text/javascript">
5296 * @class Roo.grid.ColumnModel
5297 * @extends Roo.util.Observable
5298 * This is the default implementation of a ColumnModel used by the Grid. It defines
5299 * the columns in the grid.
5302 var colModel = new Roo.grid.ColumnModel([
5303 {header: "Ticker", width: 60, sortable: true, locked: true},
5304 {header: "Company Name", width: 150, sortable: true},
5305 {header: "Market Cap.", width: 100, sortable: true},
5306 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5307 {header: "Employees", width: 100, sortable: true, resizable: false}
5312 * The config options listed for this class are options which may appear in each
5313 * individual column definition.
5314 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5316 * @param {Object} config An Array of column config objects. See this class's
5317 * config objects for details.
5319 Roo.grid.ColumnModel = function(config){
5321 * The config passed into the constructor
5323 this.config = config;
5326 // if no id, create one
5327 // if the column does not have a dataIndex mapping,
5328 // map it to the order it is in the config
5329 for(var i = 0, len = config.length; i < len; i++){
5331 if(typeof c.dataIndex == "undefined"){
5334 if(typeof c.renderer == "string"){
5335 c.renderer = Roo.util.Format[c.renderer];
5337 if(typeof c.id == "undefined"){
5340 if(c.editor && c.editor.xtype){
5341 c.editor = Roo.factory(c.editor, Roo.grid);
5343 if(c.editor && c.editor.isFormField){
5344 c.editor = new Roo.grid.GridEditor(c.editor);
5346 this.lookup[c.id] = c;
5350 * The width of columns which have no width specified (defaults to 100)
5353 this.defaultWidth = 100;
5356 * Default sortable of columns which have no sortable specified (defaults to false)
5359 this.defaultSortable = false;
5363 * @event widthchange
5364 * Fires when the width of a column changes.
5365 * @param {ColumnModel} this
5366 * @param {Number} columnIndex The column index
5367 * @param {Number} newWidth The new width
5369 "widthchange": true,
5371 * @event headerchange
5372 * Fires when the text of a header changes.
5373 * @param {ColumnModel} this
5374 * @param {Number} columnIndex The column index
5375 * @param {Number} newText The new header text
5377 "headerchange": true,
5379 * @event hiddenchange
5380 * Fires when a column is hidden or "unhidden".
5381 * @param {ColumnModel} this
5382 * @param {Number} columnIndex The column index
5383 * @param {Boolean} hidden true if hidden, false otherwise
5385 "hiddenchange": true,
5387 * @event columnmoved
5388 * Fires when a column is moved.
5389 * @param {ColumnModel} this
5390 * @param {Number} oldIndex
5391 * @param {Number} newIndex
5393 "columnmoved" : true,
5395 * @event columlockchange
5396 * Fires when a column's locked state is changed
5397 * @param {ColumnModel} this
5398 * @param {Number} colIndex
5399 * @param {Boolean} locked true if locked
5401 "columnlockchange" : true
5403 Roo.grid.ColumnModel.superclass.constructor.call(this);
5405 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5407 * @cfg {String} header The header text to display in the Grid view.
5410 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5411 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5412 * specified, the column's index is used as an index into the Record's data Array.
5415 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5416 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5419 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5420 * Defaults to the value of the {@link #defaultSortable} property.
5421 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5424 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5427 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5430 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5433 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5436 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5437 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5438 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5439 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5442 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5445 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5448 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5451 * @cfg {String} cursor (Optional)
5454 * @cfg {String} tooltip (Optional)
5457 * @cfg {Number} xs (Optional)
5460 * @cfg {Number} sm (Optional)
5463 * @cfg {Number} md (Optional)
5466 * @cfg {Number} lg (Optional)
5469 * Returns the id of the column at the specified index.
5470 * @param {Number} index The column index
5471 * @return {String} the id
5473 getColumnId : function(index){
5474 return this.config[index].id;
5478 * Returns the column for a specified id.
5479 * @param {String} id The column id
5480 * @return {Object} the column
5482 getColumnById : function(id){
5483 return this.lookup[id];
5488 * Returns the column for a specified dataIndex.
5489 * @param {String} dataIndex The column dataIndex
5490 * @return {Object|Boolean} the column or false if not found
5492 getColumnByDataIndex: function(dataIndex){
5493 var index = this.findColumnIndex(dataIndex);
5494 return index > -1 ? this.config[index] : false;
5498 * Returns the index for a specified column id.
5499 * @param {String} id The column id
5500 * @return {Number} the index, or -1 if not found
5502 getIndexById : function(id){
5503 for(var i = 0, len = this.config.length; i < len; i++){
5504 if(this.config[i].id == id){
5512 * Returns the index for a specified column dataIndex.
5513 * @param {String} dataIndex The column dataIndex
5514 * @return {Number} the index, or -1 if not found
5517 findColumnIndex : function(dataIndex){
5518 for(var i = 0, len = this.config.length; i < len; i++){
5519 if(this.config[i].dataIndex == dataIndex){
5527 moveColumn : function(oldIndex, newIndex){
5528 var c = this.config[oldIndex];
5529 this.config.splice(oldIndex, 1);
5530 this.config.splice(newIndex, 0, c);
5531 this.dataMap = null;
5532 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5535 isLocked : function(colIndex){
5536 return this.config[colIndex].locked === true;
5539 setLocked : function(colIndex, value, suppressEvent){
5540 if(this.isLocked(colIndex) == value){
5543 this.config[colIndex].locked = value;
5545 this.fireEvent("columnlockchange", this, colIndex, value);
5549 getTotalLockedWidth : function(){
5551 for(var i = 0; i < this.config.length; i++){
5552 if(this.isLocked(i) && !this.isHidden(i)){
5553 this.totalWidth += this.getColumnWidth(i);
5559 getLockedCount : function(){
5560 for(var i = 0, len = this.config.length; i < len; i++){
5561 if(!this.isLocked(i)){
5566 return this.config.length;
5570 * Returns the number of columns.
5573 getColumnCount : function(visibleOnly){
5574 if(visibleOnly === true){
5576 for(var i = 0, len = this.config.length; i < len; i++){
5577 if(!this.isHidden(i)){
5583 return this.config.length;
5587 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5588 * @param {Function} fn
5589 * @param {Object} scope (optional)
5590 * @return {Array} result
5592 getColumnsBy : function(fn, scope){
5594 for(var i = 0, len = this.config.length; i < len; i++){
5595 var c = this.config[i];
5596 if(fn.call(scope||this, c, i) === true){
5604 * Returns true if the specified column is sortable.
5605 * @param {Number} col The column index
5608 isSortable : function(col){
5609 if(typeof this.config[col].sortable == "undefined"){
5610 return this.defaultSortable;
5612 return this.config[col].sortable;
5616 * Returns the rendering (formatting) function defined for the column.
5617 * @param {Number} col The column index.
5618 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5620 getRenderer : function(col){
5621 if(!this.config[col].renderer){
5622 return Roo.grid.ColumnModel.defaultRenderer;
5624 return this.config[col].renderer;
5628 * Sets the rendering (formatting) function for a column.
5629 * @param {Number} col The column index
5630 * @param {Function} fn The function to use to process the cell's raw data
5631 * to return HTML markup for the grid view. The render function is called with
5632 * the following parameters:<ul>
5633 * <li>Data value.</li>
5634 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5635 * <li>css A CSS style string to apply to the table cell.</li>
5636 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5637 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5638 * <li>Row index</li>
5639 * <li>Column index</li>
5640 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5642 setRenderer : function(col, fn){
5643 this.config[col].renderer = fn;
5647 * Returns the width for the specified column.
5648 * @param {Number} col The column index
5651 getColumnWidth : function(col){
5652 return this.config[col].width * 1 || this.defaultWidth;
5656 * Sets the width for a column.
5657 * @param {Number} col The column index
5658 * @param {Number} width The new width
5660 setColumnWidth : function(col, width, suppressEvent){
5661 this.config[col].width = width;
5662 this.totalWidth = null;
5664 this.fireEvent("widthchange", this, col, width);
5669 * Returns the total width of all columns.
5670 * @param {Boolean} includeHidden True to include hidden column widths
5673 getTotalWidth : function(includeHidden){
5674 if(!this.totalWidth){
5675 this.totalWidth = 0;
5676 for(var i = 0, len = this.config.length; i < len; i++){
5677 if(includeHidden || !this.isHidden(i)){
5678 this.totalWidth += this.getColumnWidth(i);
5682 return this.totalWidth;
5686 * Returns the header for the specified column.
5687 * @param {Number} col The column index
5690 getColumnHeader : function(col){
5691 return this.config[col].header;
5695 * Sets the header for a column.
5696 * @param {Number} col The column index
5697 * @param {String} header The new header
5699 setColumnHeader : function(col, header){
5700 this.config[col].header = header;
5701 this.fireEvent("headerchange", this, col, header);
5705 * Returns the tooltip for the specified column.
5706 * @param {Number} col The column index
5709 getColumnTooltip : function(col){
5710 return this.config[col].tooltip;
5713 * Sets the tooltip for a column.
5714 * @param {Number} col The column index
5715 * @param {String} tooltip The new tooltip
5717 setColumnTooltip : function(col, tooltip){
5718 this.config[col].tooltip = tooltip;
5722 * Returns the dataIndex for the specified column.
5723 * @param {Number} col The column index
5726 getDataIndex : function(col){
5727 return this.config[col].dataIndex;
5731 * Sets the dataIndex for a column.
5732 * @param {Number} col The column index
5733 * @param {Number} dataIndex The new dataIndex
5735 setDataIndex : function(col, dataIndex){
5736 this.config[col].dataIndex = dataIndex;
5742 * Returns true if the cell is editable.
5743 * @param {Number} colIndex The column index
5744 * @param {Number} rowIndex The row index - this is nto actually used..?
5747 isCellEditable : function(colIndex, rowIndex){
5748 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5752 * Returns the editor defined for the cell/column.
5753 * return false or null to disable editing.
5754 * @param {Number} colIndex The column index
5755 * @param {Number} rowIndex The row index
5758 getCellEditor : function(colIndex, rowIndex){
5759 return this.config[colIndex].editor;
5763 * Sets if a column is editable.
5764 * @param {Number} col The column index
5765 * @param {Boolean} editable True if the column is editable
5767 setEditable : function(col, editable){
5768 this.config[col].editable = editable;
5773 * Returns true if the column is hidden.
5774 * @param {Number} colIndex The column index
5777 isHidden : function(colIndex){
5778 return this.config[colIndex].hidden;
5783 * Returns true if the column width cannot be changed
5785 isFixed : function(colIndex){
5786 return this.config[colIndex].fixed;
5790 * Returns true if the column can be resized
5793 isResizable : function(colIndex){
5794 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5797 * Sets if a column is hidden.
5798 * @param {Number} colIndex The column index
5799 * @param {Boolean} hidden True if the column is hidden
5801 setHidden : function(colIndex, hidden){
5802 this.config[colIndex].hidden = hidden;
5803 this.totalWidth = null;
5804 this.fireEvent("hiddenchange", this, colIndex, hidden);
5808 * Sets the editor for a column.
5809 * @param {Number} col The column index
5810 * @param {Object} editor The editor object
5812 setEditor : function(col, editor){
5813 this.config[col].editor = editor;
5817 Roo.grid.ColumnModel.defaultRenderer = function(value)
5819 if(typeof value == "object") {
5822 if(typeof value == "string" && value.length < 1){
5826 return String.format("{0}", value);
5829 // Alias for backwards compatibility
5830 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5833 * Ext JS Library 1.1.1
5834 * Copyright(c) 2006-2007, Ext JS, LLC.
5836 * Originally Released Under LGPL - original licence link has changed is not relivant.
5839 * <script type="text/javascript">
5843 * @class Roo.LoadMask
5844 * A simple utility class for generically masking elements while loading data. If the element being masked has
5845 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5846 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5847 * element's UpdateManager load indicator and will be destroyed after the initial load.
5849 * Create a new LoadMask
5850 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5851 * @param {Object} config The config object
5853 Roo.LoadMask = function(el, config){
5854 this.el = Roo.get(el);
5855 Roo.apply(this, config);
5857 this.store.on('beforeload', this.onBeforeLoad, this);
5858 this.store.on('load', this.onLoad, this);
5859 this.store.on('loadexception', this.onLoadException, this);
5860 this.removeMask = false;
5862 var um = this.el.getUpdateManager();
5863 um.showLoadIndicator = false; // disable the default indicator
5864 um.on('beforeupdate', this.onBeforeLoad, this);
5865 um.on('update', this.onLoad, this);
5866 um.on('failure', this.onLoad, this);
5867 this.removeMask = true;
5871 Roo.LoadMask.prototype = {
5873 * @cfg {Boolean} removeMask
5874 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5875 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5879 * The text to display in a centered loading message box (defaults to 'Loading...')
5883 * @cfg {String} msgCls
5884 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5886 msgCls : 'x-mask-loading',
5889 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5895 * Disables the mask to prevent it from being displayed
5897 disable : function(){
5898 this.disabled = true;
5902 * Enables the mask so that it can be displayed
5904 enable : function(){
5905 this.disabled = false;
5908 onLoadException : function()
5912 if (typeof(arguments[3]) != 'undefined') {
5913 Roo.MessageBox.alert("Error loading",arguments[3]);
5917 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5918 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5925 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5930 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5934 onBeforeLoad : function(){
5936 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5941 destroy : function(){
5943 this.store.un('beforeload', this.onBeforeLoad, this);
5944 this.store.un('load', this.onLoad, this);
5945 this.store.un('loadexception', this.onLoadException, this);
5947 var um = this.el.getUpdateManager();
5948 um.un('beforeupdate', this.onBeforeLoad, this);
5949 um.un('update', this.onLoad, this);
5950 um.un('failure', this.onLoad, this);
5961 * @class Roo.bootstrap.Table
5962 * @extends Roo.bootstrap.Component
5963 * Bootstrap Table class
5964 * @cfg {String} cls table class
5965 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5966 * @cfg {String} bgcolor Specifies the background color for a table
5967 * @cfg {Number} border Specifies whether the table cells should have borders or not
5968 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5969 * @cfg {Number} cellspacing Specifies the space between cells
5970 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5971 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5972 * @cfg {String} sortable Specifies that the table should be sortable
5973 * @cfg {String} summary Specifies a summary of the content of a table
5974 * @cfg {Number} width Specifies the width of a table
5975 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5977 * @cfg {boolean} striped Should the rows be alternative striped
5978 * @cfg {boolean} bordered Add borders to the table
5979 * @cfg {boolean} hover Add hover highlighting
5980 * @cfg {boolean} condensed Format condensed
5981 * @cfg {boolean} responsive Format condensed
5982 * @cfg {Boolean} loadMask (true|false) default false
5983 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5984 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5985 * @cfg {Boolean} rowSelection (true|false) default false
5986 * @cfg {Boolean} cellSelection (true|false) default false
5987 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5988 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5989 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5990 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5994 * Create a new Table
5995 * @param {Object} config The config object
5998 Roo.bootstrap.Table = function(config){
5999 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6004 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6005 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6006 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6007 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6009 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6011 this.sm.grid = this;
6012 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6013 this.sm = this.selModel;
6014 this.sm.xmodule = this.xmodule || false;
6017 if (this.cm && typeof(this.cm.config) == 'undefined') {
6018 this.colModel = new Roo.grid.ColumnModel(this.cm);
6019 this.cm = this.colModel;
6020 this.cm.xmodule = this.xmodule || false;
6023 this.store= Roo.factory(this.store, Roo.data);
6024 this.ds = this.store;
6025 this.ds.xmodule = this.xmodule || false;
6028 if (this.footer && this.store) {
6029 this.footer.dataSource = this.ds;
6030 this.footer = Roo.factory(this.footer);
6037 * Fires when a cell is clicked
6038 * @param {Roo.bootstrap.Table} this
6039 * @param {Roo.Element} el
6040 * @param {Number} rowIndex
6041 * @param {Number} columnIndex
6042 * @param {Roo.EventObject} e
6046 * @event celldblclick
6047 * Fires when a cell is double clicked
6048 * @param {Roo.bootstrap.Table} this
6049 * @param {Roo.Element} el
6050 * @param {Number} rowIndex
6051 * @param {Number} columnIndex
6052 * @param {Roo.EventObject} e
6054 "celldblclick" : true,
6057 * Fires when a row is clicked
6058 * @param {Roo.bootstrap.Table} this
6059 * @param {Roo.Element} el
6060 * @param {Number} rowIndex
6061 * @param {Roo.EventObject} e
6065 * @event rowdblclick
6066 * Fires when a row is double clicked
6067 * @param {Roo.bootstrap.Table} this
6068 * @param {Roo.Element} el
6069 * @param {Number} rowIndex
6070 * @param {Roo.EventObject} e
6072 "rowdblclick" : true,
6075 * Fires when a mouseover occur
6076 * @param {Roo.bootstrap.Table} this
6077 * @param {Roo.Element} el
6078 * @param {Number} rowIndex
6079 * @param {Number} columnIndex
6080 * @param {Roo.EventObject} e
6085 * Fires when a mouseout occur
6086 * @param {Roo.bootstrap.Table} this
6087 * @param {Roo.Element} el
6088 * @param {Number} rowIndex
6089 * @param {Number} columnIndex
6090 * @param {Roo.EventObject} e
6095 * Fires when a row is rendered, so you can change add a style to it.
6096 * @param {Roo.bootstrap.Table} this
6097 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6101 * @event rowsrendered
6102 * Fires when all the rows have been rendered
6103 * @param {Roo.bootstrap.Table} this
6105 'rowsrendered' : true,
6107 * @event contextmenu
6108 * The raw contextmenu event for the entire grid.
6109 * @param {Roo.EventObject} e
6111 "contextmenu" : true,
6113 * @event rowcontextmenu
6114 * Fires when a row is right clicked
6115 * @param {Roo.bootstrap.Table} this
6116 * @param {Number} rowIndex
6117 * @param {Roo.EventObject} e
6119 "rowcontextmenu" : true,
6121 * @event cellcontextmenu
6122 * Fires when a cell is right clicked
6123 * @param {Roo.bootstrap.Table} this
6124 * @param {Number} rowIndex
6125 * @param {Number} cellIndex
6126 * @param {Roo.EventObject} e
6128 "cellcontextmenu" : true,
6130 * @event headercontextmenu
6131 * Fires when a header is right clicked
6132 * @param {Roo.bootstrap.Table} this
6133 * @param {Number} columnIndex
6134 * @param {Roo.EventObject} e
6136 "headercontextmenu" : true
6140 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6166 rowSelection : false,
6167 cellSelection : false,
6170 // Roo.Element - the tbody
6172 // Roo.Element - thead element
6175 container: false, // used by gridpanel...
6181 auto_hide_footer : false,
6183 getAutoCreate : function()
6185 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6192 if (this.scrollBody) {
6193 cfg.cls += ' table-body-fixed';
6196 cfg.cls += ' table-striped';
6200 cfg.cls += ' table-hover';
6202 if (this.bordered) {
6203 cfg.cls += ' table-bordered';
6205 if (this.condensed) {
6206 cfg.cls += ' table-condensed';
6208 if (this.responsive) {
6209 cfg.cls += ' table-responsive';
6213 cfg.cls+= ' ' +this.cls;
6216 // this lot should be simplifed...
6229 ].forEach(function(k) {
6237 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6240 if(this.store || this.cm){
6241 if(this.headerShow){
6242 cfg.cn.push(this.renderHeader());
6245 cfg.cn.push(this.renderBody());
6247 if(this.footerShow){
6248 cfg.cn.push(this.renderFooter());
6250 // where does this come from?
6251 //cfg.cls+= ' TableGrid';
6254 return { cn : [ cfg ] };
6257 initEvents : function()
6259 if(!this.store || !this.cm){
6262 if (this.selModel) {
6263 this.selModel.initEvents();
6267 //Roo.log('initEvents with ds!!!!');
6269 this.mainBody = this.el.select('tbody', true).first();
6270 this.mainHead = this.el.select('thead', true).first();
6271 this.mainFoot = this.el.select('tfoot', true).first();
6277 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6278 e.on('click', _this.sort, _this);
6281 this.mainBody.on("click", this.onClick, this);
6282 this.mainBody.on("dblclick", this.onDblClick, this);
6284 // why is this done????? = it breaks dialogs??
6285 //this.parent().el.setStyle('position', 'relative');
6289 this.footer.parentId = this.id;
6290 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6293 this.el.select('tfoot tr td').first().addClass('hide');
6298 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6301 this.store.on('load', this.onLoad, this);
6302 this.store.on('beforeload', this.onBeforeLoad, this);
6303 this.store.on('update', this.onUpdate, this);
6304 this.store.on('add', this.onAdd, this);
6305 this.store.on("clear", this.clear, this);
6307 this.el.on("contextmenu", this.onContextMenu, this);
6309 this.mainBody.on('scroll', this.onBodyScroll, this);
6311 this.cm.on("headerchange", this.onHeaderChange, this);
6313 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6317 onContextMenu : function(e, t)
6319 this.processEvent("contextmenu", e);
6322 processEvent : function(name, e)
6324 if (name != 'touchstart' ) {
6325 this.fireEvent(name, e);
6328 var t = e.getTarget();
6330 var cell = Roo.get(t);
6336 if(cell.findParent('tfoot', false, true)){
6340 if(cell.findParent('thead', false, true)){
6342 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6343 cell = Roo.get(t).findParent('th', false, true);
6345 Roo.log("failed to find th in thead?");
6346 Roo.log(e.getTarget());
6351 var cellIndex = cell.dom.cellIndex;
6353 var ename = name == 'touchstart' ? 'click' : name;
6354 this.fireEvent("header" + ename, this, cellIndex, e);
6359 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6360 cell = Roo.get(t).findParent('td', false, true);
6362 Roo.log("failed to find th in tbody?");
6363 Roo.log(e.getTarget());
6368 var row = cell.findParent('tr', false, true);
6369 var cellIndex = cell.dom.cellIndex;
6370 var rowIndex = row.dom.rowIndex - 1;
6374 this.fireEvent("row" + name, this, rowIndex, e);
6378 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6384 onMouseover : function(e, el)
6386 var cell = Roo.get(el);
6392 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6393 cell = cell.findParent('td', false, true);
6396 var row = cell.findParent('tr', false, true);
6397 var cellIndex = cell.dom.cellIndex;
6398 var rowIndex = row.dom.rowIndex - 1; // start from 0
6400 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6404 onMouseout : function(e, el)
6406 var cell = Roo.get(el);
6412 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6413 cell = cell.findParent('td', false, true);
6416 var row = cell.findParent('tr', false, true);
6417 var cellIndex = cell.dom.cellIndex;
6418 var rowIndex = row.dom.rowIndex - 1; // start from 0
6420 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6424 onClick : function(e, el)
6426 var cell = Roo.get(el);
6428 if(!cell || (!this.cellSelection && !this.rowSelection)){
6432 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6433 cell = cell.findParent('td', false, true);
6436 if(!cell || typeof(cell) == 'undefined'){
6440 var row = cell.findParent('tr', false, true);
6442 if(!row || typeof(row) == 'undefined'){
6446 var cellIndex = cell.dom.cellIndex;
6447 var rowIndex = this.getRowIndex(row);
6449 // why??? - should these not be based on SelectionModel?
6450 if(this.cellSelection){
6451 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6454 if(this.rowSelection){
6455 this.fireEvent('rowclick', this, row, rowIndex, e);
6461 onDblClick : function(e,el)
6463 var cell = Roo.get(el);
6465 if(!cell || (!this.cellSelection && !this.rowSelection)){
6469 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6470 cell = cell.findParent('td', false, true);
6473 if(!cell || typeof(cell) == 'undefined'){
6477 var row = cell.findParent('tr', false, true);
6479 if(!row || typeof(row) == 'undefined'){
6483 var cellIndex = cell.dom.cellIndex;
6484 var rowIndex = this.getRowIndex(row);
6486 if(this.cellSelection){
6487 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6490 if(this.rowSelection){
6491 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6495 sort : function(e,el)
6497 var col = Roo.get(el);
6499 if(!col.hasClass('sortable')){
6503 var sort = col.attr('sort');
6506 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6510 this.store.sortInfo = {field : sort, direction : dir};
6513 Roo.log("calling footer first");
6514 this.footer.onClick('first');
6517 this.store.load({ params : { start : 0 } });
6521 renderHeader : function()
6529 this.totalWidth = 0;
6531 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6533 var config = cm.config[i];
6537 cls : 'x-hcol-' + i,
6539 html: cm.getColumnHeader(i)
6544 if(typeof(config.sortable) != 'undefined' && config.sortable){
6546 c.html = '<i class="glyphicon"></i>' + c.html;
6549 if(typeof(config.lgHeader) != 'undefined'){
6550 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6553 if(typeof(config.mdHeader) != 'undefined'){
6554 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6557 if(typeof(config.smHeader) != 'undefined'){
6558 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6561 if(typeof(config.xsHeader) != 'undefined'){
6562 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6569 if(typeof(config.tooltip) != 'undefined'){
6570 c.tooltip = config.tooltip;
6573 if(typeof(config.colspan) != 'undefined'){
6574 c.colspan = config.colspan;
6577 if(typeof(config.hidden) != 'undefined' && config.hidden){
6578 c.style += ' display:none;';
6581 if(typeof(config.dataIndex) != 'undefined'){
6582 c.sort = config.dataIndex;
6587 if(typeof(config.align) != 'undefined' && config.align.length){
6588 c.style += ' text-align:' + config.align + ';';
6591 if(typeof(config.width) != 'undefined'){
6592 c.style += ' width:' + config.width + 'px;';
6593 this.totalWidth += config.width;
6595 this.totalWidth += 100; // assume minimum of 100 per column?
6598 if(typeof(config.cls) != 'undefined'){
6599 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6602 ['xs','sm','md','lg'].map(function(size){
6604 if(typeof(config[size]) == 'undefined'){
6608 if (!config[size]) { // 0 = hidden
6609 c.cls += ' hidden-' + size;
6613 c.cls += ' col-' + size + '-' + config[size];
6623 renderBody : function()
6633 colspan : this.cm.getColumnCount()
6643 renderFooter : function()
6653 colspan : this.cm.getColumnCount()
6667 // Roo.log('ds onload');
6672 var ds = this.store;
6674 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6675 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6676 if (_this.store.sortInfo) {
6678 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6679 e.select('i', true).addClass(['glyphicon-arrow-up']);
6682 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6683 e.select('i', true).addClass(['glyphicon-arrow-down']);
6688 var tbody = this.mainBody;
6690 if(ds.getCount() > 0){
6691 ds.data.each(function(d,rowIndex){
6692 var row = this.renderRow(cm, ds, rowIndex);
6694 tbody.createChild(row);
6698 if(row.cellObjects.length){
6699 Roo.each(row.cellObjects, function(r){
6700 _this.renderCellObject(r);
6707 var tfoot = this.el.select('tfoot', true).first();
6709 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6711 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6713 var total = this.ds.getTotalCount();
6715 if(this.footer.pageSize < total){
6716 this.mainFoot.show();
6720 Roo.each(this.el.select('tbody td', true).elements, function(e){
6721 e.on('mouseover', _this.onMouseover, _this);
6724 Roo.each(this.el.select('tbody td', true).elements, function(e){
6725 e.on('mouseout', _this.onMouseout, _this);
6727 this.fireEvent('rowsrendered', this);
6733 onUpdate : function(ds,record)
6735 this.refreshRow(record);
6739 onRemove : function(ds, record, index, isUpdate){
6740 if(isUpdate !== true){
6741 this.fireEvent("beforerowremoved", this, index, record);
6743 var bt = this.mainBody.dom;
6745 var rows = this.el.select('tbody > tr', true).elements;
6747 if(typeof(rows[index]) != 'undefined'){
6748 bt.removeChild(rows[index].dom);
6751 // if(bt.rows[index]){
6752 // bt.removeChild(bt.rows[index]);
6755 if(isUpdate !== true){
6756 //this.stripeRows(index);
6757 //this.syncRowHeights(index, index);
6759 this.fireEvent("rowremoved", this, index, record);
6763 onAdd : function(ds, records, rowIndex)
6765 //Roo.log('on Add called');
6766 // - note this does not handle multiple adding very well..
6767 var bt = this.mainBody.dom;
6768 for (var i =0 ; i < records.length;i++) {
6769 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6770 //Roo.log(records[i]);
6771 //Roo.log(this.store.getAt(rowIndex+i));
6772 this.insertRow(this.store, rowIndex + i, false);
6779 refreshRow : function(record){
6780 var ds = this.store, index;
6781 if(typeof record == 'number'){
6783 record = ds.getAt(index);
6785 index = ds.indexOf(record);
6787 this.insertRow(ds, index, true);
6789 this.onRemove(ds, record, index+1, true);
6791 //this.syncRowHeights(index, index);
6793 this.fireEvent("rowupdated", this, index, record);
6796 insertRow : function(dm, rowIndex, isUpdate){
6799 this.fireEvent("beforerowsinserted", this, rowIndex);
6801 //var s = this.getScrollState();
6802 var row = this.renderRow(this.cm, this.store, rowIndex);
6803 // insert before rowIndex..
6804 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6808 if(row.cellObjects.length){
6809 Roo.each(row.cellObjects, function(r){
6810 _this.renderCellObject(r);
6815 this.fireEvent("rowsinserted", this, rowIndex);
6816 //this.syncRowHeights(firstRow, lastRow);
6817 //this.stripeRows(firstRow);
6824 getRowDom : function(rowIndex)
6826 var rows = this.el.select('tbody > tr', true).elements;
6828 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6831 // returns the object tree for a tr..
6834 renderRow : function(cm, ds, rowIndex)
6836 var d = ds.getAt(rowIndex);
6840 cls : 'x-row-' + rowIndex,
6844 var cellObjects = [];
6846 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6847 var config = cm.config[i];
6849 var renderer = cm.getRenderer(i);
6853 if(typeof(renderer) !== 'undefined'){
6854 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6856 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6857 // and are rendered into the cells after the row is rendered - using the id for the element.
6859 if(typeof(value) === 'object'){
6869 rowIndex : rowIndex,
6874 this.fireEvent('rowclass', this, rowcfg);
6878 cls : rowcfg.rowClass + ' x-col-' + i,
6880 html: (typeof(value) === 'object') ? '' : value
6887 if(typeof(config.colspan) != 'undefined'){
6888 td.colspan = config.colspan;
6891 if(typeof(config.hidden) != 'undefined' && config.hidden){
6892 td.style += ' display:none;';
6895 if(typeof(config.align) != 'undefined' && config.align.length){
6896 td.style += ' text-align:' + config.align + ';';
6898 if(typeof(config.valign) != 'undefined' && config.valign.length){
6899 td.style += ' vertical-align:' + config.valign + ';';
6902 if(typeof(config.width) != 'undefined'){
6903 td.style += ' width:' + config.width + 'px;';
6906 if(typeof(config.cursor) != 'undefined'){
6907 td.style += ' cursor:' + config.cursor + ';';
6910 if(typeof(config.cls) != 'undefined'){
6911 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6914 ['xs','sm','md','lg'].map(function(size){
6916 if(typeof(config[size]) == 'undefined'){
6920 if (!config[size]) { // 0 = hidden
6921 td.cls += ' hidden-' + size;
6925 td.cls += ' col-' + size + '-' + config[size];
6933 row.cellObjects = cellObjects;
6941 onBeforeLoad : function()
6950 this.el.select('tbody', true).first().dom.innerHTML = '';
6953 * Show or hide a row.
6954 * @param {Number} rowIndex to show or hide
6955 * @param {Boolean} state hide
6957 setRowVisibility : function(rowIndex, state)
6959 var bt = this.mainBody.dom;
6961 var rows = this.el.select('tbody > tr', true).elements;
6963 if(typeof(rows[rowIndex]) == 'undefined'){
6966 rows[rowIndex].dom.style.display = state ? '' : 'none';
6970 getSelectionModel : function(){
6972 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6974 return this.selModel;
6977 * Render the Roo.bootstrap object from renderder
6979 renderCellObject : function(r)
6983 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6985 var t = r.cfg.render(r.container);
6988 Roo.each(r.cfg.cn, function(c){
6990 container: t.getChildContainer(),
6993 _this.renderCellObject(child);
6998 getRowIndex : function(row)
7002 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7013 * Returns the grid's underlying element = used by panel.Grid
7014 * @return {Element} The element
7016 getGridEl : function(){
7020 * Forces a resize - used by panel.Grid
7021 * @return {Element} The element
7023 autoSize : function()
7025 //var ctr = Roo.get(this.container.dom.parentElement);
7026 var ctr = Roo.get(this.el.dom);
7028 var thd = this.getGridEl().select('thead',true).first();
7029 var tbd = this.getGridEl().select('tbody', true).first();
7030 var tfd = this.getGridEl().select('tfoot', true).first();
7032 var cw = ctr.getWidth();
7036 tbd.setSize(ctr.getWidth(),
7037 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7039 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7042 cw = Math.max(cw, this.totalWidth);
7043 this.getGridEl().select('tr',true).setWidth(cw);
7044 // resize 'expandable coloumn?
7046 return; // we doe not have a view in this design..
7049 onBodyScroll: function()
7051 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7053 this.mainHead.setStyle({
7054 'position' : 'relative',
7055 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7061 var scrollHeight = this.mainBody.dom.scrollHeight;
7063 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7065 var height = this.mainBody.getHeight();
7067 if(scrollHeight - height == scrollTop) {
7069 var total = this.ds.getTotalCount();
7071 if(this.footer.cursor + this.footer.pageSize < total){
7073 this.footer.ds.load({
7075 start : this.footer.cursor + this.footer.pageSize,
7076 limit : this.footer.pageSize
7086 onHeaderChange : function()
7088 var header = this.renderHeader();
7089 var table = this.el.select('table', true).first();
7091 this.mainHead.remove();
7092 this.mainHead = table.createChild(header, this.mainBody, false);
7095 onHiddenChange : function(colModel, colIndex, hidden)
7097 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7098 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7100 this.CSS.updateRule(thSelector, "display", "");
7101 this.CSS.updateRule(tdSelector, "display", "");
7104 this.CSS.updateRule(thSelector, "display", "none");
7105 this.CSS.updateRule(tdSelector, "display", "none");
7108 this.onHeaderChange();
7125 * @class Roo.bootstrap.TableCell
7126 * @extends Roo.bootstrap.Component
7127 * Bootstrap TableCell class
7128 * @cfg {String} html cell contain text
7129 * @cfg {String} cls cell class
7130 * @cfg {String} tag cell tag (td|th) default td
7131 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7132 * @cfg {String} align Aligns the content in a cell
7133 * @cfg {String} axis Categorizes cells
7134 * @cfg {String} bgcolor Specifies the background color of a cell
7135 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7136 * @cfg {Number} colspan Specifies the number of columns a cell should span
7137 * @cfg {String} headers Specifies one or more header cells a cell is related to
7138 * @cfg {Number} height Sets the height of a cell
7139 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7140 * @cfg {Number} rowspan Sets the number of rows a cell should span
7141 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7142 * @cfg {String} valign Vertical aligns the content in a cell
7143 * @cfg {Number} width Specifies the width of a cell
7146 * Create a new TableCell
7147 * @param {Object} config The config object
7150 Roo.bootstrap.TableCell = function(config){
7151 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7154 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7174 getAutoCreate : function(){
7175 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7195 cfg.align=this.align
7201 cfg.bgcolor=this.bgcolor
7204 cfg.charoff=this.charoff
7207 cfg.colspan=this.colspan
7210 cfg.headers=this.headers
7213 cfg.height=this.height
7216 cfg.nowrap=this.nowrap
7219 cfg.rowspan=this.rowspan
7222 cfg.scope=this.scope
7225 cfg.valign=this.valign
7228 cfg.width=this.width
7247 * @class Roo.bootstrap.TableRow
7248 * @extends Roo.bootstrap.Component
7249 * Bootstrap TableRow class
7250 * @cfg {String} cls row class
7251 * @cfg {String} align Aligns the content in a table row
7252 * @cfg {String} bgcolor Specifies a background color for a table row
7253 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7254 * @cfg {String} valign Vertical aligns the content in a table row
7257 * Create a new TableRow
7258 * @param {Object} config The config object
7261 Roo.bootstrap.TableRow = function(config){
7262 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7265 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7273 getAutoCreate : function(){
7274 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7284 cfg.align = this.align;
7287 cfg.bgcolor = this.bgcolor;
7290 cfg.charoff = this.charoff;
7293 cfg.valign = this.valign;
7311 * @class Roo.bootstrap.TableBody
7312 * @extends Roo.bootstrap.Component
7313 * Bootstrap TableBody class
7314 * @cfg {String} cls element class
7315 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7316 * @cfg {String} align Aligns the content inside the element
7317 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7318 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7321 * Create a new TableBody
7322 * @param {Object} config The config object
7325 Roo.bootstrap.TableBody = function(config){
7326 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7329 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7337 getAutoCreate : function(){
7338 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7352 cfg.align = this.align;
7355 cfg.charoff = this.charoff;
7358 cfg.valign = this.valign;
7365 // initEvents : function()
7372 // this.store = Roo.factory(this.store, Roo.data);
7373 // this.store.on('load', this.onLoad, this);
7375 // this.store.load();
7379 // onLoad: function ()
7381 // this.fireEvent('load', this);
7391 * Ext JS Library 1.1.1
7392 * Copyright(c) 2006-2007, Ext JS, LLC.
7394 * Originally Released Under LGPL - original licence link has changed is not relivant.
7397 * <script type="text/javascript">
7400 // as we use this in bootstrap.
7401 Roo.namespace('Roo.form');
7403 * @class Roo.form.Action
7404 * Internal Class used to handle form actions
7406 * @param {Roo.form.BasicForm} el The form element or its id
7407 * @param {Object} config Configuration options
7412 // define the action interface
7413 Roo.form.Action = function(form, options){
7415 this.options = options || {};
7418 * Client Validation Failed
7421 Roo.form.Action.CLIENT_INVALID = 'client';
7423 * Server Validation Failed
7426 Roo.form.Action.SERVER_INVALID = 'server';
7428 * Connect to Server Failed
7431 Roo.form.Action.CONNECT_FAILURE = 'connect';
7433 * Reading Data from Server Failed
7436 Roo.form.Action.LOAD_FAILURE = 'load';
7438 Roo.form.Action.prototype = {
7440 failureType : undefined,
7441 response : undefined,
7445 run : function(options){
7450 success : function(response){
7455 handleResponse : function(response){
7459 // default connection failure
7460 failure : function(response){
7462 this.response = response;
7463 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7464 this.form.afterAction(this, false);
7467 processResponse : function(response){
7468 this.response = response;
7469 if(!response.responseText){
7472 this.result = this.handleResponse(response);
7476 // utility functions used internally
7477 getUrl : function(appendParams){
7478 var url = this.options.url || this.form.url || this.form.el.dom.action;
7480 var p = this.getParams();
7482 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7488 getMethod : function(){
7489 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7492 getParams : function(){
7493 var bp = this.form.baseParams;
7494 var p = this.options.params;
7496 if(typeof p == "object"){
7497 p = Roo.urlEncode(Roo.applyIf(p, bp));
7498 }else if(typeof p == 'string' && bp){
7499 p += '&' + Roo.urlEncode(bp);
7502 p = Roo.urlEncode(bp);
7507 createCallback : function(){
7509 success: this.success,
7510 failure: this.failure,
7512 timeout: (this.form.timeout*1000),
7513 upload: this.form.fileUpload ? this.success : undefined
7518 Roo.form.Action.Submit = function(form, options){
7519 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7522 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7525 haveProgress : false,
7526 uploadComplete : false,
7528 // uploadProgress indicator.
7529 uploadProgress : function()
7531 if (!this.form.progressUrl) {
7535 if (!this.haveProgress) {
7536 Roo.MessageBox.progress("Uploading", "Uploading");
7538 if (this.uploadComplete) {
7539 Roo.MessageBox.hide();
7543 this.haveProgress = true;
7545 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7547 var c = new Roo.data.Connection();
7549 url : this.form.progressUrl,
7554 success : function(req){
7555 //console.log(data);
7559 rdata = Roo.decode(req.responseText)
7561 Roo.log("Invalid data from server..");
7565 if (!rdata || !rdata.success) {
7567 Roo.MessageBox.alert(Roo.encode(rdata));
7570 var data = rdata.data;
7572 if (this.uploadComplete) {
7573 Roo.MessageBox.hide();
7578 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7579 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7582 this.uploadProgress.defer(2000,this);
7585 failure: function(data) {
7586 Roo.log('progress url failed ');
7597 // run get Values on the form, so it syncs any secondary forms.
7598 this.form.getValues();
7600 var o = this.options;
7601 var method = this.getMethod();
7602 var isPost = method == 'POST';
7603 if(o.clientValidation === false || this.form.isValid()){
7605 if (this.form.progressUrl) {
7606 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7607 (new Date() * 1) + '' + Math.random());
7612 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7613 form:this.form.el.dom,
7614 url:this.getUrl(!isPost),
7616 params:isPost ? this.getParams() : null,
7617 isUpload: this.form.fileUpload
7620 this.uploadProgress();
7622 }else if (o.clientValidation !== false){ // client validation failed
7623 this.failureType = Roo.form.Action.CLIENT_INVALID;
7624 this.form.afterAction(this, false);
7628 success : function(response)
7630 this.uploadComplete= true;
7631 if (this.haveProgress) {
7632 Roo.MessageBox.hide();
7636 var result = this.processResponse(response);
7637 if(result === true || result.success){
7638 this.form.afterAction(this, true);
7642 this.form.markInvalid(result.errors);
7643 this.failureType = Roo.form.Action.SERVER_INVALID;
7645 this.form.afterAction(this, false);
7647 failure : function(response)
7649 this.uploadComplete= true;
7650 if (this.haveProgress) {
7651 Roo.MessageBox.hide();
7654 this.response = response;
7655 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7656 this.form.afterAction(this, false);
7659 handleResponse : function(response){
7660 if(this.form.errorReader){
7661 var rs = this.form.errorReader.read(response);
7664 for(var i = 0, len = rs.records.length; i < len; i++) {
7665 var r = rs.records[i];
7669 if(errors.length < 1){
7673 success : rs.success,
7679 ret = Roo.decode(response.responseText);
7683 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7693 Roo.form.Action.Load = function(form, options){
7694 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7695 this.reader = this.form.reader;
7698 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7703 Roo.Ajax.request(Roo.apply(
7704 this.createCallback(), {
7705 method:this.getMethod(),
7706 url:this.getUrl(false),
7707 params:this.getParams()
7711 success : function(response){
7713 var result = this.processResponse(response);
7714 if(result === true || !result.success || !result.data){
7715 this.failureType = Roo.form.Action.LOAD_FAILURE;
7716 this.form.afterAction(this, false);
7719 this.form.clearInvalid();
7720 this.form.setValues(result.data);
7721 this.form.afterAction(this, true);
7724 handleResponse : function(response){
7725 if(this.form.reader){
7726 var rs = this.form.reader.read(response);
7727 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7729 success : rs.success,
7733 return Roo.decode(response.responseText);
7737 Roo.form.Action.ACTION_TYPES = {
7738 'load' : Roo.form.Action.Load,
7739 'submit' : Roo.form.Action.Submit
7748 * @class Roo.bootstrap.Form
7749 * @extends Roo.bootstrap.Component
7750 * Bootstrap Form class
7751 * @cfg {String} method GET | POST (default POST)
7752 * @cfg {String} labelAlign top | left (default top)
7753 * @cfg {String} align left | right - for navbars
7754 * @cfg {Boolean} loadMask load mask when submit (default true)
7759 * @param {Object} config The config object
7763 Roo.bootstrap.Form = function(config){
7765 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7767 Roo.bootstrap.Form.popover.apply();
7771 * @event clientvalidation
7772 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7773 * @param {Form} this
7774 * @param {Boolean} valid true if the form has passed client-side validation
7776 clientvalidation: true,
7778 * @event beforeaction
7779 * Fires before any action is performed. Return false to cancel the action.
7780 * @param {Form} this
7781 * @param {Action} action The action to be performed
7785 * @event actionfailed
7786 * Fires when an action fails.
7787 * @param {Form} this
7788 * @param {Action} action The action that failed
7790 actionfailed : true,
7792 * @event actioncomplete
7793 * Fires when an action is completed.
7794 * @param {Form} this
7795 * @param {Action} action The action that completed
7797 actioncomplete : true
7801 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7804 * @cfg {String} method
7805 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7810 * The URL to use for form actions if one isn't supplied in the action options.
7813 * @cfg {Boolean} fileUpload
7814 * Set to true if this form is a file upload.
7818 * @cfg {Object} baseParams
7819 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7823 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7827 * @cfg {Sting} align (left|right) for navbar forms
7832 activeAction : null,
7835 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7836 * element by passing it or its id or mask the form itself by passing in true.
7839 waitMsgTarget : false,
7844 * @cfg {Boolean} errorMask (true|false) default false
7849 * @cfg {Number} maskOffset Default 100
7854 * @cfg {Boolean} maskBody
7858 getAutoCreate : function(){
7862 method : this.method || 'POST',
7863 id : this.id || Roo.id(),
7866 if (this.parent().xtype.match(/^Nav/)) {
7867 cfg.cls = 'navbar-form navbar-' + this.align;
7871 if (this.labelAlign == 'left' ) {
7872 cfg.cls += ' form-horizontal';
7878 initEvents : function()
7880 this.el.on('submit', this.onSubmit, this);
7881 // this was added as random key presses on the form where triggering form submit.
7882 this.el.on('keypress', function(e) {
7883 if (e.getCharCode() != 13) {
7886 // we might need to allow it for textareas.. and some other items.
7887 // check e.getTarget().
7889 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7893 Roo.log("keypress blocked");
7901 onSubmit : function(e){
7906 * Returns true if client-side validation on the form is successful.
7909 isValid : function(){
7910 var items = this.getItems();
7914 items.each(function(f){
7920 Roo.log('invalid field: ' + f.name);
7924 if(!target && f.el.isVisible(true)){
7930 if(this.errorMask && !valid){
7931 Roo.bootstrap.Form.popover.mask(this, target);
7938 * Returns true if any fields in this form have changed since their original load.
7941 isDirty : function(){
7943 var items = this.getItems();
7944 items.each(function(f){
7954 * Performs a predefined action (submit or load) or custom actions you define on this form.
7955 * @param {String} actionName The name of the action type
7956 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7957 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7958 * accept other config options):
7960 Property Type Description
7961 ---------------- --------------- ----------------------------------------------------------------------------------
7962 url String The url for the action (defaults to the form's url)
7963 method String The form method to use (defaults to the form's method, or POST if not defined)
7964 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7965 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7966 validate the form on the client (defaults to false)
7968 * @return {BasicForm} this
7970 doAction : function(action, options){
7971 if(typeof action == 'string'){
7972 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7974 if(this.fireEvent('beforeaction', this, action) !== false){
7975 this.beforeAction(action);
7976 action.run.defer(100, action);
7982 beforeAction : function(action){
7983 var o = action.options;
7988 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7990 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7993 // not really supported yet.. ??
7995 //if(this.waitMsgTarget === true){
7996 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7997 //}else if(this.waitMsgTarget){
7998 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7999 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8001 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8007 afterAction : function(action, success){
8008 this.activeAction = null;
8009 var o = action.options;
8014 Roo.get(document.body).unmask();
8020 //if(this.waitMsgTarget === true){
8021 // this.el.unmask();
8022 //}else if(this.waitMsgTarget){
8023 // this.waitMsgTarget.unmask();
8025 // Roo.MessageBox.updateProgress(1);
8026 // Roo.MessageBox.hide();
8033 Roo.callback(o.success, o.scope, [this, action]);
8034 this.fireEvent('actioncomplete', this, action);
8038 // failure condition..
8039 // we have a scenario where updates need confirming.
8040 // eg. if a locking scenario exists..
8041 // we look for { errors : { needs_confirm : true }} in the response.
8043 (typeof(action.result) != 'undefined') &&
8044 (typeof(action.result.errors) != 'undefined') &&
8045 (typeof(action.result.errors.needs_confirm) != 'undefined')
8048 Roo.log("not supported yet");
8051 Roo.MessageBox.confirm(
8052 "Change requires confirmation",
8053 action.result.errorMsg,
8058 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8068 Roo.callback(o.failure, o.scope, [this, action]);
8069 // show an error message if no failed handler is set..
8070 if (!this.hasListener('actionfailed')) {
8071 Roo.log("need to add dialog support");
8073 Roo.MessageBox.alert("Error",
8074 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8075 action.result.errorMsg :
8076 "Saving Failed, please check your entries or try again"
8081 this.fireEvent('actionfailed', this, action);
8086 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8087 * @param {String} id The value to search for
8090 findField : function(id){
8091 var items = this.getItems();
8092 var field = items.get(id);
8094 items.each(function(f){
8095 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8102 return field || null;
8105 * Mark fields in this form invalid in bulk.
8106 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8107 * @return {BasicForm} this
8109 markInvalid : function(errors){
8110 if(errors instanceof Array){
8111 for(var i = 0, len = errors.length; i < len; i++){
8112 var fieldError = errors[i];
8113 var f = this.findField(fieldError.id);
8115 f.markInvalid(fieldError.msg);
8121 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8122 field.markInvalid(errors[id]);
8126 //Roo.each(this.childForms || [], function (f) {
8127 // f.markInvalid(errors);
8134 * Set values for fields in this form in bulk.
8135 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8136 * @return {BasicForm} this
8138 setValues : function(values){
8139 if(values instanceof Array){ // array of objects
8140 for(var i = 0, len = values.length; i < len; i++){
8142 var f = this.findField(v.id);
8144 f.setValue(v.value);
8145 if(this.trackResetOnLoad){
8146 f.originalValue = f.getValue();
8150 }else{ // object hash
8153 if(typeof values[id] != 'function' && (field = this.findField(id))){
8155 if (field.setFromData &&
8157 field.displayField &&
8158 // combos' with local stores can
8159 // be queried via setValue()
8160 // to set their value..
8161 (field.store && !field.store.isLocal)
8165 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8166 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8167 field.setFromData(sd);
8169 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8171 field.setFromData(values);
8174 field.setValue(values[id]);
8178 if(this.trackResetOnLoad){
8179 field.originalValue = field.getValue();
8185 //Roo.each(this.childForms || [], function (f) {
8186 // f.setValues(values);
8193 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8194 * they are returned as an array.
8195 * @param {Boolean} asString
8198 getValues : function(asString){
8199 //if (this.childForms) {
8200 // copy values from the child forms
8201 // Roo.each(this.childForms, function (f) {
8202 // this.setValues(f.getValues());
8208 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8209 if(asString === true){
8212 return Roo.urlDecode(fs);
8216 * Returns the fields in this form as an object with key/value pairs.
8217 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8220 getFieldValues : function(with_hidden)
8222 var items = this.getItems();
8224 items.each(function(f){
8230 var v = f.getValue();
8232 if (f.inputType =='radio') {
8233 if (typeof(ret[f.getName()]) == 'undefined') {
8234 ret[f.getName()] = ''; // empty..
8237 if (!f.el.dom.checked) {
8245 if(f.xtype == 'MoneyField'){
8246 ret[f.currencyName] = f.getCurrency();
8249 // not sure if this supported any more..
8250 if ((typeof(v) == 'object') && f.getRawValue) {
8251 v = f.getRawValue() ; // dates..
8253 // combo boxes where name != hiddenName...
8254 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8255 ret[f.name] = f.getRawValue();
8257 ret[f.getName()] = v;
8264 * Clears all invalid messages in this form.
8265 * @return {BasicForm} this
8267 clearInvalid : function(){
8268 var items = this.getItems();
8270 items.each(function(f){
8279 * @return {BasicForm} this
8282 var items = this.getItems();
8283 items.each(function(f){
8287 Roo.each(this.childForms || [], function (f) {
8295 getItems : function()
8297 var r=new Roo.util.MixedCollection(false, function(o){
8298 return o.id || (o.id = Roo.id());
8300 var iter = function(el) {
8307 Roo.each(el.items,function(e) {
8316 hideFields : function(items)
8318 Roo.each(items, function(i){
8320 var f = this.findField(i);
8326 if(f.xtype == 'DateField'){
8327 f.setVisible(false);
8336 showFields : function(items)
8338 Roo.each(items, function(i){
8340 var f = this.findField(i);
8346 if(f.xtype == 'DateField'){
8358 Roo.apply(Roo.bootstrap.Form, {
8385 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8386 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8387 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8388 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8391 this.maskEl.top.enableDisplayMode("block");
8392 this.maskEl.left.enableDisplayMode("block");
8393 this.maskEl.bottom.enableDisplayMode("block");
8394 this.maskEl.right.enableDisplayMode("block");
8396 this.toolTip = new Roo.bootstrap.Tooltip({
8397 cls : 'roo-form-error-popover',
8399 'left' : ['r-l', [-2,0], 'right'],
8400 'right' : ['l-r', [2,0], 'left'],
8401 'bottom' : ['tl-bl', [0,2], 'top'],
8402 'top' : [ 'bl-tl', [0,-2], 'bottom']
8406 this.toolTip.render(Roo.get(document.body));
8408 this.toolTip.el.enableDisplayMode("block");
8410 Roo.get(document.body).on('click', function(){
8414 Roo.get(document.body).on('touchstart', function(){
8418 this.isApplied = true
8421 mask : function(form, target)
8425 this.target = target;
8427 if(!this.form.errorMask || !target.el){
8431 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8433 Roo.log(scrollable);
8435 var ot = this.target.el.calcOffsetsTo(scrollable);
8437 var scrollTo = ot[1] - this.form.maskOffset;
8439 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8441 scrollable.scrollTo('top', scrollTo);
8443 var box = this.target.el.getBox();
8445 var zIndex = Roo.bootstrap.Modal.zIndex++;
8448 this.maskEl.top.setStyle('position', 'absolute');
8449 this.maskEl.top.setStyle('z-index', zIndex);
8450 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8451 this.maskEl.top.setLeft(0);
8452 this.maskEl.top.setTop(0);
8453 this.maskEl.top.show();
8455 this.maskEl.left.setStyle('position', 'absolute');
8456 this.maskEl.left.setStyle('z-index', zIndex);
8457 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8458 this.maskEl.left.setLeft(0);
8459 this.maskEl.left.setTop(box.y - this.padding);
8460 this.maskEl.left.show();
8462 this.maskEl.bottom.setStyle('position', 'absolute');
8463 this.maskEl.bottom.setStyle('z-index', zIndex);
8464 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8465 this.maskEl.bottom.setLeft(0);
8466 this.maskEl.bottom.setTop(box.bottom + this.padding);
8467 this.maskEl.bottom.show();
8469 this.maskEl.right.setStyle('position', 'absolute');
8470 this.maskEl.right.setStyle('z-index', zIndex);
8471 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8472 this.maskEl.right.setLeft(box.right + this.padding);
8473 this.maskEl.right.setTop(box.y - this.padding);
8474 this.maskEl.right.show();
8476 this.toolTip.bindEl = this.target.el;
8478 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8480 var tip = this.target.blankText;
8482 if(this.target.getValue() !== '' ) {
8484 if (this.target.invalidText.length) {
8485 tip = this.target.invalidText;
8486 } else if (this.target.regexText.length){
8487 tip = this.target.regexText;
8491 this.toolTip.show(tip);
8493 this.intervalID = window.setInterval(function() {
8494 Roo.bootstrap.Form.popover.unmask();
8497 window.onwheel = function(){ return false;};
8499 (function(){ this.isMasked = true; }).defer(500, this);
8505 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8509 this.maskEl.top.setStyle('position', 'absolute');
8510 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8511 this.maskEl.top.hide();
8513 this.maskEl.left.setStyle('position', 'absolute');
8514 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8515 this.maskEl.left.hide();
8517 this.maskEl.bottom.setStyle('position', 'absolute');
8518 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8519 this.maskEl.bottom.hide();
8521 this.maskEl.right.setStyle('position', 'absolute');
8522 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8523 this.maskEl.right.hide();
8525 this.toolTip.hide();
8527 this.toolTip.el.hide();
8529 window.onwheel = function(){ return true;};
8531 if(this.intervalID){
8532 window.clearInterval(this.intervalID);
8533 this.intervalID = false;
8536 this.isMasked = false;
8546 * Ext JS Library 1.1.1
8547 * Copyright(c) 2006-2007, Ext JS, LLC.
8549 * Originally Released Under LGPL - original licence link has changed is not relivant.
8552 * <script type="text/javascript">
8555 * @class Roo.form.VTypes
8556 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8559 Roo.form.VTypes = function(){
8560 // closure these in so they are only created once.
8561 var alpha = /^[a-zA-Z_]+$/;
8562 var alphanum = /^[a-zA-Z0-9_]+$/;
8563 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8564 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8566 // All these messages and functions are configurable
8569 * The function used to validate email addresses
8570 * @param {String} value The email address
8572 'email' : function(v){
8573 return email.test(v);
8576 * The error text to display when the email validation function returns false
8579 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8581 * The keystroke filter mask to be applied on email input
8584 'emailMask' : /[a-z0-9_\.\-@]/i,
8587 * The function used to validate URLs
8588 * @param {String} value The URL
8590 'url' : function(v){
8594 * The error text to display when the url validation function returns false
8597 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8600 * The function used to validate alpha values
8601 * @param {String} value The value
8603 'alpha' : function(v){
8604 return alpha.test(v);
8607 * The error text to display when the alpha validation function returns false
8610 'alphaText' : 'This field should only contain letters and _',
8612 * The keystroke filter mask to be applied on alpha input
8615 'alphaMask' : /[a-z_]/i,
8618 * The function used to validate alphanumeric values
8619 * @param {String} value The value
8621 'alphanum' : function(v){
8622 return alphanum.test(v);
8625 * The error text to display when the alphanumeric validation function returns false
8628 'alphanumText' : 'This field should only contain letters, numbers and _',
8630 * The keystroke filter mask to be applied on alphanumeric input
8633 'alphanumMask' : /[a-z0-9_]/i
8643 * @class Roo.bootstrap.Input
8644 * @extends Roo.bootstrap.Component
8645 * Bootstrap Input class
8646 * @cfg {Boolean} disabled is it disabled
8647 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8648 * @cfg {String} name name of the input
8649 * @cfg {string} fieldLabel - the label associated
8650 * @cfg {string} placeholder - placeholder to put in text.
8651 * @cfg {string} before - input group add on before
8652 * @cfg {string} after - input group add on after
8653 * @cfg {string} size - (lg|sm) or leave empty..
8654 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8655 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8656 * @cfg {Number} md colspan out of 12 for computer-sized screens
8657 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8658 * @cfg {string} value default value of the input
8659 * @cfg {Number} labelWidth set the width of label
8660 * @cfg {Number} labellg set the width of label (1-12)
8661 * @cfg {Number} labelmd set the width of label (1-12)
8662 * @cfg {Number} labelsm set the width of label (1-12)
8663 * @cfg {Number} labelxs set the width of label (1-12)
8664 * @cfg {String} labelAlign (top|left)
8665 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8666 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8667 * @cfg {String} indicatorpos (left|right) default left
8668 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8669 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8671 * @cfg {String} align (left|center|right) Default left
8672 * @cfg {Boolean} forceFeedback (true|false) Default false
8675 * Create a new Input
8676 * @param {Object} config The config object
8679 Roo.bootstrap.Input = function(config){
8681 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8686 * Fires when this field receives input focus.
8687 * @param {Roo.form.Field} this
8692 * Fires when this field loses input focus.
8693 * @param {Roo.form.Field} this
8698 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8699 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8700 * @param {Roo.form.Field} this
8701 * @param {Roo.EventObject} e The event object
8706 * Fires just before the field blurs if the field value has changed.
8707 * @param {Roo.form.Field} this
8708 * @param {Mixed} newValue The new value
8709 * @param {Mixed} oldValue The original value
8714 * Fires after the field has been marked as invalid.
8715 * @param {Roo.form.Field} this
8716 * @param {String} msg The validation message
8721 * Fires after the field has been validated with no errors.
8722 * @param {Roo.form.Field} this
8727 * Fires after the key up
8728 * @param {Roo.form.Field} this
8729 * @param {Roo.EventObject} e The event Object
8735 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8737 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8738 automatic validation (defaults to "keyup").
8740 validationEvent : "keyup",
8742 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8744 validateOnBlur : true,
8746 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8748 validationDelay : 250,
8750 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8752 focusClass : "x-form-focus", // not needed???
8756 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8758 invalidClass : "has-warning",
8761 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8763 validClass : "has-success",
8766 * @cfg {Boolean} hasFeedback (true|false) default true
8771 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8773 invalidFeedbackClass : "glyphicon-warning-sign",
8776 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8778 validFeedbackClass : "glyphicon-ok",
8781 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8783 selectOnFocus : false,
8786 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8790 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8795 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8797 disableKeyFilter : false,
8800 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8804 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8808 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8810 blankText : "Please complete this mandatory field",
8813 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8817 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8819 maxLength : Number.MAX_VALUE,
8821 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8823 minLengthText : "The minimum length for this field is {0}",
8825 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8827 maxLengthText : "The maximum length for this field is {0}",
8831 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8832 * If available, this function will be called only after the basic validators all return true, and will be passed the
8833 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8837 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8838 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8839 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8843 * @cfg {String} regexText -- Depricated - use Invalid Text
8848 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8854 autocomplete: false,
8873 formatedValue : false,
8874 forceFeedback : false,
8876 indicatorpos : 'left',
8886 parentLabelAlign : function()
8889 while (parent.parent()) {
8890 parent = parent.parent();
8891 if (typeof(parent.labelAlign) !='undefined') {
8892 return parent.labelAlign;
8899 getAutoCreate : function()
8901 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8907 if(this.inputType != 'hidden'){
8908 cfg.cls = 'form-group' //input-group
8914 type : this.inputType,
8916 cls : 'form-control',
8917 placeholder : this.placeholder || '',
8918 autocomplete : this.autocomplete || 'new-password'
8921 if(this.capture.length){
8922 input.capture = this.capture;
8925 if(this.accept.length){
8926 input.accept = this.accept + "/*";
8930 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8933 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8934 input.maxLength = this.maxLength;
8937 if (this.disabled) {
8938 input.disabled=true;
8941 if (this.readOnly) {
8942 input.readonly=true;
8946 input.name = this.name;
8950 input.cls += ' input-' + this.size;
8954 ['xs','sm','md','lg'].map(function(size){
8955 if (settings[size]) {
8956 cfg.cls += ' col-' + size + '-' + settings[size];
8960 var inputblock = input;
8964 cls: 'glyphicon form-control-feedback'
8967 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8970 cls : 'has-feedback',
8978 if (this.before || this.after) {
8981 cls : 'input-group',
8985 if (this.before && typeof(this.before) == 'string') {
8987 inputblock.cn.push({
8989 cls : 'roo-input-before input-group-addon',
8993 if (this.before && typeof(this.before) == 'object') {
8994 this.before = Roo.factory(this.before);
8996 inputblock.cn.push({
8998 cls : 'roo-input-before input-group-' +
8999 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9003 inputblock.cn.push(input);
9005 if (this.after && typeof(this.after) == 'string') {
9006 inputblock.cn.push({
9008 cls : 'roo-input-after input-group-addon',
9012 if (this.after && typeof(this.after) == 'object') {
9013 this.after = Roo.factory(this.after);
9015 inputblock.cn.push({
9017 cls : 'roo-input-after input-group-' +
9018 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9022 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9023 inputblock.cls += ' has-feedback';
9024 inputblock.cn.push(feedback);
9028 if (align ==='left' && this.fieldLabel.length) {
9030 cfg.cls += ' roo-form-group-label-left';
9035 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9036 tooltip : 'This field is required'
9041 cls : 'control-label',
9042 html : this.fieldLabel
9053 var labelCfg = cfg.cn[1];
9054 var contentCfg = cfg.cn[2];
9056 if(this.indicatorpos == 'right'){
9061 cls : 'control-label',
9065 html : this.fieldLabel
9069 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9070 tooltip : 'This field is required'
9083 labelCfg = cfg.cn[0];
9084 contentCfg = cfg.cn[1];
9088 if(this.labelWidth > 12){
9089 labelCfg.style = "width: " + this.labelWidth + 'px';
9092 if(this.labelWidth < 13 && this.labelmd == 0){
9093 this.labelmd = this.labelWidth;
9096 if(this.labellg > 0){
9097 labelCfg.cls += ' col-lg-' + this.labellg;
9098 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9101 if(this.labelmd > 0){
9102 labelCfg.cls += ' col-md-' + this.labelmd;
9103 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9106 if(this.labelsm > 0){
9107 labelCfg.cls += ' col-sm-' + this.labelsm;
9108 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9111 if(this.labelxs > 0){
9112 labelCfg.cls += ' col-xs-' + this.labelxs;
9113 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9117 } else if ( this.fieldLabel.length) {
9122 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9123 tooltip : 'This field is required'
9127 //cls : 'input-group-addon',
9128 html : this.fieldLabel
9136 if(this.indicatorpos == 'right'){
9141 //cls : 'input-group-addon',
9142 html : this.fieldLabel
9147 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9148 tooltip : 'This field is required'
9168 if (this.parentType === 'Navbar' && this.parent().bar) {
9169 cfg.cls += ' navbar-form';
9172 if (this.parentType === 'NavGroup') {
9173 cfg.cls += ' navbar-form';
9181 * return the real input element.
9183 inputEl: function ()
9185 return this.el.select('input.form-control',true).first();
9188 tooltipEl : function()
9190 return this.inputEl();
9193 indicatorEl : function()
9195 var indicator = this.el.select('i.roo-required-indicator',true).first();
9205 setDisabled : function(v)
9207 var i = this.inputEl().dom;
9209 i.removeAttribute('disabled');
9213 i.setAttribute('disabled','true');
9215 initEvents : function()
9218 this.inputEl().on("keydown" , this.fireKey, this);
9219 this.inputEl().on("focus", this.onFocus, this);
9220 this.inputEl().on("blur", this.onBlur, this);
9222 this.inputEl().relayEvent('keyup', this);
9224 this.indicator = this.indicatorEl();
9227 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9230 // reference to original value for reset
9231 this.originalValue = this.getValue();
9232 //Roo.form.TextField.superclass.initEvents.call(this);
9233 if(this.validationEvent == 'keyup'){
9234 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9235 this.inputEl().on('keyup', this.filterValidation, this);
9237 else if(this.validationEvent !== false){
9238 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9241 if(this.selectOnFocus){
9242 this.on("focus", this.preFocus, this);
9245 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9246 this.inputEl().on("keypress", this.filterKeys, this);
9248 this.inputEl().relayEvent('keypress', this);
9251 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9252 this.el.on("click", this.autoSize, this);
9255 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9256 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9259 if (typeof(this.before) == 'object') {
9260 this.before.render(this.el.select('.roo-input-before',true).first());
9262 if (typeof(this.after) == 'object') {
9263 this.after.render(this.el.select('.roo-input-after',true).first());
9266 this.inputEl().on('change', this.onChange, this);
9269 filterValidation : function(e){
9270 if(!e.isNavKeyPress()){
9271 this.validationTask.delay(this.validationDelay);
9275 * Validates the field value
9276 * @return {Boolean} True if the value is valid, else false
9278 validate : function(){
9279 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9280 if(this.disabled || this.validateValue(this.getRawValue())){
9291 * Validates a value according to the field's validation rules and marks the field as invalid
9292 * if the validation fails
9293 * @param {Mixed} value The value to validate
9294 * @return {Boolean} True if the value is valid, else false
9296 validateValue : function(value)
9298 if(this.getVisibilityEl().hasClass('hidden')){
9302 if(value.length < 1) { // if it's blank
9303 if(this.allowBlank){
9309 if(value.length < this.minLength){
9312 if(value.length > this.maxLength){
9316 var vt = Roo.form.VTypes;
9317 if(!vt[this.vtype](value, this)){
9321 if(typeof this.validator == "function"){
9322 var msg = this.validator(value);
9326 if (typeof(msg) == 'string') {
9327 this.invalidText = msg;
9331 if(this.regex && !this.regex.test(value)){
9339 fireKey : function(e){
9340 //Roo.log('field ' + e.getKey());
9341 if(e.isNavKeyPress()){
9342 this.fireEvent("specialkey", this, e);
9345 focus : function (selectText){
9347 this.inputEl().focus();
9348 if(selectText === true){
9349 this.inputEl().dom.select();
9355 onFocus : function(){
9356 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9357 // this.el.addClass(this.focusClass);
9360 this.hasFocus = true;
9361 this.startValue = this.getValue();
9362 this.fireEvent("focus", this);
9366 beforeBlur : Roo.emptyFn,
9370 onBlur : function(){
9372 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9373 //this.el.removeClass(this.focusClass);
9375 this.hasFocus = false;
9376 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9379 var v = this.getValue();
9380 if(String(v) !== String(this.startValue)){
9381 this.fireEvent('change', this, v, this.startValue);
9383 this.fireEvent("blur", this);
9386 onChange : function(e)
9388 var v = this.getValue();
9389 if(String(v) !== String(this.startValue)){
9390 this.fireEvent('change', this, v, this.startValue);
9396 * Resets the current field value to the originally loaded value and clears any validation messages
9399 this.setValue(this.originalValue);
9403 * Returns the name of the field
9404 * @return {Mixed} name The name field
9406 getName: function(){
9410 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9411 * @return {Mixed} value The field value
9413 getValue : function(){
9415 var v = this.inputEl().getValue();
9420 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9421 * @return {Mixed} value The field value
9423 getRawValue : function(){
9424 var v = this.inputEl().getValue();
9430 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9431 * @param {Mixed} value The value to set
9433 setRawValue : function(v){
9434 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9437 selectText : function(start, end){
9438 var v = this.getRawValue();
9440 start = start === undefined ? 0 : start;
9441 end = end === undefined ? v.length : end;
9442 var d = this.inputEl().dom;
9443 if(d.setSelectionRange){
9444 d.setSelectionRange(start, end);
9445 }else if(d.createTextRange){
9446 var range = d.createTextRange();
9447 range.moveStart("character", start);
9448 range.moveEnd("character", v.length-end);
9455 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9456 * @param {Mixed} value The value to set
9458 setValue : function(v){
9461 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9467 processValue : function(value){
9468 if(this.stripCharsRe){
9469 var newValue = value.replace(this.stripCharsRe, '');
9470 if(newValue !== value){
9471 this.setRawValue(newValue);
9478 preFocus : function(){
9480 if(this.selectOnFocus){
9481 this.inputEl().dom.select();
9484 filterKeys : function(e){
9486 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9489 var c = e.getCharCode(), cc = String.fromCharCode(c);
9490 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9493 if(!this.maskRe.test(cc)){
9498 * Clear any invalid styles/messages for this field
9500 clearInvalid : function(){
9502 if(!this.el || this.preventMark){ // not rendered
9507 this.el.removeClass(this.invalidClass);
9509 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9511 var feedback = this.el.select('.form-control-feedback', true).first();
9514 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9520 this.indicator.removeClass('visible');
9521 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9524 this.fireEvent('valid', this);
9528 * Mark this field as valid
9530 markValid : function()
9532 if(!this.el || this.preventMark){ // not rendered...
9536 this.el.removeClass([this.invalidClass, this.validClass]);
9538 var feedback = this.el.select('.form-control-feedback', true).first();
9541 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9545 this.indicator.removeClass('visible');
9546 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9553 if(this.allowBlank && !this.getRawValue().length){
9557 this.el.addClass(this.validClass);
9559 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9561 var feedback = this.el.select('.form-control-feedback', true).first();
9564 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9565 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9570 this.fireEvent('valid', this);
9574 * Mark this field as invalid
9575 * @param {String} msg The validation message
9577 markInvalid : function(msg)
9579 if(!this.el || this.preventMark){ // not rendered
9583 this.el.removeClass([this.invalidClass, this.validClass]);
9585 var feedback = this.el.select('.form-control-feedback', true).first();
9588 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9595 if(this.allowBlank && !this.getRawValue().length){
9600 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9601 this.indicator.addClass('visible');
9604 this.el.addClass(this.invalidClass);
9606 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9608 var feedback = this.el.select('.form-control-feedback', true).first();
9611 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9613 if(this.getValue().length || this.forceFeedback){
9614 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9621 this.fireEvent('invalid', this, msg);
9624 SafariOnKeyDown : function(event)
9626 // this is a workaround for a password hang bug on chrome/ webkit.
9627 if (this.inputEl().dom.type != 'password') {
9631 var isSelectAll = false;
9633 if(this.inputEl().dom.selectionEnd > 0){
9634 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9636 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9637 event.preventDefault();
9642 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9644 event.preventDefault();
9645 // this is very hacky as keydown always get's upper case.
9647 var cc = String.fromCharCode(event.getCharCode());
9648 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9652 adjustWidth : function(tag, w){
9653 tag = tag.toLowerCase();
9654 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9655 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9659 if(tag == 'textarea'){
9662 }else if(Roo.isOpera){
9666 if(tag == 'textarea'){
9674 setFieldLabel : function(v)
9681 var ar = this.el.select('label > span',true);
9683 if (ar.elements.length) {
9684 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9685 this.fieldLabel = v;
9689 var br = this.el.select('label',true);
9691 if(br.elements.length) {
9692 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9693 this.fieldLabel = v;
9697 Roo.log('Cannot Found any of label > span || label in input');
9701 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9702 this.fieldLabel = v;
9717 * @class Roo.bootstrap.TextArea
9718 * @extends Roo.bootstrap.Input
9719 * Bootstrap TextArea class
9720 * @cfg {Number} cols Specifies the visible width of a text area
9721 * @cfg {Number} rows Specifies the visible number of lines in a text area
9722 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9723 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9724 * @cfg {string} html text
9727 * Create a new TextArea
9728 * @param {Object} config The config object
9731 Roo.bootstrap.TextArea = function(config){
9732 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9736 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9746 getAutoCreate : function(){
9748 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9754 if(this.inputType != 'hidden'){
9755 cfg.cls = 'form-group' //input-group
9763 value : this.value || '',
9764 html: this.html || '',
9765 cls : 'form-control',
9766 placeholder : this.placeholder || ''
9770 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9771 input.maxLength = this.maxLength;
9775 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9779 input.cols = this.cols;
9782 if (this.readOnly) {
9783 input.readonly = true;
9787 input.name = this.name;
9791 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9795 ['xs','sm','md','lg'].map(function(size){
9796 if (settings[size]) {
9797 cfg.cls += ' col-' + size + '-' + settings[size];
9801 var inputblock = input;
9803 if(this.hasFeedback && !this.allowBlank){
9807 cls: 'glyphicon form-control-feedback'
9811 cls : 'has-feedback',
9820 if (this.before || this.after) {
9823 cls : 'input-group',
9827 inputblock.cn.push({
9829 cls : 'input-group-addon',
9834 inputblock.cn.push(input);
9836 if(this.hasFeedback && !this.allowBlank){
9837 inputblock.cls += ' has-feedback';
9838 inputblock.cn.push(feedback);
9842 inputblock.cn.push({
9844 cls : 'input-group-addon',
9851 if (align ==='left' && this.fieldLabel.length) {
9856 cls : 'control-label',
9857 html : this.fieldLabel
9868 if(this.labelWidth > 12){
9869 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9872 if(this.labelWidth < 13 && this.labelmd == 0){
9873 this.labelmd = this.labelWidth;
9876 if(this.labellg > 0){
9877 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9878 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9881 if(this.labelmd > 0){
9882 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9883 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9886 if(this.labelsm > 0){
9887 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9888 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9891 if(this.labelxs > 0){
9892 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9893 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9896 } else if ( this.fieldLabel.length) {
9901 //cls : 'input-group-addon',
9902 html : this.fieldLabel
9920 if (this.disabled) {
9921 input.disabled=true;
9928 * return the real textarea element.
9930 inputEl: function ()
9932 return this.el.select('textarea.form-control',true).first();
9936 * Clear any invalid styles/messages for this field
9938 clearInvalid : function()
9941 if(!this.el || this.preventMark){ // not rendered
9945 var label = this.el.select('label', true).first();
9946 var icon = this.el.select('i.fa-star', true).first();
9952 this.el.removeClass(this.invalidClass);
9954 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9956 var feedback = this.el.select('.form-control-feedback', true).first();
9959 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9964 this.fireEvent('valid', this);
9968 * Mark this field as valid
9970 markValid : function()
9972 if(!this.el || this.preventMark){ // not rendered
9976 this.el.removeClass([this.invalidClass, this.validClass]);
9978 var feedback = this.el.select('.form-control-feedback', true).first();
9981 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9984 if(this.disabled || this.allowBlank){
9988 var label = this.el.select('label', true).first();
9989 var icon = this.el.select('i.fa-star', true).first();
9995 this.el.addClass(this.validClass);
9997 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9999 var feedback = this.el.select('.form-control-feedback', true).first();
10002 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10003 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10008 this.fireEvent('valid', this);
10012 * Mark this field as invalid
10013 * @param {String} msg The validation message
10015 markInvalid : function(msg)
10017 if(!this.el || this.preventMark){ // not rendered
10021 this.el.removeClass([this.invalidClass, this.validClass]);
10023 var feedback = this.el.select('.form-control-feedback', true).first();
10026 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10029 if(this.disabled || this.allowBlank){
10033 var label = this.el.select('label', true).first();
10034 var icon = this.el.select('i.fa-star', true).first();
10036 if(!this.getValue().length && label && !icon){
10037 this.el.createChild({
10039 cls : 'text-danger fa fa-lg fa-star',
10040 tooltip : 'This field is required',
10041 style : 'margin-right:5px;'
10045 this.el.addClass(this.invalidClass);
10047 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10049 var feedback = this.el.select('.form-control-feedback', true).first();
10052 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10054 if(this.getValue().length || this.forceFeedback){
10055 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10062 this.fireEvent('invalid', this, msg);
10070 * trigger field - base class for combo..
10075 * @class Roo.bootstrap.TriggerField
10076 * @extends Roo.bootstrap.Input
10077 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10078 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10079 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10080 * for which you can provide a custom implementation. For example:
10082 var trigger = new Roo.bootstrap.TriggerField();
10083 trigger.onTriggerClick = myTriggerFn;
10084 trigger.applyTo('my-field');
10087 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10088 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10089 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10090 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10091 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10094 * Create a new TriggerField.
10095 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10096 * to the base TextField)
10098 Roo.bootstrap.TriggerField = function(config){
10099 this.mimicing = false;
10100 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10103 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10105 * @cfg {String} triggerClass A CSS class to apply to the trigger
10108 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10113 * @cfg {Boolean} removable (true|false) special filter default false
10117 /** @cfg {Boolean} grow @hide */
10118 /** @cfg {Number} growMin @hide */
10119 /** @cfg {Number} growMax @hide */
10125 autoSize: Roo.emptyFn,
10129 deferHeight : true,
10132 actionMode : 'wrap',
10137 getAutoCreate : function(){
10139 var align = this.labelAlign || this.parentLabelAlign();
10144 cls: 'form-group' //input-group
10151 type : this.inputType,
10152 cls : 'form-control',
10153 autocomplete: 'new-password',
10154 placeholder : this.placeholder || ''
10158 input.name = this.name;
10161 input.cls += ' input-' + this.size;
10164 if (this.disabled) {
10165 input.disabled=true;
10168 var inputblock = input;
10170 if(this.hasFeedback && !this.allowBlank){
10174 cls: 'glyphicon form-control-feedback'
10177 if(this.removable && !this.editable && !this.tickable){
10179 cls : 'has-feedback',
10185 cls : 'roo-combo-removable-btn close'
10192 cls : 'has-feedback',
10201 if(this.removable && !this.editable && !this.tickable){
10203 cls : 'roo-removable',
10209 cls : 'roo-combo-removable-btn close'
10216 if (this.before || this.after) {
10219 cls : 'input-group',
10223 inputblock.cn.push({
10225 cls : 'input-group-addon',
10230 inputblock.cn.push(input);
10232 if(this.hasFeedback && !this.allowBlank){
10233 inputblock.cls += ' has-feedback';
10234 inputblock.cn.push(feedback);
10238 inputblock.cn.push({
10240 cls : 'input-group-addon',
10253 cls: 'form-hidden-field'
10267 cls: 'form-hidden-field'
10271 cls: 'roo-select2-choices',
10275 cls: 'roo-select2-search-field',
10288 cls: 'roo-select2-container input-group',
10293 // cls: 'typeahead typeahead-long dropdown-menu',
10294 // style: 'display:none'
10299 if(!this.multiple && this.showToggleBtn){
10305 if (this.caret != false) {
10308 cls: 'fa fa-' + this.caret
10315 cls : 'input-group-addon btn dropdown-toggle',
10320 cls: 'combobox-clear',
10334 combobox.cls += ' roo-select2-container-multi';
10337 if (align ==='left' && this.fieldLabel.length) {
10339 cfg.cls += ' roo-form-group-label-left';
10344 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10345 tooltip : 'This field is required'
10350 cls : 'control-label',
10351 html : this.fieldLabel
10363 var labelCfg = cfg.cn[1];
10364 var contentCfg = cfg.cn[2];
10366 if(this.indicatorpos == 'right'){
10371 cls : 'control-label',
10375 html : this.fieldLabel
10379 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10380 tooltip : 'This field is required'
10393 labelCfg = cfg.cn[0];
10394 contentCfg = cfg.cn[1];
10397 if(this.labelWidth > 12){
10398 labelCfg.style = "width: " + this.labelWidth + 'px';
10401 if(this.labelWidth < 13 && this.labelmd == 0){
10402 this.labelmd = this.labelWidth;
10405 if(this.labellg > 0){
10406 labelCfg.cls += ' col-lg-' + this.labellg;
10407 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10410 if(this.labelmd > 0){
10411 labelCfg.cls += ' col-md-' + this.labelmd;
10412 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10415 if(this.labelsm > 0){
10416 labelCfg.cls += ' col-sm-' + this.labelsm;
10417 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10420 if(this.labelxs > 0){
10421 labelCfg.cls += ' col-xs-' + this.labelxs;
10422 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10425 } else if ( this.fieldLabel.length) {
10426 // Roo.log(" label");
10430 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10431 tooltip : 'This field is required'
10435 //cls : 'input-group-addon',
10436 html : this.fieldLabel
10444 if(this.indicatorpos == 'right'){
10452 html : this.fieldLabel
10456 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10457 tooltip : 'This field is required'
10470 // Roo.log(" no label && no align");
10477 ['xs','sm','md','lg'].map(function(size){
10478 if (settings[size]) {
10479 cfg.cls += ' col-' + size + '-' + settings[size];
10490 onResize : function(w, h){
10491 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10492 // if(typeof w == 'number'){
10493 // var x = w - this.trigger.getWidth();
10494 // this.inputEl().setWidth(this.adjustWidth('input', x));
10495 // this.trigger.setStyle('left', x+'px');
10500 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10503 getResizeEl : function(){
10504 return this.inputEl();
10508 getPositionEl : function(){
10509 return this.inputEl();
10513 alignErrorIcon : function(){
10514 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10518 initEvents : function(){
10522 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10523 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10524 if(!this.multiple && this.showToggleBtn){
10525 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10526 if(this.hideTrigger){
10527 this.trigger.setDisplayed(false);
10529 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10533 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10536 if(this.removable && !this.editable && !this.tickable){
10537 var close = this.closeTriggerEl();
10540 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10541 close.on('click', this.removeBtnClick, this, close);
10545 //this.trigger.addClassOnOver('x-form-trigger-over');
10546 //this.trigger.addClassOnClick('x-form-trigger-click');
10549 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10553 closeTriggerEl : function()
10555 var close = this.el.select('.roo-combo-removable-btn', true).first();
10556 return close ? close : false;
10559 removeBtnClick : function(e, h, el)
10561 e.preventDefault();
10563 if(this.fireEvent("remove", this) !== false){
10565 this.fireEvent("afterremove", this)
10569 createList : function()
10571 this.list = Roo.get(document.body).createChild({
10573 cls: 'typeahead typeahead-long dropdown-menu',
10574 style: 'display:none'
10577 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10582 initTrigger : function(){
10587 onDestroy : function(){
10589 this.trigger.removeAllListeners();
10590 // this.trigger.remove();
10593 // this.wrap.remove();
10595 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10599 onFocus : function(){
10600 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10602 if(!this.mimicing){
10603 this.wrap.addClass('x-trigger-wrap-focus');
10604 this.mimicing = true;
10605 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10606 if(this.monitorTab){
10607 this.el.on("keydown", this.checkTab, this);
10614 checkTab : function(e){
10615 if(e.getKey() == e.TAB){
10616 this.triggerBlur();
10621 onBlur : function(){
10626 mimicBlur : function(e, t){
10628 if(!this.wrap.contains(t) && this.validateBlur()){
10629 this.triggerBlur();
10635 triggerBlur : function(){
10636 this.mimicing = false;
10637 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10638 if(this.monitorTab){
10639 this.el.un("keydown", this.checkTab, this);
10641 //this.wrap.removeClass('x-trigger-wrap-focus');
10642 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10646 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10647 validateBlur : function(e, t){
10652 onDisable : function(){
10653 this.inputEl().dom.disabled = true;
10654 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10656 // this.wrap.addClass('x-item-disabled');
10661 onEnable : function(){
10662 this.inputEl().dom.disabled = false;
10663 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10665 // this.el.removeClass('x-item-disabled');
10670 onShow : function(){
10671 var ae = this.getActionEl();
10674 ae.dom.style.display = '';
10675 ae.dom.style.visibility = 'visible';
10681 onHide : function(){
10682 var ae = this.getActionEl();
10683 ae.dom.style.display = 'none';
10687 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10688 * by an implementing function.
10690 * @param {EventObject} e
10692 onTriggerClick : Roo.emptyFn
10696 * Ext JS Library 1.1.1
10697 * Copyright(c) 2006-2007, Ext JS, LLC.
10699 * Originally Released Under LGPL - original licence link has changed is not relivant.
10702 * <script type="text/javascript">
10707 * @class Roo.data.SortTypes
10709 * Defines the default sorting (casting?) comparison functions used when sorting data.
10711 Roo.data.SortTypes = {
10713 * Default sort that does nothing
10714 * @param {Mixed} s The value being converted
10715 * @return {Mixed} The comparison value
10717 none : function(s){
10722 * The regular expression used to strip tags
10726 stripTagsRE : /<\/?[^>]+>/gi,
10729 * Strips all HTML tags to sort on text only
10730 * @param {Mixed} s The value being converted
10731 * @return {String} The comparison value
10733 asText : function(s){
10734 return String(s).replace(this.stripTagsRE, "");
10738 * Strips all HTML tags to sort on text only - Case insensitive
10739 * @param {Mixed} s The value being converted
10740 * @return {String} The comparison value
10742 asUCText : function(s){
10743 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10747 * Case insensitive string
10748 * @param {Mixed} s The value being converted
10749 * @return {String} The comparison value
10751 asUCString : function(s) {
10752 return String(s).toUpperCase();
10757 * @param {Mixed} s The value being converted
10758 * @return {Number} The comparison value
10760 asDate : function(s) {
10764 if(s instanceof Date){
10765 return s.getTime();
10767 return Date.parse(String(s));
10772 * @param {Mixed} s The value being converted
10773 * @return {Float} The comparison value
10775 asFloat : function(s) {
10776 var val = parseFloat(String(s).replace(/,/g, ""));
10785 * @param {Mixed} s The value being converted
10786 * @return {Number} The comparison value
10788 asInt : function(s) {
10789 var val = parseInt(String(s).replace(/,/g, ""));
10797 * Ext JS Library 1.1.1
10798 * Copyright(c) 2006-2007, Ext JS, LLC.
10800 * Originally Released Under LGPL - original licence link has changed is not relivant.
10803 * <script type="text/javascript">
10807 * @class Roo.data.Record
10808 * Instances of this class encapsulate both record <em>definition</em> information, and record
10809 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10810 * to access Records cached in an {@link Roo.data.Store} object.<br>
10812 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10813 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10816 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10818 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10819 * {@link #create}. The parameters are the same.
10820 * @param {Array} data An associative Array of data values keyed by the field name.
10821 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10822 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10823 * not specified an integer id is generated.
10825 Roo.data.Record = function(data, id){
10826 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10831 * Generate a constructor for a specific record layout.
10832 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10833 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10834 * Each field definition object may contain the following properties: <ul>
10835 * <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,
10836 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10837 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10838 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10839 * is being used, then this is a string containing the javascript expression to reference the data relative to
10840 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10841 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10842 * this may be omitted.</p></li>
10843 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10844 * <ul><li>auto (Default, implies no conversion)</li>
10849 * <li>date</li></ul></p></li>
10850 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10851 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10852 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10853 * by the Reader into an object that will be stored in the Record. It is passed the
10854 * following parameters:<ul>
10855 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10857 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10859 * <br>usage:<br><pre><code>
10860 var TopicRecord = Roo.data.Record.create(
10861 {name: 'title', mapping: 'topic_title'},
10862 {name: 'author', mapping: 'username'},
10863 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10864 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10865 {name: 'lastPoster', mapping: 'user2'},
10866 {name: 'excerpt', mapping: 'post_text'}
10869 var myNewRecord = new TopicRecord({
10870 title: 'Do my job please',
10873 lastPost: new Date(),
10874 lastPoster: 'Animal',
10875 excerpt: 'No way dude!'
10877 myStore.add(myNewRecord);
10882 Roo.data.Record.create = function(o){
10883 var f = function(){
10884 f.superclass.constructor.apply(this, arguments);
10886 Roo.extend(f, Roo.data.Record);
10887 var p = f.prototype;
10888 p.fields = new Roo.util.MixedCollection(false, function(field){
10891 for(var i = 0, len = o.length; i < len; i++){
10892 p.fields.add(new Roo.data.Field(o[i]));
10894 f.getField = function(name){
10895 return p.fields.get(name);
10900 Roo.data.Record.AUTO_ID = 1000;
10901 Roo.data.Record.EDIT = 'edit';
10902 Roo.data.Record.REJECT = 'reject';
10903 Roo.data.Record.COMMIT = 'commit';
10905 Roo.data.Record.prototype = {
10907 * Readonly flag - true if this record has been modified.
10916 join : function(store){
10917 this.store = store;
10921 * Set the named field to the specified value.
10922 * @param {String} name The name of the field to set.
10923 * @param {Object} value The value to set the field to.
10925 set : function(name, value){
10926 if(this.data[name] == value){
10930 if(!this.modified){
10931 this.modified = {};
10933 if(typeof this.modified[name] == 'undefined'){
10934 this.modified[name] = this.data[name];
10936 this.data[name] = value;
10937 if(!this.editing && this.store){
10938 this.store.afterEdit(this);
10943 * Get the value of the named field.
10944 * @param {String} name The name of the field to get the value of.
10945 * @return {Object} The value of the field.
10947 get : function(name){
10948 return this.data[name];
10952 beginEdit : function(){
10953 this.editing = true;
10954 this.modified = {};
10958 cancelEdit : function(){
10959 this.editing = false;
10960 delete this.modified;
10964 endEdit : function(){
10965 this.editing = false;
10966 if(this.dirty && this.store){
10967 this.store.afterEdit(this);
10972 * Usually called by the {@link Roo.data.Store} which owns the Record.
10973 * Rejects all changes made to the Record since either creation, or the last commit operation.
10974 * Modified fields are reverted to their original values.
10976 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10977 * of reject operations.
10979 reject : function(){
10980 var m = this.modified;
10982 if(typeof m[n] != "function"){
10983 this.data[n] = m[n];
10986 this.dirty = false;
10987 delete this.modified;
10988 this.editing = false;
10990 this.store.afterReject(this);
10995 * Usually called by the {@link Roo.data.Store} which owns the Record.
10996 * Commits all changes made to the Record since either creation, or the last commit operation.
10998 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10999 * of commit operations.
11001 commit : function(){
11002 this.dirty = false;
11003 delete this.modified;
11004 this.editing = false;
11006 this.store.afterCommit(this);
11011 hasError : function(){
11012 return this.error != null;
11016 clearError : function(){
11021 * Creates a copy of this record.
11022 * @param {String} id (optional) A new record id if you don't want to use this record's id
11025 copy : function(newId) {
11026 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11030 * Ext JS Library 1.1.1
11031 * Copyright(c) 2006-2007, Ext JS, LLC.
11033 * Originally Released Under LGPL - original licence link has changed is not relivant.
11036 * <script type="text/javascript">
11042 * @class Roo.data.Store
11043 * @extends Roo.util.Observable
11044 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11045 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11047 * 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
11048 * has no knowledge of the format of the data returned by the Proxy.<br>
11050 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11051 * instances from the data object. These records are cached and made available through accessor functions.
11053 * Creates a new Store.
11054 * @param {Object} config A config object containing the objects needed for the Store to access data,
11055 * and read the data into Records.
11057 Roo.data.Store = function(config){
11058 this.data = new Roo.util.MixedCollection(false);
11059 this.data.getKey = function(o){
11062 this.baseParams = {};
11064 this.paramNames = {
11069 "multisort" : "_multisort"
11072 if(config && config.data){
11073 this.inlineData = config.data;
11074 delete config.data;
11077 Roo.apply(this, config);
11079 if(this.reader){ // reader passed
11080 this.reader = Roo.factory(this.reader, Roo.data);
11081 this.reader.xmodule = this.xmodule || false;
11082 if(!this.recordType){
11083 this.recordType = this.reader.recordType;
11085 if(this.reader.onMetaChange){
11086 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11090 if(this.recordType){
11091 this.fields = this.recordType.prototype.fields;
11093 this.modified = [];
11097 * @event datachanged
11098 * Fires when the data cache has changed, and a widget which is using this Store
11099 * as a Record cache should refresh its view.
11100 * @param {Store} this
11102 datachanged : true,
11104 * @event metachange
11105 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11106 * @param {Store} this
11107 * @param {Object} meta The JSON metadata
11112 * Fires when Records have been added to the Store
11113 * @param {Store} this
11114 * @param {Roo.data.Record[]} records The array of Records added
11115 * @param {Number} index The index at which the record(s) were added
11120 * Fires when a Record has been removed from the Store
11121 * @param {Store} this
11122 * @param {Roo.data.Record} record The Record that was removed
11123 * @param {Number} index The index at which the record was removed
11128 * Fires when a Record has been updated
11129 * @param {Store} this
11130 * @param {Roo.data.Record} record The Record that was updated
11131 * @param {String} operation The update operation being performed. Value may be one of:
11133 Roo.data.Record.EDIT
11134 Roo.data.Record.REJECT
11135 Roo.data.Record.COMMIT
11141 * Fires when the data cache has been cleared.
11142 * @param {Store} this
11146 * @event beforeload
11147 * Fires before a request is made for a new data object. If the beforeload handler returns false
11148 * the load action will be canceled.
11149 * @param {Store} this
11150 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11154 * @event beforeloadadd
11155 * Fires after a new set of Records has been loaded.
11156 * @param {Store} this
11157 * @param {Roo.data.Record[]} records The Records that were loaded
11158 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11160 beforeloadadd : true,
11163 * Fires after a new set of Records has been loaded, before they are added to the store.
11164 * @param {Store} this
11165 * @param {Roo.data.Record[]} records The Records that were loaded
11166 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11167 * @params {Object} return from reader
11171 * @event loadexception
11172 * Fires if an exception occurs in the Proxy during loading.
11173 * Called with the signature of the Proxy's "loadexception" event.
11174 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11177 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11178 * @param {Object} load options
11179 * @param {Object} jsonData from your request (normally this contains the Exception)
11181 loadexception : true
11185 this.proxy = Roo.factory(this.proxy, Roo.data);
11186 this.proxy.xmodule = this.xmodule || false;
11187 this.relayEvents(this.proxy, ["loadexception"]);
11189 this.sortToggle = {};
11190 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11192 Roo.data.Store.superclass.constructor.call(this);
11194 if(this.inlineData){
11195 this.loadData(this.inlineData);
11196 delete this.inlineData;
11200 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11202 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11203 * without a remote query - used by combo/forms at present.
11207 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11210 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11213 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11214 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11217 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11218 * on any HTTP request
11221 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11224 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11228 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11229 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11231 remoteSort : false,
11234 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11235 * loaded or when a record is removed. (defaults to false).
11237 pruneModifiedRecords : false,
11240 lastOptions : null,
11243 * Add Records to the Store and fires the add event.
11244 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11246 add : function(records){
11247 records = [].concat(records);
11248 for(var i = 0, len = records.length; i < len; i++){
11249 records[i].join(this);
11251 var index = this.data.length;
11252 this.data.addAll(records);
11253 this.fireEvent("add", this, records, index);
11257 * Remove a Record from the Store and fires the remove event.
11258 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11260 remove : function(record){
11261 var index = this.data.indexOf(record);
11262 this.data.removeAt(index);
11264 if(this.pruneModifiedRecords){
11265 this.modified.remove(record);
11267 this.fireEvent("remove", this, record, index);
11271 * Remove all Records from the Store and fires the clear event.
11273 removeAll : function(){
11275 if(this.pruneModifiedRecords){
11276 this.modified = [];
11278 this.fireEvent("clear", this);
11282 * Inserts Records to the Store at the given index and fires the add event.
11283 * @param {Number} index The start index at which to insert the passed Records.
11284 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11286 insert : function(index, records){
11287 records = [].concat(records);
11288 for(var i = 0, len = records.length; i < len; i++){
11289 this.data.insert(index, records[i]);
11290 records[i].join(this);
11292 this.fireEvent("add", this, records, index);
11296 * Get the index within the cache of the passed Record.
11297 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11298 * @return {Number} The index of the passed Record. Returns -1 if not found.
11300 indexOf : function(record){
11301 return this.data.indexOf(record);
11305 * Get the index within the cache of the Record with the passed id.
11306 * @param {String} id The id of the Record to find.
11307 * @return {Number} The index of the Record. Returns -1 if not found.
11309 indexOfId : function(id){
11310 return this.data.indexOfKey(id);
11314 * Get the Record with the specified id.
11315 * @param {String} id The id of the Record to find.
11316 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11318 getById : function(id){
11319 return this.data.key(id);
11323 * Get the Record at the specified index.
11324 * @param {Number} index The index of the Record to find.
11325 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11327 getAt : function(index){
11328 return this.data.itemAt(index);
11332 * Returns a range of Records between specified indices.
11333 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11334 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11335 * @return {Roo.data.Record[]} An array of Records
11337 getRange : function(start, end){
11338 return this.data.getRange(start, end);
11342 storeOptions : function(o){
11343 o = Roo.apply({}, o);
11346 this.lastOptions = o;
11350 * Loads the Record cache from the configured Proxy using the configured Reader.
11352 * If using remote paging, then the first load call must specify the <em>start</em>
11353 * and <em>limit</em> properties in the options.params property to establish the initial
11354 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11356 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11357 * and this call will return before the new data has been loaded. Perform any post-processing
11358 * in a callback function, or in a "load" event handler.</strong>
11360 * @param {Object} options An object containing properties which control loading options:<ul>
11361 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11362 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11363 * passed the following arguments:<ul>
11364 * <li>r : Roo.data.Record[]</li>
11365 * <li>options: Options object from the load call</li>
11366 * <li>success: Boolean success indicator</li></ul></li>
11367 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11368 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11371 load : function(options){
11372 options = options || {};
11373 if(this.fireEvent("beforeload", this, options) !== false){
11374 this.storeOptions(options);
11375 var p = Roo.apply(options.params || {}, this.baseParams);
11376 // if meta was not loaded from remote source.. try requesting it.
11377 if (!this.reader.metaFromRemote) {
11378 p._requestMeta = 1;
11380 if(this.sortInfo && this.remoteSort){
11381 var pn = this.paramNames;
11382 p[pn["sort"]] = this.sortInfo.field;
11383 p[pn["dir"]] = this.sortInfo.direction;
11385 if (this.multiSort) {
11386 var pn = this.paramNames;
11387 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11390 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11395 * Reloads the Record cache from the configured Proxy using the configured Reader and
11396 * the options from the last load operation performed.
11397 * @param {Object} options (optional) An object containing properties which may override the options
11398 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11399 * the most recently used options are reused).
11401 reload : function(options){
11402 this.load(Roo.applyIf(options||{}, this.lastOptions));
11406 // Called as a callback by the Reader during a load operation.
11407 loadRecords : function(o, options, success){
11408 if(!o || success === false){
11409 if(success !== false){
11410 this.fireEvent("load", this, [], options, o);
11412 if(options.callback){
11413 options.callback.call(options.scope || this, [], options, false);
11417 // if data returned failure - throw an exception.
11418 if (o.success === false) {
11419 // show a message if no listener is registered.
11420 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11421 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11423 // loadmask wil be hooked into this..
11424 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11427 var r = o.records, t = o.totalRecords || r.length;
11429 this.fireEvent("beforeloadadd", this, r, options, o);
11431 if(!options || options.add !== true){
11432 if(this.pruneModifiedRecords){
11433 this.modified = [];
11435 for(var i = 0, len = r.length; i < len; i++){
11439 this.data = this.snapshot;
11440 delete this.snapshot;
11443 this.data.addAll(r);
11444 this.totalLength = t;
11446 this.fireEvent("datachanged", this);
11448 this.totalLength = Math.max(t, this.data.length+r.length);
11452 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11454 var e = new Roo.data.Record({});
11456 e.set(this.parent.displayField, this.parent.emptyTitle);
11457 e.set(this.parent.valueField, '');
11462 this.fireEvent("load", this, r, options, o);
11463 if(options.callback){
11464 options.callback.call(options.scope || this, r, options, true);
11470 * Loads data from a passed data block. A Reader which understands the format of the data
11471 * must have been configured in the constructor.
11472 * @param {Object} data The data block from which to read the Records. The format of the data expected
11473 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11474 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11476 loadData : function(o, append){
11477 var r = this.reader.readRecords(o);
11478 this.loadRecords(r, {add: append}, true);
11482 * Gets the number of cached records.
11484 * <em>If using paging, this may not be the total size of the dataset. If the data object
11485 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11486 * the data set size</em>
11488 getCount : function(){
11489 return this.data.length || 0;
11493 * Gets the total number of records in the dataset as returned by the server.
11495 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11496 * the dataset size</em>
11498 getTotalCount : function(){
11499 return this.totalLength || 0;
11503 * Returns the sort state of the Store as an object with two properties:
11505 field {String} The name of the field by which the Records are sorted
11506 direction {String} The sort order, "ASC" or "DESC"
11509 getSortState : function(){
11510 return this.sortInfo;
11514 applySort : function(){
11515 if(this.sortInfo && !this.remoteSort){
11516 var s = this.sortInfo, f = s.field;
11517 var st = this.fields.get(f).sortType;
11518 var fn = function(r1, r2){
11519 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11520 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11522 this.data.sort(s.direction, fn);
11523 if(this.snapshot && this.snapshot != this.data){
11524 this.snapshot.sort(s.direction, fn);
11530 * Sets the default sort column and order to be used by the next load operation.
11531 * @param {String} fieldName The name of the field to sort by.
11532 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11534 setDefaultSort : function(field, dir){
11535 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11539 * Sort the Records.
11540 * If remote sorting is used, the sort is performed on the server, and the cache is
11541 * reloaded. If local sorting is used, the cache is sorted internally.
11542 * @param {String} fieldName The name of the field to sort by.
11543 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11545 sort : function(fieldName, dir){
11546 var f = this.fields.get(fieldName);
11548 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11550 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11551 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11556 this.sortToggle[f.name] = dir;
11557 this.sortInfo = {field: f.name, direction: dir};
11558 if(!this.remoteSort){
11560 this.fireEvent("datachanged", this);
11562 this.load(this.lastOptions);
11567 * Calls the specified function for each of the Records in the cache.
11568 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11569 * Returning <em>false</em> aborts and exits the iteration.
11570 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11572 each : function(fn, scope){
11573 this.data.each(fn, scope);
11577 * Gets all records modified since the last commit. Modified records are persisted across load operations
11578 * (e.g., during paging).
11579 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11581 getModifiedRecords : function(){
11582 return this.modified;
11586 createFilterFn : function(property, value, anyMatch){
11587 if(!value.exec){ // not a regex
11588 value = String(value);
11589 if(value.length == 0){
11592 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11594 return function(r){
11595 return value.test(r.data[property]);
11600 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11601 * @param {String} property A field on your records
11602 * @param {Number} start The record index to start at (defaults to 0)
11603 * @param {Number} end The last record index to include (defaults to length - 1)
11604 * @return {Number} The sum
11606 sum : function(property, start, end){
11607 var rs = this.data.items, v = 0;
11608 start = start || 0;
11609 end = (end || end === 0) ? end : rs.length-1;
11611 for(var i = start; i <= end; i++){
11612 v += (rs[i].data[property] || 0);
11618 * Filter the records by a specified property.
11619 * @param {String} field A field on your records
11620 * @param {String/RegExp} value Either a string that the field
11621 * should start with or a RegExp to test against the field
11622 * @param {Boolean} anyMatch True to match any part not just the beginning
11624 filter : function(property, value, anyMatch){
11625 var fn = this.createFilterFn(property, value, anyMatch);
11626 return fn ? this.filterBy(fn) : this.clearFilter();
11630 * Filter by a function. The specified function will be called with each
11631 * record in this data source. If the function returns true the record is included,
11632 * otherwise it is filtered.
11633 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11634 * @param {Object} scope (optional) The scope of the function (defaults to this)
11636 filterBy : function(fn, scope){
11637 this.snapshot = this.snapshot || this.data;
11638 this.data = this.queryBy(fn, scope||this);
11639 this.fireEvent("datachanged", this);
11643 * Query the records by a specified property.
11644 * @param {String} field A field on your records
11645 * @param {String/RegExp} value Either a string that the field
11646 * should start with or a RegExp to test against the field
11647 * @param {Boolean} anyMatch True to match any part not just the beginning
11648 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11650 query : function(property, value, anyMatch){
11651 var fn = this.createFilterFn(property, value, anyMatch);
11652 return fn ? this.queryBy(fn) : this.data.clone();
11656 * Query by a function. The specified function will be called with each
11657 * record in this data source. If the function returns true the record is included
11659 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11660 * @param {Object} scope (optional) The scope of the function (defaults to this)
11661 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11663 queryBy : function(fn, scope){
11664 var data = this.snapshot || this.data;
11665 return data.filterBy(fn, scope||this);
11669 * Collects unique values for a particular dataIndex from this store.
11670 * @param {String} dataIndex The property to collect
11671 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11672 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11673 * @return {Array} An array of the unique values
11675 collect : function(dataIndex, allowNull, bypassFilter){
11676 var d = (bypassFilter === true && this.snapshot) ?
11677 this.snapshot.items : this.data.items;
11678 var v, sv, r = [], l = {};
11679 for(var i = 0, len = d.length; i < len; i++){
11680 v = d[i].data[dataIndex];
11682 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11691 * Revert to a view of the Record cache with no filtering applied.
11692 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11694 clearFilter : function(suppressEvent){
11695 if(this.snapshot && this.snapshot != this.data){
11696 this.data = this.snapshot;
11697 delete this.snapshot;
11698 if(suppressEvent !== true){
11699 this.fireEvent("datachanged", this);
11705 afterEdit : function(record){
11706 if(this.modified.indexOf(record) == -1){
11707 this.modified.push(record);
11709 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11713 afterReject : function(record){
11714 this.modified.remove(record);
11715 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11719 afterCommit : function(record){
11720 this.modified.remove(record);
11721 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11725 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11726 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11728 commitChanges : function(){
11729 var m = this.modified.slice(0);
11730 this.modified = [];
11731 for(var i = 0, len = m.length; i < len; i++){
11737 * Cancel outstanding changes on all changed records.
11739 rejectChanges : function(){
11740 var m = this.modified.slice(0);
11741 this.modified = [];
11742 for(var i = 0, len = m.length; i < len; i++){
11747 onMetaChange : function(meta, rtype, o){
11748 this.recordType = rtype;
11749 this.fields = rtype.prototype.fields;
11750 delete this.snapshot;
11751 this.sortInfo = meta.sortInfo || this.sortInfo;
11752 this.modified = [];
11753 this.fireEvent('metachange', this, this.reader.meta);
11756 moveIndex : function(data, type)
11758 var index = this.indexOf(data);
11760 var newIndex = index + type;
11764 this.insert(newIndex, data);
11769 * Ext JS Library 1.1.1
11770 * Copyright(c) 2006-2007, Ext JS, LLC.
11772 * Originally Released Under LGPL - original licence link has changed is not relivant.
11775 * <script type="text/javascript">
11779 * @class Roo.data.SimpleStore
11780 * @extends Roo.data.Store
11781 * Small helper class to make creating Stores from Array data easier.
11782 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11783 * @cfg {Array} fields An array of field definition objects, or field name strings.
11784 * @cfg {Array} data The multi-dimensional array of data
11786 * @param {Object} config
11788 Roo.data.SimpleStore = function(config){
11789 Roo.data.SimpleStore.superclass.constructor.call(this, {
11791 reader: new Roo.data.ArrayReader({
11794 Roo.data.Record.create(config.fields)
11796 proxy : new Roo.data.MemoryProxy(config.data)
11800 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11802 * Ext JS Library 1.1.1
11803 * Copyright(c) 2006-2007, Ext JS, LLC.
11805 * Originally Released Under LGPL - original licence link has changed is not relivant.
11808 * <script type="text/javascript">
11813 * @extends Roo.data.Store
11814 * @class Roo.data.JsonStore
11815 * Small helper class to make creating Stores for JSON data easier. <br/>
11817 var store = new Roo.data.JsonStore({
11818 url: 'get-images.php',
11820 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11823 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11824 * JsonReader and HttpProxy (unless inline data is provided).</b>
11825 * @cfg {Array} fields An array of field definition objects, or field name strings.
11827 * @param {Object} config
11829 Roo.data.JsonStore = function(c){
11830 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11831 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11832 reader: new Roo.data.JsonReader(c, c.fields)
11835 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11837 * Ext JS Library 1.1.1
11838 * Copyright(c) 2006-2007, Ext JS, LLC.
11840 * Originally Released Under LGPL - original licence link has changed is not relivant.
11843 * <script type="text/javascript">
11847 Roo.data.Field = function(config){
11848 if(typeof config == "string"){
11849 config = {name: config};
11851 Roo.apply(this, config);
11854 this.type = "auto";
11857 var st = Roo.data.SortTypes;
11858 // named sortTypes are supported, here we look them up
11859 if(typeof this.sortType == "string"){
11860 this.sortType = st[this.sortType];
11863 // set default sortType for strings and dates
11864 if(!this.sortType){
11867 this.sortType = st.asUCString;
11870 this.sortType = st.asDate;
11873 this.sortType = st.none;
11878 var stripRe = /[\$,%]/g;
11880 // prebuilt conversion function for this field, instead of
11881 // switching every time we're reading a value
11883 var cv, dateFormat = this.dateFormat;
11888 cv = function(v){ return v; };
11891 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11895 return v !== undefined && v !== null && v !== '' ?
11896 parseInt(String(v).replace(stripRe, ""), 10) : '';
11901 return v !== undefined && v !== null && v !== '' ?
11902 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11907 cv = function(v){ return v === true || v === "true" || v == 1; };
11914 if(v instanceof Date){
11918 if(dateFormat == "timestamp"){
11919 return new Date(v*1000);
11921 return Date.parseDate(v, dateFormat);
11923 var parsed = Date.parse(v);
11924 return parsed ? new Date(parsed) : null;
11933 Roo.data.Field.prototype = {
11941 * Ext JS Library 1.1.1
11942 * Copyright(c) 2006-2007, Ext JS, LLC.
11944 * Originally Released Under LGPL - original licence link has changed is not relivant.
11947 * <script type="text/javascript">
11950 // Base class for reading structured data from a data source. This class is intended to be
11951 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11954 * @class Roo.data.DataReader
11955 * Base class for reading structured data from a data source. This class is intended to be
11956 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11959 Roo.data.DataReader = function(meta, recordType){
11963 this.recordType = recordType instanceof Array ?
11964 Roo.data.Record.create(recordType) : recordType;
11967 Roo.data.DataReader.prototype = {
11969 * Create an empty record
11970 * @param {Object} data (optional) - overlay some values
11971 * @return {Roo.data.Record} record created.
11973 newRow : function(d) {
11975 this.recordType.prototype.fields.each(function(c) {
11977 case 'int' : da[c.name] = 0; break;
11978 case 'date' : da[c.name] = new Date(); break;
11979 case 'float' : da[c.name] = 0.0; break;
11980 case 'boolean' : da[c.name] = false; break;
11981 default : da[c.name] = ""; break;
11985 return new this.recordType(Roo.apply(da, d));
11990 * Ext JS Library 1.1.1
11991 * Copyright(c) 2006-2007, Ext JS, LLC.
11993 * Originally Released Under LGPL - original licence link has changed is not relivant.
11996 * <script type="text/javascript">
12000 * @class Roo.data.DataProxy
12001 * @extends Roo.data.Observable
12002 * This class is an abstract base class for implementations which provide retrieval of
12003 * unformatted data objects.<br>
12005 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12006 * (of the appropriate type which knows how to parse the data object) to provide a block of
12007 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12009 * Custom implementations must implement the load method as described in
12010 * {@link Roo.data.HttpProxy#load}.
12012 Roo.data.DataProxy = function(){
12015 * @event beforeload
12016 * Fires before a network request is made to retrieve a data object.
12017 * @param {Object} This DataProxy object.
12018 * @param {Object} params The params parameter to the load function.
12023 * Fires before the load method's callback is called.
12024 * @param {Object} This DataProxy object.
12025 * @param {Object} o The data object.
12026 * @param {Object} arg The callback argument object passed to the load function.
12030 * @event loadexception
12031 * Fires if an Exception occurs during data retrieval.
12032 * @param {Object} This DataProxy object.
12033 * @param {Object} o The data object.
12034 * @param {Object} arg The callback argument object passed to the load function.
12035 * @param {Object} e The Exception.
12037 loadexception : true
12039 Roo.data.DataProxy.superclass.constructor.call(this);
12042 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12045 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12049 * Ext JS Library 1.1.1
12050 * Copyright(c) 2006-2007, Ext JS, LLC.
12052 * Originally Released Under LGPL - original licence link has changed is not relivant.
12055 * <script type="text/javascript">
12058 * @class Roo.data.MemoryProxy
12059 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12060 * to the Reader when its load method is called.
12062 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12064 Roo.data.MemoryProxy = function(data){
12068 Roo.data.MemoryProxy.superclass.constructor.call(this);
12072 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12075 * Load data from the requested source (in this case an in-memory
12076 * data object passed to the constructor), read the data object into
12077 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12078 * process that block using the passed callback.
12079 * @param {Object} params This parameter is not used by the MemoryProxy class.
12080 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12081 * object into a block of Roo.data.Records.
12082 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12083 * The function must be passed <ul>
12084 * <li>The Record block object</li>
12085 * <li>The "arg" argument from the load function</li>
12086 * <li>A boolean success indicator</li>
12088 * @param {Object} scope The scope in which to call the callback
12089 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12091 load : function(params, reader, callback, scope, arg){
12092 params = params || {};
12095 result = reader.readRecords(this.data);
12097 this.fireEvent("loadexception", this, arg, null, e);
12098 callback.call(scope, null, arg, false);
12101 callback.call(scope, result, arg, true);
12105 update : function(params, records){
12110 * Ext JS Library 1.1.1
12111 * Copyright(c) 2006-2007, Ext JS, LLC.
12113 * Originally Released Under LGPL - original licence link has changed is not relivant.
12116 * <script type="text/javascript">
12119 * @class Roo.data.HttpProxy
12120 * @extends Roo.data.DataProxy
12121 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12122 * configured to reference a certain URL.<br><br>
12124 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12125 * from which the running page was served.<br><br>
12127 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12129 * Be aware that to enable the browser to parse an XML document, the server must set
12130 * the Content-Type header in the HTTP response to "text/xml".
12132 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12133 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12134 * will be used to make the request.
12136 Roo.data.HttpProxy = function(conn){
12137 Roo.data.HttpProxy.superclass.constructor.call(this);
12138 // is conn a conn config or a real conn?
12140 this.useAjax = !conn || !conn.events;
12144 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12145 // thse are take from connection...
12148 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12151 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12152 * extra parameters to each request made by this object. (defaults to undefined)
12155 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12156 * to each request made by this object. (defaults to undefined)
12159 * @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)
12162 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12165 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12171 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12175 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12176 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12177 * a finer-grained basis than the DataProxy events.
12179 getConnection : function(){
12180 return this.useAjax ? Roo.Ajax : this.conn;
12184 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12185 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12186 * process that block using the passed callback.
12187 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12188 * for the request to the remote server.
12189 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12190 * object into a block of Roo.data.Records.
12191 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12192 * The function must be passed <ul>
12193 * <li>The Record block object</li>
12194 * <li>The "arg" argument from the load function</li>
12195 * <li>A boolean success indicator</li>
12197 * @param {Object} scope The scope in which to call the callback
12198 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12200 load : function(params, reader, callback, scope, arg){
12201 if(this.fireEvent("beforeload", this, params) !== false){
12203 params : params || {},
12205 callback : callback,
12210 callback : this.loadResponse,
12214 Roo.applyIf(o, this.conn);
12215 if(this.activeRequest){
12216 Roo.Ajax.abort(this.activeRequest);
12218 this.activeRequest = Roo.Ajax.request(o);
12220 this.conn.request(o);
12223 callback.call(scope||this, null, arg, false);
12228 loadResponse : function(o, success, response){
12229 delete this.activeRequest;
12231 this.fireEvent("loadexception", this, o, response);
12232 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12237 result = o.reader.read(response);
12239 this.fireEvent("loadexception", this, o, response, e);
12240 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12244 this.fireEvent("load", this, o, o.request.arg);
12245 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12249 update : function(dataSet){
12254 updateResponse : function(dataSet){
12259 * Ext JS Library 1.1.1
12260 * Copyright(c) 2006-2007, Ext JS, LLC.
12262 * Originally Released Under LGPL - original licence link has changed is not relivant.
12265 * <script type="text/javascript">
12269 * @class Roo.data.ScriptTagProxy
12270 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12271 * other than the originating domain of the running page.<br><br>
12273 * <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
12274 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12276 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12277 * source code that is used as the source inside a <script> tag.<br><br>
12279 * In order for the browser to process the returned data, the server must wrap the data object
12280 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12281 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12282 * depending on whether the callback name was passed:
12285 boolean scriptTag = false;
12286 String cb = request.getParameter("callback");
12289 response.setContentType("text/javascript");
12291 response.setContentType("application/x-json");
12293 Writer out = response.getWriter();
12295 out.write(cb + "(");
12297 out.print(dataBlock.toJsonString());
12304 * @param {Object} config A configuration object.
12306 Roo.data.ScriptTagProxy = function(config){
12307 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12308 Roo.apply(this, config);
12309 this.head = document.getElementsByTagName("head")[0];
12312 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12314 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12316 * @cfg {String} url The URL from which to request the data object.
12319 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12323 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12324 * the server the name of the callback function set up by the load call to process the returned data object.
12325 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12326 * javascript output which calls this named function passing the data object as its only parameter.
12328 callbackParam : "callback",
12330 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12331 * name to the request.
12336 * Load data from the configured URL, read the data object into
12337 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12338 * process that block using the passed callback.
12339 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12340 * for the request to the remote server.
12341 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12342 * object into a block of Roo.data.Records.
12343 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12344 * The function must be passed <ul>
12345 * <li>The Record block object</li>
12346 * <li>The "arg" argument from the load function</li>
12347 * <li>A boolean success indicator</li>
12349 * @param {Object} scope The scope in which to call the callback
12350 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12352 load : function(params, reader, callback, scope, arg){
12353 if(this.fireEvent("beforeload", this, params) !== false){
12355 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12357 var url = this.url;
12358 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12360 url += "&_dc=" + (new Date().getTime());
12362 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12365 cb : "stcCallback"+transId,
12366 scriptId : "stcScript"+transId,
12370 callback : callback,
12376 window[trans.cb] = function(o){
12377 conn.handleResponse(o, trans);
12380 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12382 if(this.autoAbort !== false){
12386 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12388 var script = document.createElement("script");
12389 script.setAttribute("src", url);
12390 script.setAttribute("type", "text/javascript");
12391 script.setAttribute("id", trans.scriptId);
12392 this.head.appendChild(script);
12394 this.trans = trans;
12396 callback.call(scope||this, null, arg, false);
12401 isLoading : function(){
12402 return this.trans ? true : false;
12406 * Abort the current server request.
12408 abort : function(){
12409 if(this.isLoading()){
12410 this.destroyTrans(this.trans);
12415 destroyTrans : function(trans, isLoaded){
12416 this.head.removeChild(document.getElementById(trans.scriptId));
12417 clearTimeout(trans.timeoutId);
12419 window[trans.cb] = undefined;
12421 delete window[trans.cb];
12424 // if hasn't been loaded, wait for load to remove it to prevent script error
12425 window[trans.cb] = function(){
12426 window[trans.cb] = undefined;
12428 delete window[trans.cb];
12435 handleResponse : function(o, trans){
12436 this.trans = false;
12437 this.destroyTrans(trans, true);
12440 result = trans.reader.readRecords(o);
12442 this.fireEvent("loadexception", this, o, trans.arg, e);
12443 trans.callback.call(trans.scope||window, null, trans.arg, false);
12446 this.fireEvent("load", this, o, trans.arg);
12447 trans.callback.call(trans.scope||window, result, trans.arg, true);
12451 handleFailure : function(trans){
12452 this.trans = false;
12453 this.destroyTrans(trans, false);
12454 this.fireEvent("loadexception", this, null, trans.arg);
12455 trans.callback.call(trans.scope||window, null, trans.arg, false);
12459 * Ext JS Library 1.1.1
12460 * Copyright(c) 2006-2007, Ext JS, LLC.
12462 * Originally Released Under LGPL - original licence link has changed is not relivant.
12465 * <script type="text/javascript">
12469 * @class Roo.data.JsonReader
12470 * @extends Roo.data.DataReader
12471 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12472 * based on mappings in a provided Roo.data.Record constructor.
12474 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12475 * in the reply previously.
12480 var RecordDef = Roo.data.Record.create([
12481 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12482 {name: 'occupation'} // This field will use "occupation" as the mapping.
12484 var myReader = new Roo.data.JsonReader({
12485 totalProperty: "results", // The property which contains the total dataset size (optional)
12486 root: "rows", // The property which contains an Array of row objects
12487 id: "id" // The property within each row object that provides an ID for the record (optional)
12491 * This would consume a JSON file like this:
12493 { 'results': 2, 'rows': [
12494 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12495 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12498 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12499 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12500 * paged from the remote server.
12501 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12502 * @cfg {String} root name of the property which contains the Array of row objects.
12503 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12504 * @cfg {Array} fields Array of field definition objects
12506 * Create a new JsonReader
12507 * @param {Object} meta Metadata configuration options
12508 * @param {Object} recordType Either an Array of field definition objects,
12509 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12511 Roo.data.JsonReader = function(meta, recordType){
12514 // set some defaults:
12515 Roo.applyIf(meta, {
12516 totalProperty: 'total',
12517 successProperty : 'success',
12522 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12524 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12527 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12528 * Used by Store query builder to append _requestMeta to params.
12531 metaFromRemote : false,
12533 * This method is only used by a DataProxy which has retrieved data from a remote server.
12534 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12535 * @return {Object} data A data block which is used by an Roo.data.Store object as
12536 * a cache of Roo.data.Records.
12538 read : function(response){
12539 var json = response.responseText;
12541 var o = /* eval:var:o */ eval("("+json+")");
12543 throw {message: "JsonReader.read: Json object not found"};
12549 this.metaFromRemote = true;
12550 this.meta = o.metaData;
12551 this.recordType = Roo.data.Record.create(o.metaData.fields);
12552 this.onMetaChange(this.meta, this.recordType, o);
12554 return this.readRecords(o);
12557 // private function a store will implement
12558 onMetaChange : function(meta, recordType, o){
12565 simpleAccess: function(obj, subsc) {
12572 getJsonAccessor: function(){
12574 return function(expr) {
12576 return(re.test(expr))
12577 ? new Function("obj", "return obj." + expr)
12582 return Roo.emptyFn;
12587 * Create a data block containing Roo.data.Records from an XML document.
12588 * @param {Object} o An object which contains an Array of row objects in the property specified
12589 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12590 * which contains the total size of the dataset.
12591 * @return {Object} data A data block which is used by an Roo.data.Store object as
12592 * a cache of Roo.data.Records.
12594 readRecords : function(o){
12596 * After any data loads, the raw JSON data is available for further custom processing.
12600 var s = this.meta, Record = this.recordType,
12601 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12603 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12605 if(s.totalProperty) {
12606 this.getTotal = this.getJsonAccessor(s.totalProperty);
12608 if(s.successProperty) {
12609 this.getSuccess = this.getJsonAccessor(s.successProperty);
12611 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12613 var g = this.getJsonAccessor(s.id);
12614 this.getId = function(rec) {
12616 return (r === undefined || r === "") ? null : r;
12619 this.getId = function(){return null;};
12622 for(var jj = 0; jj < fl; jj++){
12624 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12625 this.ef[jj] = this.getJsonAccessor(map);
12629 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12630 if(s.totalProperty){
12631 var vt = parseInt(this.getTotal(o), 10);
12636 if(s.successProperty){
12637 var vs = this.getSuccess(o);
12638 if(vs === false || vs === 'false'){
12643 for(var i = 0; i < c; i++){
12646 var id = this.getId(n);
12647 for(var j = 0; j < fl; j++){
12649 var v = this.ef[j](n);
12651 Roo.log('missing convert for ' + f.name);
12655 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12657 var record = new Record(values, id);
12659 records[i] = record;
12665 totalRecords : totalRecords
12670 * Ext JS Library 1.1.1
12671 * Copyright(c) 2006-2007, Ext JS, LLC.
12673 * Originally Released Under LGPL - original licence link has changed is not relivant.
12676 * <script type="text/javascript">
12680 * @class Roo.data.ArrayReader
12681 * @extends Roo.data.DataReader
12682 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12683 * Each element of that Array represents a row of data fields. The
12684 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12685 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12689 var RecordDef = Roo.data.Record.create([
12690 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12691 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12693 var myReader = new Roo.data.ArrayReader({
12694 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12698 * This would consume an Array like this:
12700 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12702 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12704 * Create a new JsonReader
12705 * @param {Object} meta Metadata configuration options.
12706 * @param {Object} recordType Either an Array of field definition objects
12707 * as specified to {@link Roo.data.Record#create},
12708 * or an {@link Roo.data.Record} object
12709 * created using {@link Roo.data.Record#create}.
12711 Roo.data.ArrayReader = function(meta, recordType){
12712 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12715 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12717 * Create a data block containing Roo.data.Records from an XML document.
12718 * @param {Object} o An Array of row objects which represents the dataset.
12719 * @return {Object} data A data block which is used by an Roo.data.Store object as
12720 * a cache of Roo.data.Records.
12722 readRecords : function(o){
12723 var sid = this.meta ? this.meta.id : null;
12724 var recordType = this.recordType, fields = recordType.prototype.fields;
12727 for(var i = 0; i < root.length; i++){
12730 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12731 for(var j = 0, jlen = fields.length; j < jlen; j++){
12732 var f = fields.items[j];
12733 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12734 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12736 values[f.name] = v;
12738 var record = new recordType(values, id);
12740 records[records.length] = record;
12744 totalRecords : records.length
12753 * @class Roo.bootstrap.ComboBox
12754 * @extends Roo.bootstrap.TriggerField
12755 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12756 * @cfg {Boolean} append (true|false) default false
12757 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12758 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12759 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12760 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12761 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12762 * @cfg {Boolean} animate default true
12763 * @cfg {Boolean} emptyResultText only for touch device
12764 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12765 * @cfg {String} emptyTitle default ''
12767 * Create a new ComboBox.
12768 * @param {Object} config Configuration options
12770 Roo.bootstrap.ComboBox = function(config){
12771 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12775 * Fires when the dropdown list is expanded
12776 * @param {Roo.bootstrap.ComboBox} combo This combo box
12781 * Fires when the dropdown list is collapsed
12782 * @param {Roo.bootstrap.ComboBox} combo This combo box
12786 * @event beforeselect
12787 * Fires before a list item is selected. Return false to cancel the selection.
12788 * @param {Roo.bootstrap.ComboBox} combo This combo box
12789 * @param {Roo.data.Record} record The data record returned from the underlying store
12790 * @param {Number} index The index of the selected item in the dropdown list
12792 'beforeselect' : true,
12795 * Fires when a list item is selected
12796 * @param {Roo.bootstrap.ComboBox} combo This combo box
12797 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12798 * @param {Number} index The index of the selected item in the dropdown list
12802 * @event beforequery
12803 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12804 * The event object passed has these properties:
12805 * @param {Roo.bootstrap.ComboBox} combo This combo box
12806 * @param {String} query The query
12807 * @param {Boolean} forceAll true to force "all" query
12808 * @param {Boolean} cancel true to cancel the query
12809 * @param {Object} e The query event object
12811 'beforequery': true,
12814 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12815 * @param {Roo.bootstrap.ComboBox} combo This combo box
12820 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12821 * @param {Roo.bootstrap.ComboBox} combo This combo box
12822 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12827 * Fires when the remove value from the combobox array
12828 * @param {Roo.bootstrap.ComboBox} combo This combo box
12832 * @event afterremove
12833 * Fires when the remove value from the combobox array
12834 * @param {Roo.bootstrap.ComboBox} combo This combo box
12836 'afterremove' : true,
12838 * @event specialfilter
12839 * Fires when specialfilter
12840 * @param {Roo.bootstrap.ComboBox} combo This combo box
12842 'specialfilter' : true,
12845 * Fires when tick the element
12846 * @param {Roo.bootstrap.ComboBox} combo This combo box
12850 * @event touchviewdisplay
12851 * Fires when touch view require special display (default is using displayField)
12852 * @param {Roo.bootstrap.ComboBox} combo This combo box
12853 * @param {Object} cfg set html .
12855 'touchviewdisplay' : true
12860 this.tickItems = [];
12862 this.selectedIndex = -1;
12863 if(this.mode == 'local'){
12864 if(config.queryDelay === undefined){
12865 this.queryDelay = 10;
12867 if(config.minChars === undefined){
12873 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12876 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12877 * rendering into an Roo.Editor, defaults to false)
12880 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12881 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12884 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12887 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12888 * the dropdown list (defaults to undefined, with no header element)
12892 * @cfg {String/Roo.Template} tpl The template to use to render the output
12896 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12898 listWidth: undefined,
12900 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12901 * mode = 'remote' or 'text' if mode = 'local')
12903 displayField: undefined,
12906 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12907 * mode = 'remote' or 'value' if mode = 'local').
12908 * Note: use of a valueField requires the user make a selection
12909 * in order for a value to be mapped.
12911 valueField: undefined,
12913 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12918 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12919 * field's data value (defaults to the underlying DOM element's name)
12921 hiddenName: undefined,
12923 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12927 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12929 selectedClass: 'active',
12932 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12936 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12937 * anchor positions (defaults to 'tl-bl')
12939 listAlign: 'tl-bl?',
12941 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12945 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12946 * query specified by the allQuery config option (defaults to 'query')
12948 triggerAction: 'query',
12950 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12951 * (defaults to 4, does not apply if editable = false)
12955 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12956 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12960 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12961 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12965 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12966 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12970 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12971 * when editable = true (defaults to false)
12973 selectOnFocus:false,
12975 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12977 queryParam: 'query',
12979 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12980 * when mode = 'remote' (defaults to 'Loading...')
12982 loadingText: 'Loading...',
12984 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12988 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12992 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12993 * traditional select (defaults to true)
12997 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13001 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13005 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13006 * listWidth has a higher value)
13010 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13011 * allow the user to set arbitrary text into the field (defaults to false)
13013 forceSelection:false,
13015 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13016 * if typeAhead = true (defaults to 250)
13018 typeAheadDelay : 250,
13020 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13021 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13023 valueNotFoundText : undefined,
13025 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13027 blockFocus : false,
13030 * @cfg {Boolean} disableClear Disable showing of clear button.
13032 disableClear : false,
13034 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13036 alwaysQuery : false,
13039 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13044 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13046 invalidClass : "has-warning",
13049 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13051 validClass : "has-success",
13054 * @cfg {Boolean} specialFilter (true|false) special filter default false
13056 specialFilter : false,
13059 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13061 mobileTouchView : true,
13064 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13066 useNativeIOS : false,
13068 ios_options : false,
13080 btnPosition : 'right',
13081 triggerList : true,
13082 showToggleBtn : true,
13084 emptyResultText: 'Empty',
13085 triggerText : 'Select',
13088 // element that contains real text value.. (when hidden is used..)
13090 getAutoCreate : function()
13095 * Render classic select for iso
13098 if(Roo.isIOS && this.useNativeIOS){
13099 cfg = this.getAutoCreateNativeIOS();
13107 if(Roo.isTouch && this.mobileTouchView){
13108 cfg = this.getAutoCreateTouchView();
13115 if(!this.tickable){
13116 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13121 * ComboBox with tickable selections
13124 var align = this.labelAlign || this.parentLabelAlign();
13127 cls : 'form-group roo-combobox-tickable' //input-group
13130 var btn_text_select = '';
13131 var btn_text_done = '';
13132 var btn_text_cancel = '';
13134 if (this.btn_text_show) {
13135 btn_text_select = 'Select';
13136 btn_text_done = 'Done';
13137 btn_text_cancel = 'Cancel';
13142 cls : 'tickable-buttons',
13147 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13148 //html : this.triggerText
13149 html: btn_text_select
13155 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13157 html: btn_text_done
13163 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13165 html: btn_text_cancel
13171 buttons.cn.unshift({
13173 cls: 'roo-select2-search-field-input'
13179 Roo.each(buttons.cn, function(c){
13181 c.cls += ' btn-' + _this.size;
13184 if (_this.disabled) {
13195 cls: 'form-hidden-field'
13199 cls: 'roo-select2-choices',
13203 cls: 'roo-select2-search-field',
13214 cls: 'roo-select2-container input-group roo-select2-container-multi',
13219 // cls: 'typeahead typeahead-long dropdown-menu',
13220 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13225 if(this.hasFeedback && !this.allowBlank){
13229 cls: 'glyphicon form-control-feedback'
13232 combobox.cn.push(feedback);
13236 if (align ==='left' && this.fieldLabel.length) {
13238 cfg.cls += ' roo-form-group-label-left';
13243 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13244 tooltip : 'This field is required'
13249 cls : 'control-label',
13250 html : this.fieldLabel
13262 var labelCfg = cfg.cn[1];
13263 var contentCfg = cfg.cn[2];
13266 if(this.indicatorpos == 'right'){
13272 cls : 'control-label',
13276 html : this.fieldLabel
13280 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13281 tooltip : 'This field is required'
13296 labelCfg = cfg.cn[0];
13297 contentCfg = cfg.cn[1];
13301 if(this.labelWidth > 12){
13302 labelCfg.style = "width: " + this.labelWidth + 'px';
13305 if(this.labelWidth < 13 && this.labelmd == 0){
13306 this.labelmd = this.labelWidth;
13309 if(this.labellg > 0){
13310 labelCfg.cls += ' col-lg-' + this.labellg;
13311 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13314 if(this.labelmd > 0){
13315 labelCfg.cls += ' col-md-' + this.labelmd;
13316 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13319 if(this.labelsm > 0){
13320 labelCfg.cls += ' col-sm-' + this.labelsm;
13321 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13324 if(this.labelxs > 0){
13325 labelCfg.cls += ' col-xs-' + this.labelxs;
13326 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13330 } else if ( this.fieldLabel.length) {
13331 // Roo.log(" label");
13335 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13336 tooltip : 'This field is required'
13340 //cls : 'input-group-addon',
13341 html : this.fieldLabel
13346 if(this.indicatorpos == 'right'){
13350 //cls : 'input-group-addon',
13351 html : this.fieldLabel
13355 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13356 tooltip : 'This field is required'
13365 // Roo.log(" no label && no align");
13372 ['xs','sm','md','lg'].map(function(size){
13373 if (settings[size]) {
13374 cfg.cls += ' col-' + size + '-' + settings[size];
13382 _initEventsCalled : false,
13385 initEvents: function()
13387 if (this._initEventsCalled) { // as we call render... prevent looping...
13390 this._initEventsCalled = true;
13393 throw "can not find store for combo";
13396 this.indicator = this.indicatorEl();
13398 this.store = Roo.factory(this.store, Roo.data);
13399 this.store.parent = this;
13401 // if we are building from html. then this element is so complex, that we can not really
13402 // use the rendered HTML.
13403 // so we have to trash and replace the previous code.
13404 if (Roo.XComponent.build_from_html) {
13405 // remove this element....
13406 var e = this.el.dom, k=0;
13407 while (e ) { e = e.previousSibling; ++k;}
13412 this.rendered = false;
13414 this.render(this.parent().getChildContainer(true), k);
13417 if(Roo.isIOS && this.useNativeIOS){
13418 this.initIOSView();
13426 if(Roo.isTouch && this.mobileTouchView){
13427 this.initTouchView();
13432 this.initTickableEvents();
13436 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13438 if(this.hiddenName){
13440 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13442 this.hiddenField.dom.value =
13443 this.hiddenValue !== undefined ? this.hiddenValue :
13444 this.value !== undefined ? this.value : '';
13446 // prevent input submission
13447 this.el.dom.removeAttribute('name');
13448 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13453 // this.el.dom.setAttribute('autocomplete', 'off');
13456 var cls = 'x-combo-list';
13458 //this.list = new Roo.Layer({
13459 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13465 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13466 _this.list.setWidth(lw);
13469 this.list.on('mouseover', this.onViewOver, this);
13470 this.list.on('mousemove', this.onViewMove, this);
13471 this.list.on('scroll', this.onViewScroll, this);
13474 this.list.swallowEvent('mousewheel');
13475 this.assetHeight = 0;
13478 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13479 this.assetHeight += this.header.getHeight();
13482 this.innerList = this.list.createChild({cls:cls+'-inner'});
13483 this.innerList.on('mouseover', this.onViewOver, this);
13484 this.innerList.on('mousemove', this.onViewMove, this);
13485 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13487 if(this.allowBlank && !this.pageSize && !this.disableClear){
13488 this.footer = this.list.createChild({cls:cls+'-ft'});
13489 this.pageTb = new Roo.Toolbar(this.footer);
13493 this.footer = this.list.createChild({cls:cls+'-ft'});
13494 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13495 {pageSize: this.pageSize});
13499 if (this.pageTb && this.allowBlank && !this.disableClear) {
13501 this.pageTb.add(new Roo.Toolbar.Fill(), {
13502 cls: 'x-btn-icon x-btn-clear',
13504 handler: function()
13507 _this.clearValue();
13508 _this.onSelect(false, -1);
13513 this.assetHeight += this.footer.getHeight();
13518 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13521 this.view = new Roo.View(this.list, this.tpl, {
13522 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13524 //this.view.wrapEl.setDisplayed(false);
13525 this.view.on('click', this.onViewClick, this);
13528 this.store.on('beforeload', this.onBeforeLoad, this);
13529 this.store.on('load', this.onLoad, this);
13530 this.store.on('loadexception', this.onLoadException, this);
13532 if(this.resizable){
13533 this.resizer = new Roo.Resizable(this.list, {
13534 pinned:true, handles:'se'
13536 this.resizer.on('resize', function(r, w, h){
13537 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13538 this.listWidth = w;
13539 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13540 this.restrictHeight();
13542 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13545 if(!this.editable){
13546 this.editable = true;
13547 this.setEditable(false);
13552 if (typeof(this.events.add.listeners) != 'undefined') {
13554 this.addicon = this.wrap.createChild(
13555 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13557 this.addicon.on('click', function(e) {
13558 this.fireEvent('add', this);
13561 if (typeof(this.events.edit.listeners) != 'undefined') {
13563 this.editicon = this.wrap.createChild(
13564 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13565 if (this.addicon) {
13566 this.editicon.setStyle('margin-left', '40px');
13568 this.editicon.on('click', function(e) {
13570 // we fire even if inothing is selected..
13571 this.fireEvent('edit', this, this.lastData );
13577 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13578 "up" : function(e){
13579 this.inKeyMode = true;
13583 "down" : function(e){
13584 if(!this.isExpanded()){
13585 this.onTriggerClick();
13587 this.inKeyMode = true;
13592 "enter" : function(e){
13593 // this.onViewClick();
13597 if(this.fireEvent("specialkey", this, e)){
13598 this.onViewClick(false);
13604 "esc" : function(e){
13608 "tab" : function(e){
13611 if(this.fireEvent("specialkey", this, e)){
13612 this.onViewClick(false);
13620 doRelay : function(foo, bar, hname){
13621 if(hname == 'down' || this.scope.isExpanded()){
13622 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13631 this.queryDelay = Math.max(this.queryDelay || 10,
13632 this.mode == 'local' ? 10 : 250);
13635 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13637 if(this.typeAhead){
13638 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13640 if(this.editable !== false){
13641 this.inputEl().on("keyup", this.onKeyUp, this);
13643 if(this.forceSelection){
13644 this.inputEl().on('blur', this.doForce, this);
13648 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13649 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13653 initTickableEvents: function()
13657 if(this.hiddenName){
13659 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13661 this.hiddenField.dom.value =
13662 this.hiddenValue !== undefined ? this.hiddenValue :
13663 this.value !== undefined ? this.value : '';
13665 // prevent input submission
13666 this.el.dom.removeAttribute('name');
13667 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13672 // this.list = this.el.select('ul.dropdown-menu',true).first();
13674 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13675 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13676 if(this.triggerList){
13677 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13680 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13681 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13683 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13684 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13686 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13687 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13689 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13690 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13691 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13694 this.cancelBtn.hide();
13699 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13700 _this.list.setWidth(lw);
13703 this.list.on('mouseover', this.onViewOver, this);
13704 this.list.on('mousemove', this.onViewMove, this);
13706 this.list.on('scroll', this.onViewScroll, this);
13709 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13710 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13713 this.view = new Roo.View(this.list, this.tpl, {
13718 selectedClass: this.selectedClass
13721 //this.view.wrapEl.setDisplayed(false);
13722 this.view.on('click', this.onViewClick, this);
13726 this.store.on('beforeload', this.onBeforeLoad, this);
13727 this.store.on('load', this.onLoad, this);
13728 this.store.on('loadexception', this.onLoadException, this);
13731 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13732 "up" : function(e){
13733 this.inKeyMode = true;
13737 "down" : function(e){
13738 this.inKeyMode = true;
13742 "enter" : function(e){
13743 if(this.fireEvent("specialkey", this, e)){
13744 this.onViewClick(false);
13750 "esc" : function(e){
13751 this.onTickableFooterButtonClick(e, false, false);
13754 "tab" : function(e){
13755 this.fireEvent("specialkey", this, e);
13757 this.onTickableFooterButtonClick(e, false, false);
13764 doRelay : function(e, fn, key){
13765 if(this.scope.isExpanded()){
13766 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13775 this.queryDelay = Math.max(this.queryDelay || 10,
13776 this.mode == 'local' ? 10 : 250);
13779 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13781 if(this.typeAhead){
13782 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13785 if(this.editable !== false){
13786 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13789 this.indicator = this.indicatorEl();
13791 if(this.indicator){
13792 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13793 this.indicator.hide();
13798 onDestroy : function(){
13800 this.view.setStore(null);
13801 this.view.el.removeAllListeners();
13802 this.view.el.remove();
13803 this.view.purgeListeners();
13806 this.list.dom.innerHTML = '';
13810 this.store.un('beforeload', this.onBeforeLoad, this);
13811 this.store.un('load', this.onLoad, this);
13812 this.store.un('loadexception', this.onLoadException, this);
13814 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13818 fireKey : function(e){
13819 if(e.isNavKeyPress() && !this.list.isVisible()){
13820 this.fireEvent("specialkey", this, e);
13825 onResize: function(w, h){
13826 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13828 // if(typeof w != 'number'){
13829 // // we do not handle it!?!?
13832 // var tw = this.trigger.getWidth();
13833 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13834 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13836 // this.inputEl().setWidth( this.adjustWidth('input', x));
13838 // //this.trigger.setStyle('left', x+'px');
13840 // if(this.list && this.listWidth === undefined){
13841 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13842 // this.list.setWidth(lw);
13843 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13851 * Allow or prevent the user from directly editing the field text. If false is passed,
13852 * the user will only be able to select from the items defined in the dropdown list. This method
13853 * is the runtime equivalent of setting the 'editable' config option at config time.
13854 * @param {Boolean} value True to allow the user to directly edit the field text
13856 setEditable : function(value){
13857 if(value == this.editable){
13860 this.editable = value;
13862 this.inputEl().dom.setAttribute('readOnly', true);
13863 this.inputEl().on('mousedown', this.onTriggerClick, this);
13864 this.inputEl().addClass('x-combo-noedit');
13866 this.inputEl().dom.setAttribute('readOnly', false);
13867 this.inputEl().un('mousedown', this.onTriggerClick, this);
13868 this.inputEl().removeClass('x-combo-noedit');
13874 onBeforeLoad : function(combo,opts){
13875 if(!this.hasFocus){
13879 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13881 this.restrictHeight();
13882 this.selectedIndex = -1;
13886 onLoad : function(){
13888 this.hasQuery = false;
13890 if(!this.hasFocus){
13894 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13895 this.loading.hide();
13898 if(this.store.getCount() > 0){
13901 this.restrictHeight();
13902 if(this.lastQuery == this.allQuery){
13903 if(this.editable && !this.tickable){
13904 this.inputEl().dom.select();
13908 !this.selectByValue(this.value, true) &&
13911 !this.store.lastOptions ||
13912 typeof(this.store.lastOptions.add) == 'undefined' ||
13913 this.store.lastOptions.add != true
13916 this.select(0, true);
13919 if(this.autoFocus){
13922 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13923 this.taTask.delay(this.typeAheadDelay);
13927 this.onEmptyResults();
13933 onLoadException : function()
13935 this.hasQuery = false;
13937 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13938 this.loading.hide();
13941 if(this.tickable && this.editable){
13946 // only causes errors at present
13947 //Roo.log(this.store.reader.jsonData);
13948 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13950 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13956 onTypeAhead : function(){
13957 if(this.store.getCount() > 0){
13958 var r = this.store.getAt(0);
13959 var newValue = r.data[this.displayField];
13960 var len = newValue.length;
13961 var selStart = this.getRawValue().length;
13963 if(selStart != len){
13964 this.setRawValue(newValue);
13965 this.selectText(selStart, newValue.length);
13971 onSelect : function(record, index){
13973 if(this.fireEvent('beforeselect', this, record, index) !== false){
13975 this.setFromData(index > -1 ? record.data : false);
13978 this.fireEvent('select', this, record, index);
13983 * Returns the currently selected field value or empty string if no value is set.
13984 * @return {String} value The selected value
13986 getValue : function()
13988 if(Roo.isIOS && this.useNativeIOS){
13989 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13993 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13996 if(this.valueField){
13997 return typeof this.value != 'undefined' ? this.value : '';
13999 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14003 getRawValue : function()
14005 if(Roo.isIOS && this.useNativeIOS){
14006 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14009 var v = this.inputEl().getValue();
14015 * Clears any text/value currently set in the field
14017 clearValue : function(){
14019 if(this.hiddenField){
14020 this.hiddenField.dom.value = '';
14023 this.setRawValue('');
14024 this.lastSelectionText = '';
14025 this.lastData = false;
14027 var close = this.closeTriggerEl();
14038 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14039 * will be displayed in the field. If the value does not match the data value of an existing item,
14040 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14041 * Otherwise the field will be blank (although the value will still be set).
14042 * @param {String} value The value to match
14044 setValue : function(v)
14046 if(Roo.isIOS && this.useNativeIOS){
14047 this.setIOSValue(v);
14057 if(this.valueField){
14058 var r = this.findRecord(this.valueField, v);
14060 text = r.data[this.displayField];
14061 }else if(this.valueNotFoundText !== undefined){
14062 text = this.valueNotFoundText;
14065 this.lastSelectionText = text;
14066 if(this.hiddenField){
14067 this.hiddenField.dom.value = v;
14069 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14072 var close = this.closeTriggerEl();
14075 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14081 * @property {Object} the last set data for the element
14086 * Sets the value of the field based on a object which is related to the record format for the store.
14087 * @param {Object} value the value to set as. or false on reset?
14089 setFromData : function(o){
14096 var dv = ''; // display value
14097 var vv = ''; // value value..
14099 if (this.displayField) {
14100 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14102 // this is an error condition!!!
14103 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14106 if(this.valueField){
14107 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14110 var close = this.closeTriggerEl();
14113 if(dv.length || vv * 1 > 0){
14115 this.blockFocus=true;
14121 if(this.hiddenField){
14122 this.hiddenField.dom.value = vv;
14124 this.lastSelectionText = dv;
14125 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14129 // no hidden field.. - we store the value in 'value', but still display
14130 // display field!!!!
14131 this.lastSelectionText = dv;
14132 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14139 reset : function(){
14140 // overridden so that last data is reset..
14147 this.setValue(this.originalValue);
14148 //this.clearInvalid();
14149 this.lastData = false;
14151 this.view.clearSelections();
14157 findRecord : function(prop, value){
14159 if(this.store.getCount() > 0){
14160 this.store.each(function(r){
14161 if(r.data[prop] == value){
14171 getName: function()
14173 // returns hidden if it's set..
14174 if (!this.rendered) {return ''};
14175 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14179 onViewMove : function(e, t){
14180 this.inKeyMode = false;
14184 onViewOver : function(e, t){
14185 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14188 var item = this.view.findItemFromChild(t);
14191 var index = this.view.indexOf(item);
14192 this.select(index, false);
14197 onViewClick : function(view, doFocus, el, e)
14199 var index = this.view.getSelectedIndexes()[0];
14201 var r = this.store.getAt(index);
14205 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14212 Roo.each(this.tickItems, function(v,k){
14214 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14216 _this.tickItems.splice(k, 1);
14218 if(typeof(e) == 'undefined' && view == false){
14219 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14231 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14232 this.tickItems.push(r.data);
14235 if(typeof(e) == 'undefined' && view == false){
14236 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14243 this.onSelect(r, index);
14245 if(doFocus !== false && !this.blockFocus){
14246 this.inputEl().focus();
14251 restrictHeight : function(){
14252 //this.innerList.dom.style.height = '';
14253 //var inner = this.innerList.dom;
14254 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14255 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14256 //this.list.beginUpdate();
14257 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14258 this.list.alignTo(this.inputEl(), this.listAlign);
14259 this.list.alignTo(this.inputEl(), this.listAlign);
14260 //this.list.endUpdate();
14264 onEmptyResults : function(){
14266 if(this.tickable && this.editable){
14267 this.hasFocus = false;
14268 this.restrictHeight();
14276 * Returns true if the dropdown list is expanded, else false.
14278 isExpanded : function(){
14279 return this.list.isVisible();
14283 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14284 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14285 * @param {String} value The data value of the item to select
14286 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14287 * selected item if it is not currently in view (defaults to true)
14288 * @return {Boolean} True if the value matched an item in the list, else false
14290 selectByValue : function(v, scrollIntoView){
14291 if(v !== undefined && v !== null){
14292 var r = this.findRecord(this.valueField || this.displayField, v);
14294 this.select(this.store.indexOf(r), scrollIntoView);
14302 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14303 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14304 * @param {Number} index The zero-based index of the list item to select
14305 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14306 * selected item if it is not currently in view (defaults to true)
14308 select : function(index, scrollIntoView){
14309 this.selectedIndex = index;
14310 this.view.select(index);
14311 if(scrollIntoView !== false){
14312 var el = this.view.getNode(index);
14314 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14317 this.list.scrollChildIntoView(el, false);
14323 selectNext : function(){
14324 var ct = this.store.getCount();
14326 if(this.selectedIndex == -1){
14328 }else if(this.selectedIndex < ct-1){
14329 this.select(this.selectedIndex+1);
14335 selectPrev : function(){
14336 var ct = this.store.getCount();
14338 if(this.selectedIndex == -1){
14340 }else if(this.selectedIndex != 0){
14341 this.select(this.selectedIndex-1);
14347 onKeyUp : function(e){
14348 if(this.editable !== false && !e.isSpecialKey()){
14349 this.lastKey = e.getKey();
14350 this.dqTask.delay(this.queryDelay);
14355 validateBlur : function(){
14356 return !this.list || !this.list.isVisible();
14360 initQuery : function(){
14362 var v = this.getRawValue();
14364 if(this.tickable && this.editable){
14365 v = this.tickableInputEl().getValue();
14372 doForce : function(){
14373 if(this.inputEl().dom.value.length > 0){
14374 this.inputEl().dom.value =
14375 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14381 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14382 * query allowing the query action to be canceled if needed.
14383 * @param {String} query The SQL query to execute
14384 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14385 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14386 * saved in the current store (defaults to false)
14388 doQuery : function(q, forceAll){
14390 if(q === undefined || q === null){
14395 forceAll: forceAll,
14399 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14404 forceAll = qe.forceAll;
14405 if(forceAll === true || (q.length >= this.minChars)){
14407 this.hasQuery = true;
14409 if(this.lastQuery != q || this.alwaysQuery){
14410 this.lastQuery = q;
14411 if(this.mode == 'local'){
14412 this.selectedIndex = -1;
14414 this.store.clearFilter();
14417 if(this.specialFilter){
14418 this.fireEvent('specialfilter', this);
14423 this.store.filter(this.displayField, q);
14426 this.store.fireEvent("datachanged", this.store);
14433 this.store.baseParams[this.queryParam] = q;
14435 var options = {params : this.getParams(q)};
14438 options.add = true;
14439 options.params.start = this.page * this.pageSize;
14442 this.store.load(options);
14445 * this code will make the page width larger, at the beginning, the list not align correctly,
14446 * we should expand the list on onLoad
14447 * so command out it
14452 this.selectedIndex = -1;
14457 this.loadNext = false;
14461 getParams : function(q){
14463 //p[this.queryParam] = q;
14467 p.limit = this.pageSize;
14473 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14475 collapse : function(){
14476 if(!this.isExpanded()){
14482 this.hasFocus = false;
14486 this.cancelBtn.hide();
14487 this.trigger.show();
14490 this.tickableInputEl().dom.value = '';
14491 this.tickableInputEl().blur();
14496 Roo.get(document).un('mousedown', this.collapseIf, this);
14497 Roo.get(document).un('mousewheel', this.collapseIf, this);
14498 if (!this.editable) {
14499 Roo.get(document).un('keydown', this.listKeyPress, this);
14501 this.fireEvent('collapse', this);
14507 collapseIf : function(e){
14508 var in_combo = e.within(this.el);
14509 var in_list = e.within(this.list);
14510 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14512 if (in_combo || in_list || is_list) {
14513 //e.stopPropagation();
14518 this.onTickableFooterButtonClick(e, false, false);
14526 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14528 expand : function(){
14530 if(this.isExpanded() || !this.hasFocus){
14534 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14535 this.list.setWidth(lw);
14541 this.restrictHeight();
14545 this.tickItems = Roo.apply([], this.item);
14548 this.cancelBtn.show();
14549 this.trigger.hide();
14552 this.tickableInputEl().focus();
14557 Roo.get(document).on('mousedown', this.collapseIf, this);
14558 Roo.get(document).on('mousewheel', this.collapseIf, this);
14559 if (!this.editable) {
14560 Roo.get(document).on('keydown', this.listKeyPress, this);
14563 this.fireEvent('expand', this);
14567 // Implements the default empty TriggerField.onTriggerClick function
14568 onTriggerClick : function(e)
14570 Roo.log('trigger click');
14572 if(this.disabled || !this.triggerList){
14577 this.loadNext = false;
14579 if(this.isExpanded()){
14581 if (!this.blockFocus) {
14582 this.inputEl().focus();
14586 this.hasFocus = true;
14587 if(this.triggerAction == 'all') {
14588 this.doQuery(this.allQuery, true);
14590 this.doQuery(this.getRawValue());
14592 if (!this.blockFocus) {
14593 this.inputEl().focus();
14598 onTickableTriggerClick : function(e)
14605 this.loadNext = false;
14606 this.hasFocus = true;
14608 if(this.triggerAction == 'all') {
14609 this.doQuery(this.allQuery, true);
14611 this.doQuery(this.getRawValue());
14615 onSearchFieldClick : function(e)
14617 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14618 this.onTickableFooterButtonClick(e, false, false);
14622 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14627 this.loadNext = false;
14628 this.hasFocus = true;
14630 if(this.triggerAction == 'all') {
14631 this.doQuery(this.allQuery, true);
14633 this.doQuery(this.getRawValue());
14637 listKeyPress : function(e)
14639 //Roo.log('listkeypress');
14640 // scroll to first matching element based on key pres..
14641 if (e.isSpecialKey()) {
14644 var k = String.fromCharCode(e.getKey()).toUpperCase();
14647 var csel = this.view.getSelectedNodes();
14648 var cselitem = false;
14650 var ix = this.view.indexOf(csel[0]);
14651 cselitem = this.store.getAt(ix);
14652 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14658 this.store.each(function(v) {
14660 // start at existing selection.
14661 if (cselitem.id == v.id) {
14667 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14668 match = this.store.indexOf(v);
14674 if (match === false) {
14675 return true; // no more action?
14678 this.view.select(match);
14679 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14680 sn.scrollIntoView(sn.dom.parentNode, false);
14683 onViewScroll : function(e, t){
14685 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){
14689 this.hasQuery = true;
14691 this.loading = this.list.select('.loading', true).first();
14693 if(this.loading === null){
14694 this.list.createChild({
14696 cls: 'loading roo-select2-more-results roo-select2-active',
14697 html: 'Loading more results...'
14700 this.loading = this.list.select('.loading', true).first();
14702 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14704 this.loading.hide();
14707 this.loading.show();
14712 this.loadNext = true;
14714 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14719 addItem : function(o)
14721 var dv = ''; // display value
14723 if (this.displayField) {
14724 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14726 // this is an error condition!!!
14727 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14734 var choice = this.choices.createChild({
14736 cls: 'roo-select2-search-choice',
14745 cls: 'roo-select2-search-choice-close fa fa-times',
14750 }, this.searchField);
14752 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14754 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14762 this.inputEl().dom.value = '';
14767 onRemoveItem : function(e, _self, o)
14769 e.preventDefault();
14771 this.lastItem = Roo.apply([], this.item);
14773 var index = this.item.indexOf(o.data) * 1;
14776 Roo.log('not this item?!');
14780 this.item.splice(index, 1);
14785 this.fireEvent('remove', this, e);
14791 syncValue : function()
14793 if(!this.item.length){
14800 Roo.each(this.item, function(i){
14801 if(_this.valueField){
14802 value.push(i[_this.valueField]);
14809 this.value = value.join(',');
14811 if(this.hiddenField){
14812 this.hiddenField.dom.value = this.value;
14815 this.store.fireEvent("datachanged", this.store);
14820 clearItem : function()
14822 if(!this.multiple){
14828 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14836 if(this.tickable && !Roo.isTouch){
14837 this.view.refresh();
14841 inputEl: function ()
14843 if(Roo.isIOS && this.useNativeIOS){
14844 return this.el.select('select.roo-ios-select', true).first();
14847 if(Roo.isTouch && this.mobileTouchView){
14848 return this.el.select('input.form-control',true).first();
14852 return this.searchField;
14855 return this.el.select('input.form-control',true).first();
14858 onTickableFooterButtonClick : function(e, btn, el)
14860 e.preventDefault();
14862 this.lastItem = Roo.apply([], this.item);
14864 if(btn && btn.name == 'cancel'){
14865 this.tickItems = Roo.apply([], this.item);
14874 Roo.each(this.tickItems, function(o){
14882 validate : function()
14884 if(this.getVisibilityEl().hasClass('hidden')){
14888 var v = this.getRawValue();
14891 v = this.getValue();
14894 if(this.disabled || this.allowBlank || v.length){
14899 this.markInvalid();
14903 tickableInputEl : function()
14905 if(!this.tickable || !this.editable){
14906 return this.inputEl();
14909 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14913 getAutoCreateTouchView : function()
14918 cls: 'form-group' //input-group
14924 type : this.inputType,
14925 cls : 'form-control x-combo-noedit',
14926 autocomplete: 'new-password',
14927 placeholder : this.placeholder || '',
14932 input.name = this.name;
14936 input.cls += ' input-' + this.size;
14939 if (this.disabled) {
14940 input.disabled = true;
14951 inputblock.cls += ' input-group';
14953 inputblock.cn.unshift({
14955 cls : 'input-group-addon',
14960 if(this.removable && !this.multiple){
14961 inputblock.cls += ' roo-removable';
14963 inputblock.cn.push({
14966 cls : 'roo-combo-removable-btn close'
14970 if(this.hasFeedback && !this.allowBlank){
14972 inputblock.cls += ' has-feedback';
14974 inputblock.cn.push({
14976 cls: 'glyphicon form-control-feedback'
14983 inputblock.cls += (this.before) ? '' : ' input-group';
14985 inputblock.cn.push({
14987 cls : 'input-group-addon',
14998 cls: 'form-hidden-field'
15012 cls: 'form-hidden-field'
15016 cls: 'roo-select2-choices',
15020 cls: 'roo-select2-search-field',
15033 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15039 if(!this.multiple && this.showToggleBtn){
15046 if (this.caret != false) {
15049 cls: 'fa fa-' + this.caret
15056 cls : 'input-group-addon btn dropdown-toggle',
15061 cls: 'combobox-clear',
15075 combobox.cls += ' roo-select2-container-multi';
15078 var align = this.labelAlign || this.parentLabelAlign();
15080 if (align ==='left' && this.fieldLabel.length) {
15085 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15086 tooltip : 'This field is required'
15090 cls : 'control-label',
15091 html : this.fieldLabel
15102 var labelCfg = cfg.cn[1];
15103 var contentCfg = cfg.cn[2];
15106 if(this.indicatorpos == 'right'){
15111 cls : 'control-label',
15115 html : this.fieldLabel
15119 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15120 tooltip : 'This field is required'
15133 labelCfg = cfg.cn[0];
15134 contentCfg = cfg.cn[1];
15139 if(this.labelWidth > 12){
15140 labelCfg.style = "width: " + this.labelWidth + 'px';
15143 if(this.labelWidth < 13 && this.labelmd == 0){
15144 this.labelmd = this.labelWidth;
15147 if(this.labellg > 0){
15148 labelCfg.cls += ' col-lg-' + this.labellg;
15149 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15152 if(this.labelmd > 0){
15153 labelCfg.cls += ' col-md-' + this.labelmd;
15154 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15157 if(this.labelsm > 0){
15158 labelCfg.cls += ' col-sm-' + this.labelsm;
15159 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15162 if(this.labelxs > 0){
15163 labelCfg.cls += ' col-xs-' + this.labelxs;
15164 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15168 } else if ( this.fieldLabel.length) {
15172 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15173 tooltip : 'This field is required'
15177 cls : 'control-label',
15178 html : this.fieldLabel
15189 if(this.indicatorpos == 'right'){
15193 cls : 'control-label',
15194 html : this.fieldLabel,
15198 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15199 tooltip : 'This field is required'
15216 var settings = this;
15218 ['xs','sm','md','lg'].map(function(size){
15219 if (settings[size]) {
15220 cfg.cls += ' col-' + size + '-' + settings[size];
15227 initTouchView : function()
15229 this.renderTouchView();
15231 this.touchViewEl.on('scroll', function(){
15232 this.el.dom.scrollTop = 0;
15235 this.originalValue = this.getValue();
15237 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15239 this.inputEl().on("click", this.showTouchView, this);
15240 if (this.triggerEl) {
15241 this.triggerEl.on("click", this.showTouchView, this);
15245 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15246 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15248 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15250 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15251 this.store.on('load', this.onTouchViewLoad, this);
15252 this.store.on('loadexception', this.onTouchViewLoadException, this);
15254 if(this.hiddenName){
15256 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15258 this.hiddenField.dom.value =
15259 this.hiddenValue !== undefined ? this.hiddenValue :
15260 this.value !== undefined ? this.value : '';
15262 this.el.dom.removeAttribute('name');
15263 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15267 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15268 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15271 if(this.removable && !this.multiple){
15272 var close = this.closeTriggerEl();
15274 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15275 close.on('click', this.removeBtnClick, this, close);
15279 * fix the bug in Safari iOS8
15281 this.inputEl().on("focus", function(e){
15282 document.activeElement.blur();
15290 renderTouchView : function()
15292 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15293 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15295 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15296 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15298 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15299 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15300 this.touchViewBodyEl.setStyle('overflow', 'auto');
15302 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15303 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15305 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15306 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15310 showTouchView : function()
15316 this.touchViewHeaderEl.hide();
15318 if(this.modalTitle.length){
15319 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15320 this.touchViewHeaderEl.show();
15323 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15324 this.touchViewEl.show();
15326 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15328 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15329 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15331 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15333 if(this.modalTitle.length){
15334 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15337 this.touchViewBodyEl.setHeight(bodyHeight);
15341 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15343 this.touchViewEl.addClass('in');
15346 this.doTouchViewQuery();
15350 hideTouchView : function()
15352 this.touchViewEl.removeClass('in');
15356 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15358 this.touchViewEl.setStyle('display', 'none');
15363 setTouchViewValue : function()
15370 Roo.each(this.tickItems, function(o){
15375 this.hideTouchView();
15378 doTouchViewQuery : function()
15387 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15391 if(!this.alwaysQuery || this.mode == 'local'){
15392 this.onTouchViewLoad();
15399 onTouchViewBeforeLoad : function(combo,opts)
15405 onTouchViewLoad : function()
15407 if(this.store.getCount() < 1){
15408 this.onTouchViewEmptyResults();
15412 this.clearTouchView();
15414 var rawValue = this.getRawValue();
15416 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15418 this.tickItems = [];
15420 this.store.data.each(function(d, rowIndex){
15421 var row = this.touchViewListGroup.createChild(template);
15423 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15424 row.addClass(d.data.cls);
15427 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15430 html : d.data[this.displayField]
15433 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15434 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15437 row.removeClass('selected');
15438 if(!this.multiple && this.valueField &&
15439 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15442 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15443 row.addClass('selected');
15446 if(this.multiple && this.valueField &&
15447 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15451 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15452 this.tickItems.push(d.data);
15455 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15459 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15461 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15463 if(this.modalTitle.length){
15464 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15467 var listHeight = this.touchViewListGroup.getHeight();
15471 if(firstChecked && listHeight > bodyHeight){
15472 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15477 onTouchViewLoadException : function()
15479 this.hideTouchView();
15482 onTouchViewEmptyResults : function()
15484 this.clearTouchView();
15486 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15488 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15492 clearTouchView : function()
15494 this.touchViewListGroup.dom.innerHTML = '';
15497 onTouchViewClick : function(e, el, o)
15499 e.preventDefault();
15502 var rowIndex = o.rowIndex;
15504 var r = this.store.getAt(rowIndex);
15506 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15508 if(!this.multiple){
15509 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15510 c.dom.removeAttribute('checked');
15513 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15515 this.setFromData(r.data);
15517 var close = this.closeTriggerEl();
15523 this.hideTouchView();
15525 this.fireEvent('select', this, r, rowIndex);
15530 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15531 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15532 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15536 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15537 this.addItem(r.data);
15538 this.tickItems.push(r.data);
15542 getAutoCreateNativeIOS : function()
15545 cls: 'form-group' //input-group,
15550 cls : 'roo-ios-select'
15554 combobox.name = this.name;
15557 if (this.disabled) {
15558 combobox.disabled = true;
15561 var settings = this;
15563 ['xs','sm','md','lg'].map(function(size){
15564 if (settings[size]) {
15565 cfg.cls += ' col-' + size + '-' + settings[size];
15575 initIOSView : function()
15577 this.store.on('load', this.onIOSViewLoad, this);
15582 onIOSViewLoad : function()
15584 if(this.store.getCount() < 1){
15588 this.clearIOSView();
15590 if(this.allowBlank) {
15592 var default_text = '-- SELECT --';
15594 if(this.placeholder.length){
15595 default_text = this.placeholder;
15598 if(this.emptyTitle.length){
15599 default_text += ' - ' + this.emptyTitle + ' -';
15602 var opt = this.inputEl().createChild({
15605 html : default_text
15609 o[this.valueField] = 0;
15610 o[this.displayField] = default_text;
15612 this.ios_options.push({
15619 this.store.data.each(function(d, rowIndex){
15623 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15624 html = d.data[this.displayField];
15629 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15630 value = d.data[this.valueField];
15639 if(this.value == d.data[this.valueField]){
15640 option['selected'] = true;
15643 var opt = this.inputEl().createChild(option);
15645 this.ios_options.push({
15652 this.inputEl().on('change', function(){
15653 this.fireEvent('select', this);
15658 clearIOSView: function()
15660 this.inputEl().dom.innerHTML = '';
15662 this.ios_options = [];
15665 setIOSValue: function(v)
15669 if(!this.ios_options){
15673 Roo.each(this.ios_options, function(opts){
15675 opts.el.dom.removeAttribute('selected');
15677 if(opts.data[this.valueField] != v){
15681 opts.el.dom.setAttribute('selected', true);
15687 * @cfg {Boolean} grow
15691 * @cfg {Number} growMin
15695 * @cfg {Number} growMax
15704 Roo.apply(Roo.bootstrap.ComboBox, {
15708 cls: 'modal-header',
15730 cls: 'list-group-item',
15734 cls: 'roo-combobox-list-group-item-value'
15738 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15752 listItemCheckbox : {
15754 cls: 'list-group-item',
15758 cls: 'roo-combobox-list-group-item-value'
15762 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15778 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15783 cls: 'modal-footer',
15791 cls: 'col-xs-6 text-left',
15794 cls: 'btn btn-danger roo-touch-view-cancel',
15800 cls: 'col-xs-6 text-right',
15803 cls: 'btn btn-success roo-touch-view-ok',
15814 Roo.apply(Roo.bootstrap.ComboBox, {
15816 touchViewTemplate : {
15818 cls: 'modal fade roo-combobox-touch-view',
15822 cls: 'modal-dialog',
15823 style : 'position:fixed', // we have to fix position....
15827 cls: 'modal-content',
15829 Roo.bootstrap.ComboBox.header,
15830 Roo.bootstrap.ComboBox.body,
15831 Roo.bootstrap.ComboBox.footer
15840 * Ext JS Library 1.1.1
15841 * Copyright(c) 2006-2007, Ext JS, LLC.
15843 * Originally Released Under LGPL - original licence link has changed is not relivant.
15846 * <script type="text/javascript">
15851 * @extends Roo.util.Observable
15852 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15853 * This class also supports single and multi selection modes. <br>
15854 * Create a data model bound view:
15856 var store = new Roo.data.Store(...);
15858 var view = new Roo.View({
15860 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15862 singleSelect: true,
15863 selectedClass: "ydataview-selected",
15867 // listen for node click?
15868 view.on("click", function(vw, index, node, e){
15869 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15873 dataModel.load("foobar.xml");
15875 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15877 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15878 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15880 * Note: old style constructor is still suported (container, template, config)
15883 * Create a new View
15884 * @param {Object} config The config object
15887 Roo.View = function(config, depreciated_tpl, depreciated_config){
15889 this.parent = false;
15891 if (typeof(depreciated_tpl) == 'undefined') {
15892 // new way.. - universal constructor.
15893 Roo.apply(this, config);
15894 this.el = Roo.get(this.el);
15897 this.el = Roo.get(config);
15898 this.tpl = depreciated_tpl;
15899 Roo.apply(this, depreciated_config);
15901 this.wrapEl = this.el.wrap().wrap();
15902 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15905 if(typeof(this.tpl) == "string"){
15906 this.tpl = new Roo.Template(this.tpl);
15908 // support xtype ctors..
15909 this.tpl = new Roo.factory(this.tpl, Roo);
15913 this.tpl.compile();
15918 * @event beforeclick
15919 * Fires before a click is processed. Returns false to cancel the default action.
15920 * @param {Roo.View} this
15921 * @param {Number} index The index of the target node
15922 * @param {HTMLElement} node The target node
15923 * @param {Roo.EventObject} e The raw event object
15925 "beforeclick" : true,
15928 * Fires when a template node is clicked.
15929 * @param {Roo.View} this
15930 * @param {Number} index The index of the target node
15931 * @param {HTMLElement} node The target node
15932 * @param {Roo.EventObject} e The raw event object
15937 * Fires when a template node is double clicked.
15938 * @param {Roo.View} this
15939 * @param {Number} index The index of the target node
15940 * @param {HTMLElement} node The target node
15941 * @param {Roo.EventObject} e The raw event object
15945 * @event contextmenu
15946 * Fires when a template node is right clicked.
15947 * @param {Roo.View} this
15948 * @param {Number} index The index of the target node
15949 * @param {HTMLElement} node The target node
15950 * @param {Roo.EventObject} e The raw event object
15952 "contextmenu" : true,
15954 * @event selectionchange
15955 * Fires when the selected nodes change.
15956 * @param {Roo.View} this
15957 * @param {Array} selections Array of the selected nodes
15959 "selectionchange" : true,
15962 * @event beforeselect
15963 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15964 * @param {Roo.View} this
15965 * @param {HTMLElement} node The node to be selected
15966 * @param {Array} selections Array of currently selected nodes
15968 "beforeselect" : true,
15970 * @event preparedata
15971 * Fires on every row to render, to allow you to change the data.
15972 * @param {Roo.View} this
15973 * @param {Object} data to be rendered (change this)
15975 "preparedata" : true
15983 "click": this.onClick,
15984 "dblclick": this.onDblClick,
15985 "contextmenu": this.onContextMenu,
15989 this.selections = [];
15991 this.cmp = new Roo.CompositeElementLite([]);
15993 this.store = Roo.factory(this.store, Roo.data);
15994 this.setStore(this.store, true);
15997 if ( this.footer && this.footer.xtype) {
15999 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16001 this.footer.dataSource = this.store;
16002 this.footer.container = fctr;
16003 this.footer = Roo.factory(this.footer, Roo);
16004 fctr.insertFirst(this.el);
16006 // this is a bit insane - as the paging toolbar seems to detach the el..
16007 // dom.parentNode.parentNode.parentNode
16008 // they get detached?
16012 Roo.View.superclass.constructor.call(this);
16017 Roo.extend(Roo.View, Roo.util.Observable, {
16020 * @cfg {Roo.data.Store} store Data store to load data from.
16025 * @cfg {String|Roo.Element} el The container element.
16030 * @cfg {String|Roo.Template} tpl The template used by this View
16034 * @cfg {String} dataName the named area of the template to use as the data area
16035 * Works with domtemplates roo-name="name"
16039 * @cfg {String} selectedClass The css class to add to selected nodes
16041 selectedClass : "x-view-selected",
16043 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16048 * @cfg {String} text to display on mask (default Loading)
16052 * @cfg {Boolean} multiSelect Allow multiple selection
16054 multiSelect : false,
16056 * @cfg {Boolean} singleSelect Allow single selection
16058 singleSelect: false,
16061 * @cfg {Boolean} toggleSelect - selecting
16063 toggleSelect : false,
16066 * @cfg {Boolean} tickable - selecting
16071 * Returns the element this view is bound to.
16072 * @return {Roo.Element}
16074 getEl : function(){
16075 return this.wrapEl;
16081 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16083 refresh : function(){
16084 //Roo.log('refresh');
16087 // if we are using something like 'domtemplate', then
16088 // the what gets used is:
16089 // t.applySubtemplate(NAME, data, wrapping data..)
16090 // the outer template then get' applied with
16091 // the store 'extra data'
16092 // and the body get's added to the
16093 // roo-name="data" node?
16094 // <span class='roo-tpl-{name}'></span> ?????
16098 this.clearSelections();
16099 this.el.update("");
16101 var records = this.store.getRange();
16102 if(records.length < 1) {
16104 // is this valid?? = should it render a template??
16106 this.el.update(this.emptyText);
16110 if (this.dataName) {
16111 this.el.update(t.apply(this.store.meta)); //????
16112 el = this.el.child('.roo-tpl-' + this.dataName);
16115 for(var i = 0, len = records.length; i < len; i++){
16116 var data = this.prepareData(records[i].data, i, records[i]);
16117 this.fireEvent("preparedata", this, data, i, records[i]);
16119 var d = Roo.apply({}, data);
16122 Roo.apply(d, {'roo-id' : Roo.id()});
16126 Roo.each(this.parent.item, function(item){
16127 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16130 Roo.apply(d, {'roo-data-checked' : 'checked'});
16134 html[html.length] = Roo.util.Format.trim(
16136 t.applySubtemplate(this.dataName, d, this.store.meta) :
16143 el.update(html.join(""));
16144 this.nodes = el.dom.childNodes;
16145 this.updateIndexes(0);
16150 * Function to override to reformat the data that is sent to
16151 * the template for each node.
16152 * DEPRICATED - use the preparedata event handler.
16153 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16154 * a JSON object for an UpdateManager bound view).
16156 prepareData : function(data, index, record)
16158 this.fireEvent("preparedata", this, data, index, record);
16162 onUpdate : function(ds, record){
16163 // Roo.log('on update');
16164 this.clearSelections();
16165 var index = this.store.indexOf(record);
16166 var n = this.nodes[index];
16167 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16168 n.parentNode.removeChild(n);
16169 this.updateIndexes(index, index);
16175 onAdd : function(ds, records, index)
16177 //Roo.log(['on Add', ds, records, index] );
16178 this.clearSelections();
16179 if(this.nodes.length == 0){
16183 var n = this.nodes[index];
16184 for(var i = 0, len = records.length; i < len; i++){
16185 var d = this.prepareData(records[i].data, i, records[i]);
16187 this.tpl.insertBefore(n, d);
16190 this.tpl.append(this.el, d);
16193 this.updateIndexes(index);
16196 onRemove : function(ds, record, index){
16197 // Roo.log('onRemove');
16198 this.clearSelections();
16199 var el = this.dataName ?
16200 this.el.child('.roo-tpl-' + this.dataName) :
16203 el.dom.removeChild(this.nodes[index]);
16204 this.updateIndexes(index);
16208 * Refresh an individual node.
16209 * @param {Number} index
16211 refreshNode : function(index){
16212 this.onUpdate(this.store, this.store.getAt(index));
16215 updateIndexes : function(startIndex, endIndex){
16216 var ns = this.nodes;
16217 startIndex = startIndex || 0;
16218 endIndex = endIndex || ns.length - 1;
16219 for(var i = startIndex; i <= endIndex; i++){
16220 ns[i].nodeIndex = i;
16225 * Changes the data store this view uses and refresh the view.
16226 * @param {Store} store
16228 setStore : function(store, initial){
16229 if(!initial && this.store){
16230 this.store.un("datachanged", this.refresh);
16231 this.store.un("add", this.onAdd);
16232 this.store.un("remove", this.onRemove);
16233 this.store.un("update", this.onUpdate);
16234 this.store.un("clear", this.refresh);
16235 this.store.un("beforeload", this.onBeforeLoad);
16236 this.store.un("load", this.onLoad);
16237 this.store.un("loadexception", this.onLoad);
16241 store.on("datachanged", this.refresh, this);
16242 store.on("add", this.onAdd, this);
16243 store.on("remove", this.onRemove, this);
16244 store.on("update", this.onUpdate, this);
16245 store.on("clear", this.refresh, this);
16246 store.on("beforeload", this.onBeforeLoad, this);
16247 store.on("load", this.onLoad, this);
16248 store.on("loadexception", this.onLoad, this);
16256 * onbeforeLoad - masks the loading area.
16259 onBeforeLoad : function(store,opts)
16261 //Roo.log('onBeforeLoad');
16263 this.el.update("");
16265 this.el.mask(this.mask ? this.mask : "Loading" );
16267 onLoad : function ()
16274 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16275 * @param {HTMLElement} node
16276 * @return {HTMLElement} The template node
16278 findItemFromChild : function(node){
16279 var el = this.dataName ?
16280 this.el.child('.roo-tpl-' + this.dataName,true) :
16283 if(!node || node.parentNode == el){
16286 var p = node.parentNode;
16287 while(p && p != el){
16288 if(p.parentNode == el){
16297 onClick : function(e){
16298 var item = this.findItemFromChild(e.getTarget());
16300 var index = this.indexOf(item);
16301 if(this.onItemClick(item, index, e) !== false){
16302 this.fireEvent("click", this, index, item, e);
16305 this.clearSelections();
16310 onContextMenu : function(e){
16311 var item = this.findItemFromChild(e.getTarget());
16313 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16318 onDblClick : function(e){
16319 var item = this.findItemFromChild(e.getTarget());
16321 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16325 onItemClick : function(item, index, e)
16327 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16330 if (this.toggleSelect) {
16331 var m = this.isSelected(item) ? 'unselect' : 'select';
16334 _t[m](item, true, false);
16337 if(this.multiSelect || this.singleSelect){
16338 if(this.multiSelect && e.shiftKey && this.lastSelection){
16339 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16341 this.select(item, this.multiSelect && e.ctrlKey);
16342 this.lastSelection = item;
16345 if(!this.tickable){
16346 e.preventDefault();
16354 * Get the number of selected nodes.
16357 getSelectionCount : function(){
16358 return this.selections.length;
16362 * Get the currently selected nodes.
16363 * @return {Array} An array of HTMLElements
16365 getSelectedNodes : function(){
16366 return this.selections;
16370 * Get the indexes of the selected nodes.
16373 getSelectedIndexes : function(){
16374 var indexes = [], s = this.selections;
16375 for(var i = 0, len = s.length; i < len; i++){
16376 indexes.push(s[i].nodeIndex);
16382 * Clear all selections
16383 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16385 clearSelections : function(suppressEvent){
16386 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16387 this.cmp.elements = this.selections;
16388 this.cmp.removeClass(this.selectedClass);
16389 this.selections = [];
16390 if(!suppressEvent){
16391 this.fireEvent("selectionchange", this, this.selections);
16397 * Returns true if the passed node is selected
16398 * @param {HTMLElement/Number} node The node or node index
16399 * @return {Boolean}
16401 isSelected : function(node){
16402 var s = this.selections;
16406 node = this.getNode(node);
16407 return s.indexOf(node) !== -1;
16412 * @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
16413 * @param {Boolean} keepExisting (optional) true to keep existing selections
16414 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16416 select : function(nodeInfo, keepExisting, suppressEvent){
16417 if(nodeInfo instanceof Array){
16419 this.clearSelections(true);
16421 for(var i = 0, len = nodeInfo.length; i < len; i++){
16422 this.select(nodeInfo[i], true, true);
16426 var node = this.getNode(nodeInfo);
16427 if(!node || this.isSelected(node)){
16428 return; // already selected.
16431 this.clearSelections(true);
16434 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16435 Roo.fly(node).addClass(this.selectedClass);
16436 this.selections.push(node);
16437 if(!suppressEvent){
16438 this.fireEvent("selectionchange", this, this.selections);
16446 * @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
16447 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16448 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16450 unselect : function(nodeInfo, keepExisting, suppressEvent)
16452 if(nodeInfo instanceof Array){
16453 Roo.each(this.selections, function(s) {
16454 this.unselect(s, nodeInfo);
16458 var node = this.getNode(nodeInfo);
16459 if(!node || !this.isSelected(node)){
16460 //Roo.log("not selected");
16461 return; // not selected.
16465 Roo.each(this.selections, function(s) {
16467 Roo.fly(node).removeClass(this.selectedClass);
16474 this.selections= ns;
16475 this.fireEvent("selectionchange", this, this.selections);
16479 * Gets a template node.
16480 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16481 * @return {HTMLElement} The node or null if it wasn't found
16483 getNode : function(nodeInfo){
16484 if(typeof nodeInfo == "string"){
16485 return document.getElementById(nodeInfo);
16486 }else if(typeof nodeInfo == "number"){
16487 return this.nodes[nodeInfo];
16493 * Gets a range template nodes.
16494 * @param {Number} startIndex
16495 * @param {Number} endIndex
16496 * @return {Array} An array of nodes
16498 getNodes : function(start, end){
16499 var ns = this.nodes;
16500 start = start || 0;
16501 end = typeof end == "undefined" ? ns.length - 1 : end;
16504 for(var i = start; i <= end; i++){
16508 for(var i = start; i >= end; i--){
16516 * Finds the index of the passed node
16517 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16518 * @return {Number} The index of the node or -1
16520 indexOf : function(node){
16521 node = this.getNode(node);
16522 if(typeof node.nodeIndex == "number"){
16523 return node.nodeIndex;
16525 var ns = this.nodes;
16526 for(var i = 0, len = ns.length; i < len; i++){
16537 * based on jquery fullcalendar
16541 Roo.bootstrap = Roo.bootstrap || {};
16543 * @class Roo.bootstrap.Calendar
16544 * @extends Roo.bootstrap.Component
16545 * Bootstrap Calendar class
16546 * @cfg {Boolean} loadMask (true|false) default false
16547 * @cfg {Object} header generate the user specific header of the calendar, default false
16550 * Create a new Container
16551 * @param {Object} config The config object
16556 Roo.bootstrap.Calendar = function(config){
16557 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16561 * Fires when a date is selected
16562 * @param {DatePicker} this
16563 * @param {Date} date The selected date
16567 * @event monthchange
16568 * Fires when the displayed month changes
16569 * @param {DatePicker} this
16570 * @param {Date} date The selected month
16572 'monthchange': true,
16574 * @event evententer
16575 * Fires when mouse over an event
16576 * @param {Calendar} this
16577 * @param {event} Event
16579 'evententer': true,
16581 * @event eventleave
16582 * Fires when the mouse leaves an
16583 * @param {Calendar} this
16586 'eventleave': true,
16588 * @event eventclick
16589 * Fires when the mouse click an
16590 * @param {Calendar} this
16599 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16602 * @cfg {Number} startDay
16603 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16611 getAutoCreate : function(){
16614 var fc_button = function(name, corner, style, content ) {
16615 return Roo.apply({},{
16617 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16619 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16622 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16633 style : 'width:100%',
16640 cls : 'fc-header-left',
16642 fc_button('prev', 'left', 'arrow', '‹' ),
16643 fc_button('next', 'right', 'arrow', '›' ),
16644 { tag: 'span', cls: 'fc-header-space' },
16645 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16653 cls : 'fc-header-center',
16657 cls: 'fc-header-title',
16660 html : 'month / year'
16668 cls : 'fc-header-right',
16670 /* fc_button('month', 'left', '', 'month' ),
16671 fc_button('week', '', '', 'week' ),
16672 fc_button('day', 'right', '', 'day' )
16684 header = this.header;
16687 var cal_heads = function() {
16689 // fixme - handle this.
16691 for (var i =0; i < Date.dayNames.length; i++) {
16692 var d = Date.dayNames[i];
16695 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16696 html : d.substring(0,3)
16700 ret[0].cls += ' fc-first';
16701 ret[6].cls += ' fc-last';
16704 var cal_cell = function(n) {
16707 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16712 cls: 'fc-day-number',
16716 cls: 'fc-day-content',
16720 style: 'position: relative;' // height: 17px;
16732 var cal_rows = function() {
16735 for (var r = 0; r < 6; r++) {
16742 for (var i =0; i < Date.dayNames.length; i++) {
16743 var d = Date.dayNames[i];
16744 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16747 row.cn[0].cls+=' fc-first';
16748 row.cn[0].cn[0].style = 'min-height:90px';
16749 row.cn[6].cls+=' fc-last';
16753 ret[0].cls += ' fc-first';
16754 ret[4].cls += ' fc-prev-last';
16755 ret[5].cls += ' fc-last';
16762 cls: 'fc-border-separate',
16763 style : 'width:100%',
16771 cls : 'fc-first fc-last',
16789 cls : 'fc-content',
16790 style : "position: relative;",
16793 cls : 'fc-view fc-view-month fc-grid',
16794 style : 'position: relative',
16795 unselectable : 'on',
16798 cls : 'fc-event-container',
16799 style : 'position:absolute;z-index:8;top:0;left:0;'
16817 initEvents : function()
16820 throw "can not find store for calendar";
16826 style: "text-align:center",
16830 style: "background-color:white;width:50%;margin:250 auto",
16834 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16845 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16847 var size = this.el.select('.fc-content', true).first().getSize();
16848 this.maskEl.setSize(size.width, size.height);
16849 this.maskEl.enableDisplayMode("block");
16850 if(!this.loadMask){
16851 this.maskEl.hide();
16854 this.store = Roo.factory(this.store, Roo.data);
16855 this.store.on('load', this.onLoad, this);
16856 this.store.on('beforeload', this.onBeforeLoad, this);
16860 this.cells = this.el.select('.fc-day',true);
16861 //Roo.log(this.cells);
16862 this.textNodes = this.el.query('.fc-day-number');
16863 this.cells.addClassOnOver('fc-state-hover');
16865 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16866 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16867 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16868 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16870 this.on('monthchange', this.onMonthChange, this);
16872 this.update(new Date().clearTime());
16875 resize : function() {
16876 var sz = this.el.getSize();
16878 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16879 this.el.select('.fc-day-content div',true).setHeight(34);
16884 showPrevMonth : function(e){
16885 this.update(this.activeDate.add("mo", -1));
16887 showToday : function(e){
16888 this.update(new Date().clearTime());
16891 showNextMonth : function(e){
16892 this.update(this.activeDate.add("mo", 1));
16896 showPrevYear : function(){
16897 this.update(this.activeDate.add("y", -1));
16901 showNextYear : function(){
16902 this.update(this.activeDate.add("y", 1));
16907 update : function(date)
16909 var vd = this.activeDate;
16910 this.activeDate = date;
16911 // if(vd && this.el){
16912 // var t = date.getTime();
16913 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16914 // Roo.log('using add remove');
16916 // this.fireEvent('monthchange', this, date);
16918 // this.cells.removeClass("fc-state-highlight");
16919 // this.cells.each(function(c){
16920 // if(c.dateValue == t){
16921 // c.addClass("fc-state-highlight");
16922 // setTimeout(function(){
16923 // try{c.dom.firstChild.focus();}catch(e){}
16933 var days = date.getDaysInMonth();
16935 var firstOfMonth = date.getFirstDateOfMonth();
16936 var startingPos = firstOfMonth.getDay()-this.startDay;
16938 if(startingPos < this.startDay){
16942 var pm = date.add(Date.MONTH, -1);
16943 var prevStart = pm.getDaysInMonth()-startingPos;
16945 this.cells = this.el.select('.fc-day',true);
16946 this.textNodes = this.el.query('.fc-day-number');
16947 this.cells.addClassOnOver('fc-state-hover');
16949 var cells = this.cells.elements;
16950 var textEls = this.textNodes;
16952 Roo.each(cells, function(cell){
16953 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16956 days += startingPos;
16958 // convert everything to numbers so it's fast
16959 var day = 86400000;
16960 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16963 //Roo.log(prevStart);
16965 var today = new Date().clearTime().getTime();
16966 var sel = date.clearTime().getTime();
16967 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16968 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16969 var ddMatch = this.disabledDatesRE;
16970 var ddText = this.disabledDatesText;
16971 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16972 var ddaysText = this.disabledDaysText;
16973 var format = this.format;
16975 var setCellClass = function(cal, cell){
16979 //Roo.log('set Cell Class');
16981 var t = d.getTime();
16985 cell.dateValue = t;
16987 cell.className += " fc-today";
16988 cell.className += " fc-state-highlight";
16989 cell.title = cal.todayText;
16992 // disable highlight in other month..
16993 //cell.className += " fc-state-highlight";
16998 cell.className = " fc-state-disabled";
16999 cell.title = cal.minText;
17003 cell.className = " fc-state-disabled";
17004 cell.title = cal.maxText;
17008 if(ddays.indexOf(d.getDay()) != -1){
17009 cell.title = ddaysText;
17010 cell.className = " fc-state-disabled";
17013 if(ddMatch && format){
17014 var fvalue = d.dateFormat(format);
17015 if(ddMatch.test(fvalue)){
17016 cell.title = ddText.replace("%0", fvalue);
17017 cell.className = " fc-state-disabled";
17021 if (!cell.initialClassName) {
17022 cell.initialClassName = cell.dom.className;
17025 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17030 for(; i < startingPos; i++) {
17031 textEls[i].innerHTML = (++prevStart);
17032 d.setDate(d.getDate()+1);
17034 cells[i].className = "fc-past fc-other-month";
17035 setCellClass(this, cells[i]);
17040 for(; i < days; i++){
17041 intDay = i - startingPos + 1;
17042 textEls[i].innerHTML = (intDay);
17043 d.setDate(d.getDate()+1);
17045 cells[i].className = ''; // "x-date-active";
17046 setCellClass(this, cells[i]);
17050 for(; i < 42; i++) {
17051 textEls[i].innerHTML = (++extraDays);
17052 d.setDate(d.getDate()+1);
17054 cells[i].className = "fc-future fc-other-month";
17055 setCellClass(this, cells[i]);
17058 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17060 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17062 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17063 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17065 if(totalRows != 6){
17066 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17067 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17070 this.fireEvent('monthchange', this, date);
17074 if(!this.internalRender){
17075 var main = this.el.dom.firstChild;
17076 var w = main.offsetWidth;
17077 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17078 Roo.fly(main).setWidth(w);
17079 this.internalRender = true;
17080 // opera does not respect the auto grow header center column
17081 // then, after it gets a width opera refuses to recalculate
17082 // without a second pass
17083 if(Roo.isOpera && !this.secondPass){
17084 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17085 this.secondPass = true;
17086 this.update.defer(10, this, [date]);
17093 findCell : function(dt) {
17094 dt = dt.clearTime().getTime();
17096 this.cells.each(function(c){
17097 //Roo.log("check " +c.dateValue + '?=' + dt);
17098 if(c.dateValue == dt){
17108 findCells : function(ev) {
17109 var s = ev.start.clone().clearTime().getTime();
17111 var e= ev.end.clone().clearTime().getTime();
17114 this.cells.each(function(c){
17115 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17117 if(c.dateValue > e){
17120 if(c.dateValue < s){
17129 // findBestRow: function(cells)
17133 // for (var i =0 ; i < cells.length;i++) {
17134 // ret = Math.max(cells[i].rows || 0,ret);
17141 addItem : function(ev)
17143 // look for vertical location slot in
17144 var cells = this.findCells(ev);
17146 // ev.row = this.findBestRow(cells);
17148 // work out the location.
17152 for(var i =0; i < cells.length; i++) {
17154 cells[i].row = cells[0].row;
17157 cells[i].row = cells[i].row + 1;
17167 if (crow.start.getY() == cells[i].getY()) {
17169 crow.end = cells[i];
17186 cells[0].events.push(ev);
17188 this.calevents.push(ev);
17191 clearEvents: function() {
17193 if(!this.calevents){
17197 Roo.each(this.cells.elements, function(c){
17203 Roo.each(this.calevents, function(e) {
17204 Roo.each(e.els, function(el) {
17205 el.un('mouseenter' ,this.onEventEnter, this);
17206 el.un('mouseleave' ,this.onEventLeave, this);
17211 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17217 renderEvents: function()
17221 this.cells.each(function(c) {
17230 if(c.row != c.events.length){
17231 r = 4 - (4 - (c.row - c.events.length));
17234 c.events = ev.slice(0, r);
17235 c.more = ev.slice(r);
17237 if(c.more.length && c.more.length == 1){
17238 c.events.push(c.more.pop());
17241 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17245 this.cells.each(function(c) {
17247 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17250 for (var e = 0; e < c.events.length; e++){
17251 var ev = c.events[e];
17252 var rows = ev.rows;
17254 for(var i = 0; i < rows.length; i++) {
17256 // how many rows should it span..
17259 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17260 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17262 unselectable : "on",
17265 cls: 'fc-event-inner',
17269 // cls: 'fc-event-time',
17270 // html : cells.length > 1 ? '' : ev.time
17274 cls: 'fc-event-title',
17275 html : String.format('{0}', ev.title)
17282 cls: 'ui-resizable-handle ui-resizable-e',
17283 html : '  '
17290 cfg.cls += ' fc-event-start';
17292 if ((i+1) == rows.length) {
17293 cfg.cls += ' fc-event-end';
17296 var ctr = _this.el.select('.fc-event-container',true).first();
17297 var cg = ctr.createChild(cfg);
17299 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17300 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17302 var r = (c.more.length) ? 1 : 0;
17303 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17304 cg.setWidth(ebox.right - sbox.x -2);
17306 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17307 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17308 cg.on('click', _this.onEventClick, _this, ev);
17319 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17320 style : 'position: absolute',
17321 unselectable : "on",
17324 cls: 'fc-event-inner',
17328 cls: 'fc-event-title',
17336 cls: 'ui-resizable-handle ui-resizable-e',
17337 html : '  '
17343 var ctr = _this.el.select('.fc-event-container',true).first();
17344 var cg = ctr.createChild(cfg);
17346 var sbox = c.select('.fc-day-content',true).first().getBox();
17347 var ebox = c.select('.fc-day-content',true).first().getBox();
17349 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17350 cg.setWidth(ebox.right - sbox.x -2);
17352 cg.on('click', _this.onMoreEventClick, _this, c.more);
17362 onEventEnter: function (e, el,event,d) {
17363 this.fireEvent('evententer', this, el, event);
17366 onEventLeave: function (e, el,event,d) {
17367 this.fireEvent('eventleave', this, el, event);
17370 onEventClick: function (e, el,event,d) {
17371 this.fireEvent('eventclick', this, el, event);
17374 onMonthChange: function () {
17378 onMoreEventClick: function(e, el, more)
17382 this.calpopover.placement = 'right';
17383 this.calpopover.setTitle('More');
17385 this.calpopover.setContent('');
17387 var ctr = this.calpopover.el.select('.popover-content', true).first();
17389 Roo.each(more, function(m){
17391 cls : 'fc-event-hori fc-event-draggable',
17394 var cg = ctr.createChild(cfg);
17396 cg.on('click', _this.onEventClick, _this, m);
17399 this.calpopover.show(el);
17404 onLoad: function ()
17406 this.calevents = [];
17409 if(this.store.getCount() > 0){
17410 this.store.data.each(function(d){
17413 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17414 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17415 time : d.data.start_time,
17416 title : d.data.title,
17417 description : d.data.description,
17418 venue : d.data.venue
17423 this.renderEvents();
17425 if(this.calevents.length && this.loadMask){
17426 this.maskEl.hide();
17430 onBeforeLoad: function()
17432 this.clearEvents();
17434 this.maskEl.show();
17448 * @class Roo.bootstrap.Popover
17449 * @extends Roo.bootstrap.Component
17450 * Bootstrap Popover class
17451 * @cfg {String} html contents of the popover (or false to use children..)
17452 * @cfg {String} title of popover (or false to hide)
17453 * @cfg {String} placement how it is placed
17454 * @cfg {String} trigger click || hover (or false to trigger manually)
17455 * @cfg {String} over what (parent or false to trigger manually.)
17456 * @cfg {Number} delay - delay before showing
17459 * Create a new Popover
17460 * @param {Object} config The config object
17463 Roo.bootstrap.Popover = function(config){
17464 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17470 * After the popover show
17472 * @param {Roo.bootstrap.Popover} this
17477 * After the popover hide
17479 * @param {Roo.bootstrap.Popover} this
17485 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17487 title: 'Fill in a title',
17490 placement : 'right',
17491 trigger : 'hover', // hover
17497 can_build_overlaid : false,
17499 getChildContainer : function()
17501 return this.el.select('.popover-content',true).first();
17504 getAutoCreate : function(){
17507 cls : 'popover roo-dynamic',
17508 style: 'display:block',
17514 cls : 'popover-inner',
17518 cls: 'popover-title',
17522 cls : 'popover-content',
17533 setTitle: function(str)
17536 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17538 setContent: function(str)
17541 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17543 // as it get's added to the bottom of the page.
17544 onRender : function(ct, position)
17546 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17548 var cfg = Roo.apply({}, this.getAutoCreate());
17552 cfg.cls += ' ' + this.cls;
17555 cfg.style = this.style;
17557 //Roo.log("adding to ");
17558 this.el = Roo.get(document.body).createChild(cfg, position);
17559 // Roo.log(this.el);
17564 initEvents : function()
17566 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17567 this.el.enableDisplayMode('block');
17569 if (this.over === false) {
17572 if (this.triggers === false) {
17575 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17576 var triggers = this.trigger ? this.trigger.split(' ') : [];
17577 Roo.each(triggers, function(trigger) {
17579 if (trigger == 'click') {
17580 on_el.on('click', this.toggle, this);
17581 } else if (trigger != 'manual') {
17582 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17583 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17585 on_el.on(eventIn ,this.enter, this);
17586 on_el.on(eventOut, this.leave, this);
17597 toggle : function () {
17598 this.hoverState == 'in' ? this.leave() : this.enter();
17601 enter : function () {
17603 clearTimeout(this.timeout);
17605 this.hoverState = 'in';
17607 if (!this.delay || !this.delay.show) {
17612 this.timeout = setTimeout(function () {
17613 if (_t.hoverState == 'in') {
17616 }, this.delay.show)
17619 leave : function() {
17620 clearTimeout(this.timeout);
17622 this.hoverState = 'out';
17624 if (!this.delay || !this.delay.hide) {
17629 this.timeout = setTimeout(function () {
17630 if (_t.hoverState == 'out') {
17633 }, this.delay.hide)
17636 show : function (on_el)
17639 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17643 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17644 if (this.html !== false) {
17645 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17647 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17648 if (!this.title.length) {
17649 this.el.select('.popover-title',true).hide();
17652 var placement = typeof this.placement == 'function' ?
17653 this.placement.call(this, this.el, on_el) :
17656 var autoToken = /\s?auto?\s?/i;
17657 var autoPlace = autoToken.test(placement);
17659 placement = placement.replace(autoToken, '') || 'top';
17663 //this.el.setXY([0,0]);
17665 this.el.dom.style.display='block';
17666 this.el.addClass(placement);
17668 //this.el.appendTo(on_el);
17670 var p = this.getPosition();
17671 var box = this.el.getBox();
17676 var align = Roo.bootstrap.Popover.alignment[placement];
17679 this.el.alignTo(on_el, align[0],align[1]);
17680 //var arrow = this.el.select('.arrow',true).first();
17681 //arrow.set(align[2],
17683 this.el.addClass('in');
17686 if (this.el.hasClass('fade')) {
17690 this.hoverState = 'in';
17692 this.fireEvent('show', this);
17697 this.el.setXY([0,0]);
17698 this.el.removeClass('in');
17700 this.hoverState = null;
17702 this.fireEvent('hide', this);
17707 Roo.bootstrap.Popover.alignment = {
17708 'left' : ['r-l', [-10,0], 'right'],
17709 'right' : ['l-r', [10,0], 'left'],
17710 'bottom' : ['t-b', [0,10], 'top'],
17711 'top' : [ 'b-t', [0,-10], 'bottom']
17722 * @class Roo.bootstrap.Progress
17723 * @extends Roo.bootstrap.Component
17724 * Bootstrap Progress class
17725 * @cfg {Boolean} striped striped of the progress bar
17726 * @cfg {Boolean} active animated of the progress bar
17730 * Create a new Progress
17731 * @param {Object} config The config object
17734 Roo.bootstrap.Progress = function(config){
17735 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17738 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17743 getAutoCreate : function(){
17751 cfg.cls += ' progress-striped';
17755 cfg.cls += ' active';
17774 * @class Roo.bootstrap.ProgressBar
17775 * @extends Roo.bootstrap.Component
17776 * Bootstrap ProgressBar class
17777 * @cfg {Number} aria_valuenow aria-value now
17778 * @cfg {Number} aria_valuemin aria-value min
17779 * @cfg {Number} aria_valuemax aria-value max
17780 * @cfg {String} label label for the progress bar
17781 * @cfg {String} panel (success | info | warning | danger )
17782 * @cfg {String} role role of the progress bar
17783 * @cfg {String} sr_only text
17787 * Create a new ProgressBar
17788 * @param {Object} config The config object
17791 Roo.bootstrap.ProgressBar = function(config){
17792 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17795 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17799 aria_valuemax : 100,
17805 getAutoCreate : function()
17810 cls: 'progress-bar',
17811 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17823 cfg.role = this.role;
17826 if(this.aria_valuenow){
17827 cfg['aria-valuenow'] = this.aria_valuenow;
17830 if(this.aria_valuemin){
17831 cfg['aria-valuemin'] = this.aria_valuemin;
17834 if(this.aria_valuemax){
17835 cfg['aria-valuemax'] = this.aria_valuemax;
17838 if(this.label && !this.sr_only){
17839 cfg.html = this.label;
17843 cfg.cls += ' progress-bar-' + this.panel;
17849 update : function(aria_valuenow)
17851 this.aria_valuenow = aria_valuenow;
17853 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17868 * @class Roo.bootstrap.TabGroup
17869 * @extends Roo.bootstrap.Column
17870 * Bootstrap Column class
17871 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17872 * @cfg {Boolean} carousel true to make the group behave like a carousel
17873 * @cfg {Boolean} bullets show bullets for the panels
17874 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17875 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17876 * @cfg {Boolean} showarrow (true|false) show arrow default true
17879 * Create a new TabGroup
17880 * @param {Object} config The config object
17883 Roo.bootstrap.TabGroup = function(config){
17884 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17886 this.navId = Roo.id();
17889 Roo.bootstrap.TabGroup.register(this);
17893 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17896 transition : false,
17901 slideOnTouch : false,
17904 getAutoCreate : function()
17906 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17908 cfg.cls += ' tab-content';
17910 if (this.carousel) {
17911 cfg.cls += ' carousel slide';
17914 cls : 'carousel-inner',
17918 if(this.bullets && !Roo.isTouch){
17921 cls : 'carousel-bullets',
17925 if(this.bullets_cls){
17926 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17933 cfg.cn[0].cn.push(bullets);
17936 if(this.showarrow){
17937 cfg.cn[0].cn.push({
17939 class : 'carousel-arrow',
17943 class : 'carousel-prev',
17947 class : 'fa fa-chevron-left'
17953 class : 'carousel-next',
17957 class : 'fa fa-chevron-right'
17970 initEvents: function()
17972 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17973 // this.el.on("touchstart", this.onTouchStart, this);
17976 if(this.autoslide){
17979 this.slideFn = window.setInterval(function() {
17980 _this.showPanelNext();
17984 if(this.showarrow){
17985 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17986 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17992 // onTouchStart : function(e, el, o)
17994 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17998 // this.showPanelNext();
18002 getChildContainer : function()
18004 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18008 * register a Navigation item
18009 * @param {Roo.bootstrap.NavItem} the navitem to add
18011 register : function(item)
18013 this.tabs.push( item);
18014 item.navId = this.navId; // not really needed..
18019 getActivePanel : function()
18022 Roo.each(this.tabs, function(t) {
18032 getPanelByName : function(n)
18035 Roo.each(this.tabs, function(t) {
18036 if (t.tabId == n) {
18044 indexOfPanel : function(p)
18047 Roo.each(this.tabs, function(t,i) {
18048 if (t.tabId == p.tabId) {
18057 * show a specific panel
18058 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18059 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18061 showPanel : function (pan)
18063 if(this.transition || typeof(pan) == 'undefined'){
18064 Roo.log("waiting for the transitionend");
18068 if (typeof(pan) == 'number') {
18069 pan = this.tabs[pan];
18072 if (typeof(pan) == 'string') {
18073 pan = this.getPanelByName(pan);
18076 var cur = this.getActivePanel();
18079 Roo.log('pan or acitve pan is undefined');
18083 if (pan.tabId == this.getActivePanel().tabId) {
18087 if (false === cur.fireEvent('beforedeactivate')) {
18091 if(this.bullets > 0 && !Roo.isTouch){
18092 this.setActiveBullet(this.indexOfPanel(pan));
18095 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18097 this.transition = true;
18098 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18099 var lr = dir == 'next' ? 'left' : 'right';
18100 pan.el.addClass(dir); // or prev
18101 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18102 cur.el.addClass(lr); // or right
18103 pan.el.addClass(lr);
18106 cur.el.on('transitionend', function() {
18107 Roo.log("trans end?");
18109 pan.el.removeClass([lr,dir]);
18110 pan.setActive(true);
18112 cur.el.removeClass([lr]);
18113 cur.setActive(false);
18115 _this.transition = false;
18117 }, this, { single: true } );
18122 cur.setActive(false);
18123 pan.setActive(true);
18128 showPanelNext : function()
18130 var i = this.indexOfPanel(this.getActivePanel());
18132 if (i >= this.tabs.length - 1 && !this.autoslide) {
18136 if (i >= this.tabs.length - 1 && this.autoslide) {
18140 this.showPanel(this.tabs[i+1]);
18143 showPanelPrev : function()
18145 var i = this.indexOfPanel(this.getActivePanel());
18147 if (i < 1 && !this.autoslide) {
18151 if (i < 1 && this.autoslide) {
18152 i = this.tabs.length;
18155 this.showPanel(this.tabs[i-1]);
18159 addBullet: function()
18161 if(!this.bullets || Roo.isTouch){
18164 var ctr = this.el.select('.carousel-bullets',true).first();
18165 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18166 var bullet = ctr.createChild({
18167 cls : 'bullet bullet-' + i
18168 },ctr.dom.lastChild);
18173 bullet.on('click', (function(e, el, o, ii, t){
18175 e.preventDefault();
18177 this.showPanel(ii);
18179 if(this.autoslide && this.slideFn){
18180 clearInterval(this.slideFn);
18181 this.slideFn = window.setInterval(function() {
18182 _this.showPanelNext();
18186 }).createDelegate(this, [i, bullet], true));
18191 setActiveBullet : function(i)
18197 Roo.each(this.el.select('.bullet', true).elements, function(el){
18198 el.removeClass('selected');
18201 var bullet = this.el.select('.bullet-' + i, true).first();
18207 bullet.addClass('selected');
18218 Roo.apply(Roo.bootstrap.TabGroup, {
18222 * register a Navigation Group
18223 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18225 register : function(navgrp)
18227 this.groups[navgrp.navId] = navgrp;
18231 * fetch a Navigation Group based on the navigation ID
18232 * if one does not exist , it will get created.
18233 * @param {string} the navgroup to add
18234 * @returns {Roo.bootstrap.NavGroup} the navgroup
18236 get: function(navId) {
18237 if (typeof(this.groups[navId]) == 'undefined') {
18238 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18240 return this.groups[navId] ;
18255 * @class Roo.bootstrap.TabPanel
18256 * @extends Roo.bootstrap.Component
18257 * Bootstrap TabPanel class
18258 * @cfg {Boolean} active panel active
18259 * @cfg {String} html panel content
18260 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18261 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18262 * @cfg {String} href click to link..
18266 * Create a new TabPanel
18267 * @param {Object} config The config object
18270 Roo.bootstrap.TabPanel = function(config){
18271 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18275 * Fires when the active status changes
18276 * @param {Roo.bootstrap.TabPanel} this
18277 * @param {Boolean} state the new state
18282 * @event beforedeactivate
18283 * Fires before a tab is de-activated - can be used to do validation on a form.
18284 * @param {Roo.bootstrap.TabPanel} this
18285 * @return {Boolean} false if there is an error
18288 'beforedeactivate': true
18291 this.tabId = this.tabId || Roo.id();
18295 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18303 getAutoCreate : function(){
18306 // item is needed for carousel - not sure if it has any effect otherwise
18307 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18308 html: this.html || ''
18312 cfg.cls += ' active';
18316 cfg.tabId = this.tabId;
18323 initEvents: function()
18325 var p = this.parent();
18327 this.navId = this.navId || p.navId;
18329 if (typeof(this.navId) != 'undefined') {
18330 // not really needed.. but just in case.. parent should be a NavGroup.
18331 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18335 var i = tg.tabs.length - 1;
18337 if(this.active && tg.bullets > 0 && i < tg.bullets){
18338 tg.setActiveBullet(i);
18342 this.el.on('click', this.onClick, this);
18345 this.el.on("touchstart", this.onTouchStart, this);
18346 this.el.on("touchmove", this.onTouchMove, this);
18347 this.el.on("touchend", this.onTouchEnd, this);
18352 onRender : function(ct, position)
18354 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18357 setActive : function(state)
18359 Roo.log("panel - set active " + this.tabId + "=" + state);
18361 this.active = state;
18363 this.el.removeClass('active');
18365 } else if (!this.el.hasClass('active')) {
18366 this.el.addClass('active');
18369 this.fireEvent('changed', this, state);
18372 onClick : function(e)
18374 e.preventDefault();
18376 if(!this.href.length){
18380 window.location.href = this.href;
18389 onTouchStart : function(e)
18391 this.swiping = false;
18393 this.startX = e.browserEvent.touches[0].clientX;
18394 this.startY = e.browserEvent.touches[0].clientY;
18397 onTouchMove : function(e)
18399 this.swiping = true;
18401 this.endX = e.browserEvent.touches[0].clientX;
18402 this.endY = e.browserEvent.touches[0].clientY;
18405 onTouchEnd : function(e)
18412 var tabGroup = this.parent();
18414 if(this.endX > this.startX){ // swiping right
18415 tabGroup.showPanelPrev();
18419 if(this.startX > this.endX){ // swiping left
18420 tabGroup.showPanelNext();
18439 * @class Roo.bootstrap.DateField
18440 * @extends Roo.bootstrap.Input
18441 * Bootstrap DateField class
18442 * @cfg {Number} weekStart default 0
18443 * @cfg {String} viewMode default empty, (months|years)
18444 * @cfg {String} minViewMode default empty, (months|years)
18445 * @cfg {Number} startDate default -Infinity
18446 * @cfg {Number} endDate default Infinity
18447 * @cfg {Boolean} todayHighlight default false
18448 * @cfg {Boolean} todayBtn default false
18449 * @cfg {Boolean} calendarWeeks default false
18450 * @cfg {Object} daysOfWeekDisabled default empty
18451 * @cfg {Boolean} singleMode default false (true | false)
18453 * @cfg {Boolean} keyboardNavigation default true
18454 * @cfg {String} language default en
18457 * Create a new DateField
18458 * @param {Object} config The config object
18461 Roo.bootstrap.DateField = function(config){
18462 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18466 * Fires when this field show.
18467 * @param {Roo.bootstrap.DateField} this
18468 * @param {Mixed} date The date value
18473 * Fires when this field hide.
18474 * @param {Roo.bootstrap.DateField} this
18475 * @param {Mixed} date The date value
18480 * Fires when select a date.
18481 * @param {Roo.bootstrap.DateField} this
18482 * @param {Mixed} date The date value
18486 * @event beforeselect
18487 * Fires when before select a date.
18488 * @param {Roo.bootstrap.DateField} this
18489 * @param {Mixed} date The date value
18491 beforeselect : true
18495 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18498 * @cfg {String} format
18499 * The default date format string which can be overriden for localization support. The format must be
18500 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18504 * @cfg {String} altFormats
18505 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18506 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18508 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18516 todayHighlight : false,
18522 keyboardNavigation: true,
18524 calendarWeeks: false,
18526 startDate: -Infinity,
18530 daysOfWeekDisabled: [],
18534 singleMode : false,
18536 UTCDate: function()
18538 return new Date(Date.UTC.apply(Date, arguments));
18541 UTCToday: function()
18543 var today = new Date();
18544 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18547 getDate: function() {
18548 var d = this.getUTCDate();
18549 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18552 getUTCDate: function() {
18556 setDate: function(d) {
18557 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18560 setUTCDate: function(d) {
18562 this.setValue(this.formatDate(this.date));
18565 onRender: function(ct, position)
18568 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18570 this.language = this.language || 'en';
18571 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18572 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18574 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18575 this.format = this.format || 'm/d/y';
18576 this.isInline = false;
18577 this.isInput = true;
18578 this.component = this.el.select('.add-on', true).first() || false;
18579 this.component = (this.component && this.component.length === 0) ? false : this.component;
18580 this.hasInput = this.component && this.inputEl().length;
18582 if (typeof(this.minViewMode === 'string')) {
18583 switch (this.minViewMode) {
18585 this.minViewMode = 1;
18588 this.minViewMode = 2;
18591 this.minViewMode = 0;
18596 if (typeof(this.viewMode === 'string')) {
18597 switch (this.viewMode) {
18610 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18612 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18614 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18616 this.picker().on('mousedown', this.onMousedown, this);
18617 this.picker().on('click', this.onClick, this);
18619 this.picker().addClass('datepicker-dropdown');
18621 this.startViewMode = this.viewMode;
18623 if(this.singleMode){
18624 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18625 v.setVisibilityMode(Roo.Element.DISPLAY);
18629 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18630 v.setStyle('width', '189px');
18634 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18635 if(!this.calendarWeeks){
18640 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18641 v.attr('colspan', function(i, val){
18642 return parseInt(val) + 1;
18647 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18649 this.setStartDate(this.startDate);
18650 this.setEndDate(this.endDate);
18652 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18659 if(this.isInline) {
18664 picker : function()
18666 return this.pickerEl;
18667 // return this.el.select('.datepicker', true).first();
18670 fillDow: function()
18672 var dowCnt = this.weekStart;
18681 if(this.calendarWeeks){
18689 while (dowCnt < this.weekStart + 7) {
18693 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18697 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18700 fillMonths: function()
18703 var months = this.picker().select('>.datepicker-months td', true).first();
18705 months.dom.innerHTML = '';
18711 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18714 months.createChild(month);
18721 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;
18723 if (this.date < this.startDate) {
18724 this.viewDate = new Date(this.startDate);
18725 } else if (this.date > this.endDate) {
18726 this.viewDate = new Date(this.endDate);
18728 this.viewDate = new Date(this.date);
18736 var d = new Date(this.viewDate),
18737 year = d.getUTCFullYear(),
18738 month = d.getUTCMonth(),
18739 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18740 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18741 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18742 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18743 currentDate = this.date && this.date.valueOf(),
18744 today = this.UTCToday();
18746 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18748 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18750 // this.picker.select('>tfoot th.today').
18751 // .text(dates[this.language].today)
18752 // .toggle(this.todayBtn !== false);
18754 this.updateNavArrows();
18757 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18759 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18761 prevMonth.setUTCDate(day);
18763 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18765 var nextMonth = new Date(prevMonth);
18767 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18769 nextMonth = nextMonth.valueOf();
18771 var fillMonths = false;
18773 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18775 while(prevMonth.valueOf() <= nextMonth) {
18778 if (prevMonth.getUTCDay() === this.weekStart) {
18780 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18788 if(this.calendarWeeks){
18789 // ISO 8601: First week contains first thursday.
18790 // ISO also states week starts on Monday, but we can be more abstract here.
18792 // Start of current week: based on weekstart/current date
18793 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18794 // Thursday of this week
18795 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18796 // First Thursday of year, year from thursday
18797 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18798 // Calendar week: ms between thursdays, div ms per day, div 7 days
18799 calWeek = (th - yth) / 864e5 / 7 + 1;
18801 fillMonths.cn.push({
18809 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18811 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18814 if (this.todayHighlight &&
18815 prevMonth.getUTCFullYear() == today.getFullYear() &&
18816 prevMonth.getUTCMonth() == today.getMonth() &&
18817 prevMonth.getUTCDate() == today.getDate()) {
18818 clsName += ' today';
18821 if (currentDate && prevMonth.valueOf() === currentDate) {
18822 clsName += ' active';
18825 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18826 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18827 clsName += ' disabled';
18830 fillMonths.cn.push({
18832 cls: 'day ' + clsName,
18833 html: prevMonth.getDate()
18836 prevMonth.setDate(prevMonth.getDate()+1);
18839 var currentYear = this.date && this.date.getUTCFullYear();
18840 var currentMonth = this.date && this.date.getUTCMonth();
18842 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18844 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18845 v.removeClass('active');
18847 if(currentYear === year && k === currentMonth){
18848 v.addClass('active');
18851 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18852 v.addClass('disabled');
18858 year = parseInt(year/10, 10) * 10;
18860 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18862 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18865 for (var i = -1; i < 11; i++) {
18866 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18868 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18876 showMode: function(dir)
18879 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18882 Roo.each(this.picker().select('>div',true).elements, function(v){
18883 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18891 if(this.isInline) {
18895 this.picker().removeClass(['bottom', 'top']);
18897 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18899 * place to the top of element!
18903 this.picker().addClass('top');
18904 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18909 this.picker().addClass('bottom');
18911 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18914 parseDate : function(value)
18916 if(!value || value instanceof Date){
18919 var v = Date.parseDate(value, this.format);
18920 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18921 v = Date.parseDate(value, 'Y-m-d');
18923 if(!v && this.altFormats){
18924 if(!this.altFormatsArray){
18925 this.altFormatsArray = this.altFormats.split("|");
18927 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18928 v = Date.parseDate(value, this.altFormatsArray[i]);
18934 formatDate : function(date, fmt)
18936 return (!date || !(date instanceof Date)) ?
18937 date : date.dateFormat(fmt || this.format);
18940 onFocus : function()
18942 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18946 onBlur : function()
18948 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18950 var d = this.inputEl().getValue();
18957 showPopup : function()
18959 this.picker().show();
18963 this.fireEvent('showpopup', this, this.date);
18966 hidePopup : function()
18968 if(this.isInline) {
18971 this.picker().hide();
18972 this.viewMode = this.startViewMode;
18975 this.fireEvent('hidepopup', this, this.date);
18979 onMousedown: function(e)
18981 e.stopPropagation();
18982 e.preventDefault();
18987 Roo.bootstrap.DateField.superclass.keyup.call(this);
18991 setValue: function(v)
18993 if(this.fireEvent('beforeselect', this, v) !== false){
18994 var d = new Date(this.parseDate(v) ).clearTime();
18996 if(isNaN(d.getTime())){
18997 this.date = this.viewDate = '';
18998 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19002 v = this.formatDate(d);
19004 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19006 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19010 this.fireEvent('select', this, this.date);
19014 getValue: function()
19016 return this.formatDate(this.date);
19019 fireKey: function(e)
19021 if (!this.picker().isVisible()){
19022 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19028 var dateChanged = false,
19030 newDate, newViewDate;
19035 e.preventDefault();
19039 if (!this.keyboardNavigation) {
19042 dir = e.keyCode == 37 ? -1 : 1;
19045 newDate = this.moveYear(this.date, dir);
19046 newViewDate = this.moveYear(this.viewDate, dir);
19047 } else if (e.shiftKey){
19048 newDate = this.moveMonth(this.date, dir);
19049 newViewDate = this.moveMonth(this.viewDate, dir);
19051 newDate = new Date(this.date);
19052 newDate.setUTCDate(this.date.getUTCDate() + dir);
19053 newViewDate = new Date(this.viewDate);
19054 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19056 if (this.dateWithinRange(newDate)){
19057 this.date = newDate;
19058 this.viewDate = newViewDate;
19059 this.setValue(this.formatDate(this.date));
19061 e.preventDefault();
19062 dateChanged = true;
19067 if (!this.keyboardNavigation) {
19070 dir = e.keyCode == 38 ? -1 : 1;
19072 newDate = this.moveYear(this.date, dir);
19073 newViewDate = this.moveYear(this.viewDate, dir);
19074 } else if (e.shiftKey){
19075 newDate = this.moveMonth(this.date, dir);
19076 newViewDate = this.moveMonth(this.viewDate, dir);
19078 newDate = new Date(this.date);
19079 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19080 newViewDate = new Date(this.viewDate);
19081 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19083 if (this.dateWithinRange(newDate)){
19084 this.date = newDate;
19085 this.viewDate = newViewDate;
19086 this.setValue(this.formatDate(this.date));
19088 e.preventDefault();
19089 dateChanged = true;
19093 this.setValue(this.formatDate(this.date));
19095 e.preventDefault();
19098 this.setValue(this.formatDate(this.date));
19112 onClick: function(e)
19114 e.stopPropagation();
19115 e.preventDefault();
19117 var target = e.getTarget();
19119 if(target.nodeName.toLowerCase() === 'i'){
19120 target = Roo.get(target).dom.parentNode;
19123 var nodeName = target.nodeName;
19124 var className = target.className;
19125 var html = target.innerHTML;
19126 //Roo.log(nodeName);
19128 switch(nodeName.toLowerCase()) {
19130 switch(className) {
19136 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19137 switch(this.viewMode){
19139 this.viewDate = this.moveMonth(this.viewDate, dir);
19143 this.viewDate = this.moveYear(this.viewDate, dir);
19149 var date = new Date();
19150 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19152 this.setValue(this.formatDate(this.date));
19159 if (className.indexOf('disabled') < 0) {
19160 this.viewDate.setUTCDate(1);
19161 if (className.indexOf('month') > -1) {
19162 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19164 var year = parseInt(html, 10) || 0;
19165 this.viewDate.setUTCFullYear(year);
19169 if(this.singleMode){
19170 this.setValue(this.formatDate(this.viewDate));
19181 //Roo.log(className);
19182 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19183 var day = parseInt(html, 10) || 1;
19184 var year = this.viewDate.getUTCFullYear(),
19185 month = this.viewDate.getUTCMonth();
19187 if (className.indexOf('old') > -1) {
19194 } else if (className.indexOf('new') > -1) {
19202 //Roo.log([year,month,day]);
19203 this.date = this.UTCDate(year, month, day,0,0,0,0);
19204 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19206 //Roo.log(this.formatDate(this.date));
19207 this.setValue(this.formatDate(this.date));
19214 setStartDate: function(startDate)
19216 this.startDate = startDate || -Infinity;
19217 if (this.startDate !== -Infinity) {
19218 this.startDate = this.parseDate(this.startDate);
19221 this.updateNavArrows();
19224 setEndDate: function(endDate)
19226 this.endDate = endDate || Infinity;
19227 if (this.endDate !== Infinity) {
19228 this.endDate = this.parseDate(this.endDate);
19231 this.updateNavArrows();
19234 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19236 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19237 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19238 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19240 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19241 return parseInt(d, 10);
19244 this.updateNavArrows();
19247 updateNavArrows: function()
19249 if(this.singleMode){
19253 var d = new Date(this.viewDate),
19254 year = d.getUTCFullYear(),
19255 month = d.getUTCMonth();
19257 Roo.each(this.picker().select('.prev', true).elements, function(v){
19259 switch (this.viewMode) {
19262 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19268 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19275 Roo.each(this.picker().select('.next', true).elements, function(v){
19277 switch (this.viewMode) {
19280 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19286 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19294 moveMonth: function(date, dir)
19299 var new_date = new Date(date.valueOf()),
19300 day = new_date.getUTCDate(),
19301 month = new_date.getUTCMonth(),
19302 mag = Math.abs(dir),
19304 dir = dir > 0 ? 1 : -1;
19307 // If going back one month, make sure month is not current month
19308 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19310 return new_date.getUTCMonth() == month;
19312 // If going forward one month, make sure month is as expected
19313 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19315 return new_date.getUTCMonth() != new_month;
19317 new_month = month + dir;
19318 new_date.setUTCMonth(new_month);
19319 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19320 if (new_month < 0 || new_month > 11) {
19321 new_month = (new_month + 12) % 12;
19324 // For magnitudes >1, move one month at a time...
19325 for (var i=0; i<mag; i++) {
19326 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19327 new_date = this.moveMonth(new_date, dir);
19329 // ...then reset the day, keeping it in the new month
19330 new_month = new_date.getUTCMonth();
19331 new_date.setUTCDate(day);
19333 return new_month != new_date.getUTCMonth();
19336 // Common date-resetting loop -- if date is beyond end of month, make it
19339 new_date.setUTCDate(--day);
19340 new_date.setUTCMonth(new_month);
19345 moveYear: function(date, dir)
19347 return this.moveMonth(date, dir*12);
19350 dateWithinRange: function(date)
19352 return date >= this.startDate && date <= this.endDate;
19358 this.picker().remove();
19361 validateValue : function(value)
19363 if(this.getVisibilityEl().hasClass('hidden')){
19367 if(value.length < 1) {
19368 if(this.allowBlank){
19374 if(value.length < this.minLength){
19377 if(value.length > this.maxLength){
19381 var vt = Roo.form.VTypes;
19382 if(!vt[this.vtype](value, this)){
19386 if(typeof this.validator == "function"){
19387 var msg = this.validator(value);
19393 if(this.regex && !this.regex.test(value)){
19397 if(typeof(this.parseDate(value)) == 'undefined'){
19401 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19405 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19415 this.date = this.viewDate = '';
19417 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19422 Roo.apply(Roo.bootstrap.DateField, {
19433 html: '<i class="fa fa-arrow-left"/>'
19443 html: '<i class="fa fa-arrow-right"/>'
19485 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19486 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19487 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19488 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19489 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19502 navFnc: 'FullYear',
19507 navFnc: 'FullYear',
19512 Roo.apply(Roo.bootstrap.DateField, {
19516 cls: 'datepicker dropdown-menu roo-dynamic',
19520 cls: 'datepicker-days',
19524 cls: 'table-condensed',
19526 Roo.bootstrap.DateField.head,
19530 Roo.bootstrap.DateField.footer
19537 cls: 'datepicker-months',
19541 cls: 'table-condensed',
19543 Roo.bootstrap.DateField.head,
19544 Roo.bootstrap.DateField.content,
19545 Roo.bootstrap.DateField.footer
19552 cls: 'datepicker-years',
19556 cls: 'table-condensed',
19558 Roo.bootstrap.DateField.head,
19559 Roo.bootstrap.DateField.content,
19560 Roo.bootstrap.DateField.footer
19579 * @class Roo.bootstrap.TimeField
19580 * @extends Roo.bootstrap.Input
19581 * Bootstrap DateField class
19585 * Create a new TimeField
19586 * @param {Object} config The config object
19589 Roo.bootstrap.TimeField = function(config){
19590 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19594 * Fires when this field show.
19595 * @param {Roo.bootstrap.DateField} thisthis
19596 * @param {Mixed} date The date value
19601 * Fires when this field hide.
19602 * @param {Roo.bootstrap.DateField} this
19603 * @param {Mixed} date The date value
19608 * Fires when select a date.
19609 * @param {Roo.bootstrap.DateField} this
19610 * @param {Mixed} date The date value
19616 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19619 * @cfg {String} format
19620 * The default time format string which can be overriden for localization support. The format must be
19621 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19625 onRender: function(ct, position)
19628 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19630 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19632 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19634 this.pop = this.picker().select('>.datepicker-time',true).first();
19635 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19637 this.picker().on('mousedown', this.onMousedown, this);
19638 this.picker().on('click', this.onClick, this);
19640 this.picker().addClass('datepicker-dropdown');
19645 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19646 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19647 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19648 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19649 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19650 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19654 fireKey: function(e){
19655 if (!this.picker().isVisible()){
19656 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19662 e.preventDefault();
19670 this.onTogglePeriod();
19673 this.onIncrementMinutes();
19676 this.onDecrementMinutes();
19685 onClick: function(e) {
19686 e.stopPropagation();
19687 e.preventDefault();
19690 picker : function()
19692 return this.el.select('.datepicker', true).first();
19695 fillTime: function()
19697 var time = this.pop.select('tbody', true).first();
19699 time.dom.innerHTML = '';
19714 cls: 'hours-up glyphicon glyphicon-chevron-up'
19734 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19755 cls: 'timepicker-hour',
19770 cls: 'timepicker-minute',
19785 cls: 'btn btn-primary period',
19807 cls: 'hours-down glyphicon glyphicon-chevron-down'
19827 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19845 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19852 var hours = this.time.getHours();
19853 var minutes = this.time.getMinutes();
19866 hours = hours - 12;
19870 hours = '0' + hours;
19874 minutes = '0' + minutes;
19877 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19878 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19879 this.pop.select('button', true).first().dom.innerHTML = period;
19885 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19887 var cls = ['bottom'];
19889 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19896 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19901 this.picker().addClass(cls.join('-'));
19905 Roo.each(cls, function(c){
19907 _this.picker().setTop(_this.inputEl().getHeight());
19911 _this.picker().setTop(0 - _this.picker().getHeight());
19916 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19920 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19927 onFocus : function()
19929 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19933 onBlur : function()
19935 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19941 this.picker().show();
19946 this.fireEvent('show', this, this.date);
19951 this.picker().hide();
19954 this.fireEvent('hide', this, this.date);
19957 setTime : function()
19960 this.setValue(this.time.format(this.format));
19962 this.fireEvent('select', this, this.date);
19967 onMousedown: function(e){
19968 e.stopPropagation();
19969 e.preventDefault();
19972 onIncrementHours: function()
19974 Roo.log('onIncrementHours');
19975 this.time = this.time.add(Date.HOUR, 1);
19980 onDecrementHours: function()
19982 Roo.log('onDecrementHours');
19983 this.time = this.time.add(Date.HOUR, -1);
19987 onIncrementMinutes: function()
19989 Roo.log('onIncrementMinutes');
19990 this.time = this.time.add(Date.MINUTE, 1);
19994 onDecrementMinutes: function()
19996 Roo.log('onDecrementMinutes');
19997 this.time = this.time.add(Date.MINUTE, -1);
20001 onTogglePeriod: function()
20003 Roo.log('onTogglePeriod');
20004 this.time = this.time.add(Date.HOUR, 12);
20011 Roo.apply(Roo.bootstrap.TimeField, {
20041 cls: 'btn btn-info ok',
20053 Roo.apply(Roo.bootstrap.TimeField, {
20057 cls: 'datepicker dropdown-menu',
20061 cls: 'datepicker-time',
20065 cls: 'table-condensed',
20067 Roo.bootstrap.TimeField.content,
20068 Roo.bootstrap.TimeField.footer
20087 * @class Roo.bootstrap.MonthField
20088 * @extends Roo.bootstrap.Input
20089 * Bootstrap MonthField class
20091 * @cfg {String} language default en
20094 * Create a new MonthField
20095 * @param {Object} config The config object
20098 Roo.bootstrap.MonthField = function(config){
20099 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20104 * Fires when this field show.
20105 * @param {Roo.bootstrap.MonthField} this
20106 * @param {Mixed} date The date value
20111 * Fires when this field hide.
20112 * @param {Roo.bootstrap.MonthField} this
20113 * @param {Mixed} date The date value
20118 * Fires when select a date.
20119 * @param {Roo.bootstrap.MonthField} this
20120 * @param {String} oldvalue The old value
20121 * @param {String} newvalue The new value
20127 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20129 onRender: function(ct, position)
20132 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20134 this.language = this.language || 'en';
20135 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20136 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20138 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20139 this.isInline = false;
20140 this.isInput = true;
20141 this.component = this.el.select('.add-on', true).first() || false;
20142 this.component = (this.component && this.component.length === 0) ? false : this.component;
20143 this.hasInput = this.component && this.inputEL().length;
20145 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20147 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20149 this.picker().on('mousedown', this.onMousedown, this);
20150 this.picker().on('click', this.onClick, this);
20152 this.picker().addClass('datepicker-dropdown');
20154 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20155 v.setStyle('width', '189px');
20162 if(this.isInline) {
20168 setValue: function(v, suppressEvent)
20170 var o = this.getValue();
20172 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20176 if(suppressEvent !== true){
20177 this.fireEvent('select', this, o, v);
20182 getValue: function()
20187 onClick: function(e)
20189 e.stopPropagation();
20190 e.preventDefault();
20192 var target = e.getTarget();
20194 if(target.nodeName.toLowerCase() === 'i'){
20195 target = Roo.get(target).dom.parentNode;
20198 var nodeName = target.nodeName;
20199 var className = target.className;
20200 var html = target.innerHTML;
20202 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20206 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20208 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20214 picker : function()
20216 return this.pickerEl;
20219 fillMonths: function()
20222 var months = this.picker().select('>.datepicker-months td', true).first();
20224 months.dom.innerHTML = '';
20230 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20233 months.createChild(month);
20242 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20243 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20246 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20247 e.removeClass('active');
20249 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20250 e.addClass('active');
20257 if(this.isInline) {
20261 this.picker().removeClass(['bottom', 'top']);
20263 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20265 * place to the top of element!
20269 this.picker().addClass('top');
20270 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20275 this.picker().addClass('bottom');
20277 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20280 onFocus : function()
20282 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20286 onBlur : function()
20288 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20290 var d = this.inputEl().getValue();
20299 this.picker().show();
20300 this.picker().select('>.datepicker-months', true).first().show();
20304 this.fireEvent('show', this, this.date);
20309 if(this.isInline) {
20312 this.picker().hide();
20313 this.fireEvent('hide', this, this.date);
20317 onMousedown: function(e)
20319 e.stopPropagation();
20320 e.preventDefault();
20325 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20329 fireKey: function(e)
20331 if (!this.picker().isVisible()){
20332 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20343 e.preventDefault();
20347 dir = e.keyCode == 37 ? -1 : 1;
20349 this.vIndex = this.vIndex + dir;
20351 if(this.vIndex < 0){
20355 if(this.vIndex > 11){
20359 if(isNaN(this.vIndex)){
20363 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20369 dir = e.keyCode == 38 ? -1 : 1;
20371 this.vIndex = this.vIndex + dir * 4;
20373 if(this.vIndex < 0){
20377 if(this.vIndex > 11){
20381 if(isNaN(this.vIndex)){
20385 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20390 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20391 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20395 e.preventDefault();
20398 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20399 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20415 this.picker().remove();
20420 Roo.apply(Roo.bootstrap.MonthField, {
20439 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20440 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20445 Roo.apply(Roo.bootstrap.MonthField, {
20449 cls: 'datepicker dropdown-menu roo-dynamic',
20453 cls: 'datepicker-months',
20457 cls: 'table-condensed',
20459 Roo.bootstrap.DateField.content
20479 * @class Roo.bootstrap.CheckBox
20480 * @extends Roo.bootstrap.Input
20481 * Bootstrap CheckBox class
20483 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20484 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20485 * @cfg {String} boxLabel The text that appears beside the checkbox
20486 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20487 * @cfg {Boolean} checked initnal the element
20488 * @cfg {Boolean} inline inline the element (default false)
20489 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20490 * @cfg {String} tooltip label tooltip
20493 * Create a new CheckBox
20494 * @param {Object} config The config object
20497 Roo.bootstrap.CheckBox = function(config){
20498 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20503 * Fires when the element is checked or unchecked.
20504 * @param {Roo.bootstrap.CheckBox} this This input
20505 * @param {Boolean} checked The new checked value
20510 * Fires when the element is click.
20511 * @param {Roo.bootstrap.CheckBox} this This input
20518 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20520 inputType: 'checkbox',
20529 getAutoCreate : function()
20531 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20537 cfg.cls = 'form-group ' + this.inputType; //input-group
20540 cfg.cls += ' ' + this.inputType + '-inline';
20546 type : this.inputType,
20547 value : this.inputValue,
20548 cls : 'roo-' + this.inputType, //'form-box',
20549 placeholder : this.placeholder || ''
20553 if(this.inputType != 'radio'){
20557 cls : 'roo-hidden-value',
20558 value : this.checked ? this.inputValue : this.valueOff
20563 if (this.weight) { // Validity check?
20564 cfg.cls += " " + this.inputType + "-" + this.weight;
20567 if (this.disabled) {
20568 input.disabled=true;
20572 input.checked = this.checked;
20577 input.name = this.name;
20579 if(this.inputType != 'radio'){
20580 hidden.name = this.name;
20581 input.name = '_hidden_' + this.name;
20586 input.cls += ' input-' + this.size;
20591 ['xs','sm','md','lg'].map(function(size){
20592 if (settings[size]) {
20593 cfg.cls += ' col-' + size + '-' + settings[size];
20597 var inputblock = input;
20599 if (this.before || this.after) {
20602 cls : 'input-group',
20607 inputblock.cn.push({
20609 cls : 'input-group-addon',
20614 inputblock.cn.push(input);
20616 if(this.inputType != 'radio'){
20617 inputblock.cn.push(hidden);
20621 inputblock.cn.push({
20623 cls : 'input-group-addon',
20630 if (align ==='left' && this.fieldLabel.length) {
20631 // Roo.log("left and has label");
20636 cls : 'control-label',
20637 html : this.fieldLabel
20647 if(this.labelWidth > 12){
20648 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20651 if(this.labelWidth < 13 && this.labelmd == 0){
20652 this.labelmd = this.labelWidth;
20655 if(this.labellg > 0){
20656 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20657 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20660 if(this.labelmd > 0){
20661 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20662 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20665 if(this.labelsm > 0){
20666 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20667 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20670 if(this.labelxs > 0){
20671 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20672 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20675 } else if ( this.fieldLabel.length) {
20676 // Roo.log(" label");
20680 tag: this.boxLabel ? 'span' : 'label',
20682 cls: 'control-label box-input-label',
20683 //cls : 'input-group-addon',
20684 html : this.fieldLabel
20693 // Roo.log(" no label && no align");
20694 cfg.cn = [ inputblock ] ;
20700 var boxLabelCfg = {
20702 //'for': id, // box label is handled by onclick - so no for...
20704 html: this.boxLabel
20708 boxLabelCfg.tooltip = this.tooltip;
20711 cfg.cn.push(boxLabelCfg);
20714 if(this.inputType != 'radio'){
20715 cfg.cn.push(hidden);
20723 * return the real input element.
20725 inputEl: function ()
20727 return this.el.select('input.roo-' + this.inputType,true).first();
20729 hiddenEl: function ()
20731 return this.el.select('input.roo-hidden-value',true).first();
20734 labelEl: function()
20736 return this.el.select('label.control-label',true).first();
20738 /* depricated... */
20742 return this.labelEl();
20745 boxLabelEl: function()
20747 return this.el.select('label.box-label',true).first();
20750 initEvents : function()
20752 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20754 this.inputEl().on('click', this.onClick, this);
20756 if (this.boxLabel) {
20757 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20760 this.startValue = this.getValue();
20763 Roo.bootstrap.CheckBox.register(this);
20767 onClick : function(e)
20769 if(this.fireEvent('click', this, e) !== false){
20770 this.setChecked(!this.checked);
20775 setChecked : function(state,suppressEvent)
20777 this.startValue = this.getValue();
20779 if(this.inputType == 'radio'){
20781 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20782 e.dom.checked = false;
20785 this.inputEl().dom.checked = true;
20787 this.inputEl().dom.value = this.inputValue;
20789 if(suppressEvent !== true){
20790 this.fireEvent('check', this, true);
20798 this.checked = state;
20800 this.inputEl().dom.checked = state;
20803 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20805 if(suppressEvent !== true){
20806 this.fireEvent('check', this, state);
20812 getValue : function()
20814 if(this.inputType == 'radio'){
20815 return this.getGroupValue();
20818 return this.hiddenEl().dom.value;
20822 getGroupValue : function()
20824 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20828 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20831 setValue : function(v,suppressEvent)
20833 if(this.inputType == 'radio'){
20834 this.setGroupValue(v, suppressEvent);
20838 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20843 setGroupValue : function(v, suppressEvent)
20845 this.startValue = this.getValue();
20847 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20848 e.dom.checked = false;
20850 if(e.dom.value == v){
20851 e.dom.checked = true;
20855 if(suppressEvent !== true){
20856 this.fireEvent('check', this, true);
20864 validate : function()
20866 if(this.getVisibilityEl().hasClass('hidden')){
20872 (this.inputType == 'radio' && this.validateRadio()) ||
20873 (this.inputType == 'checkbox' && this.validateCheckbox())
20879 this.markInvalid();
20883 validateRadio : function()
20885 if(this.getVisibilityEl().hasClass('hidden')){
20889 if(this.allowBlank){
20895 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20896 if(!e.dom.checked){
20908 validateCheckbox : function()
20911 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20912 //return (this.getValue() == this.inputValue) ? true : false;
20915 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20923 for(var i in group){
20924 if(group[i].el.isVisible(true)){
20932 for(var i in group){
20937 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20944 * Mark this field as valid
20946 markValid : function()
20950 this.fireEvent('valid', this);
20952 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20955 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20962 if(this.inputType == 'radio'){
20963 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20965 e.findParent('.form-group', false, true).addClass(_this.validClass);
20972 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20973 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20977 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20983 for(var i in group){
20984 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20985 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20990 * Mark this field as invalid
20991 * @param {String} msg The validation message
20993 markInvalid : function(msg)
20995 if(this.allowBlank){
21001 this.fireEvent('invalid', this, msg);
21003 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21006 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21010 label.markInvalid();
21013 if(this.inputType == 'radio'){
21014 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21015 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21016 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21023 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21024 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21028 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21034 for(var i in group){
21035 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21036 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21041 clearInvalid : function()
21043 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21045 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21047 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21049 if (label && label.iconEl) {
21050 label.iconEl.removeClass(label.validClass);
21051 label.iconEl.removeClass(label.invalidClass);
21055 disable : function()
21057 if(this.inputType != 'radio'){
21058 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21065 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21066 _this.getActionEl().addClass(this.disabledClass);
21067 e.dom.disabled = true;
21071 this.disabled = true;
21072 this.fireEvent("disable", this);
21076 enable : function()
21078 if(this.inputType != 'radio'){
21079 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21086 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087 _this.getActionEl().removeClass(this.disabledClass);
21088 e.dom.disabled = false;
21092 this.disabled = false;
21093 this.fireEvent("enable", this);
21097 setBoxLabel : function(v)
21102 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21108 Roo.apply(Roo.bootstrap.CheckBox, {
21113 * register a CheckBox Group
21114 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21116 register : function(checkbox)
21118 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21119 this.groups[checkbox.groupId] = {};
21122 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21126 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21130 * fetch a CheckBox Group based on the group ID
21131 * @param {string} the group ID
21132 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21134 get: function(groupId) {
21135 if (typeof(this.groups[groupId]) == 'undefined') {
21139 return this.groups[groupId] ;
21152 * @class Roo.bootstrap.Radio
21153 * @extends Roo.bootstrap.Component
21154 * Bootstrap Radio class
21155 * @cfg {String} boxLabel - the label associated
21156 * @cfg {String} value - the value of radio
21159 * Create a new Radio
21160 * @param {Object} config The config object
21162 Roo.bootstrap.Radio = function(config){
21163 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21167 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21173 getAutoCreate : function()
21177 cls : 'form-group radio',
21182 html : this.boxLabel
21190 initEvents : function()
21192 this.parent().register(this);
21194 this.el.on('click', this.onClick, this);
21198 onClick : function(e)
21200 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21201 this.setChecked(true);
21205 setChecked : function(state, suppressEvent)
21207 this.parent().setValue(this.value, suppressEvent);
21211 setBoxLabel : function(v)
21216 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21231 * @class Roo.bootstrap.SecurePass
21232 * @extends Roo.bootstrap.Input
21233 * Bootstrap SecurePass class
21237 * Create a new SecurePass
21238 * @param {Object} config The config object
21241 Roo.bootstrap.SecurePass = function (config) {
21242 // these go here, so the translation tool can replace them..
21244 PwdEmpty: "Please type a password, and then retype it to confirm.",
21245 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21246 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21247 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21248 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21249 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21250 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21251 TooWeak: "Your password is Too Weak."
21253 this.meterLabel = "Password strength:";
21254 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21255 this.meterClass = [
21256 "roo-password-meter-tooweak",
21257 "roo-password-meter-weak",
21258 "roo-password-meter-medium",
21259 "roo-password-meter-strong",
21260 "roo-password-meter-grey"
21265 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21268 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21270 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21272 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21273 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21274 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21275 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21276 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21277 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21278 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21288 * @cfg {String/Object} Label for the strength meter (defaults to
21289 * 'Password strength:')
21294 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21295 * ['Weak', 'Medium', 'Strong'])
21298 pwdStrengths: false,
21311 initEvents: function ()
21313 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21315 if (this.el.is('input[type=password]') && Roo.isSafari) {
21316 this.el.on('keydown', this.SafariOnKeyDown, this);
21319 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21322 onRender: function (ct, position)
21324 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21325 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21326 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21328 this.trigger.createChild({
21333 cls: 'roo-password-meter-grey col-xs-12',
21336 //width: this.meterWidth + 'px'
21340 cls: 'roo-password-meter-text'
21346 if (this.hideTrigger) {
21347 this.trigger.setDisplayed(false);
21349 this.setSize(this.width || '', this.height || '');
21352 onDestroy: function ()
21354 if (this.trigger) {
21355 this.trigger.removeAllListeners();
21356 this.trigger.remove();
21359 this.wrap.remove();
21361 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21364 checkStrength: function ()
21366 var pwd = this.inputEl().getValue();
21367 if (pwd == this._lastPwd) {
21372 if (this.ClientSideStrongPassword(pwd)) {
21374 } else if (this.ClientSideMediumPassword(pwd)) {
21376 } else if (this.ClientSideWeakPassword(pwd)) {
21382 Roo.log('strength1: ' + strength);
21384 //var pm = this.trigger.child('div/div/div').dom;
21385 var pm = this.trigger.child('div/div');
21386 pm.removeClass(this.meterClass);
21387 pm.addClass(this.meterClass[strength]);
21390 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21392 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21394 this._lastPwd = pwd;
21398 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21400 this._lastPwd = '';
21402 var pm = this.trigger.child('div/div');
21403 pm.removeClass(this.meterClass);
21404 pm.addClass('roo-password-meter-grey');
21407 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21410 this.inputEl().dom.type='password';
21413 validateValue: function (value)
21416 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21419 if (value.length == 0) {
21420 if (this.allowBlank) {
21421 this.clearInvalid();
21425 this.markInvalid(this.errors.PwdEmpty);
21426 this.errorMsg = this.errors.PwdEmpty;
21434 if ('[\x21-\x7e]*'.match(value)) {
21435 this.markInvalid(this.errors.PwdBadChar);
21436 this.errorMsg = this.errors.PwdBadChar;
21439 if (value.length < 6) {
21440 this.markInvalid(this.errors.PwdShort);
21441 this.errorMsg = this.errors.PwdShort;
21444 if (value.length > 16) {
21445 this.markInvalid(this.errors.PwdLong);
21446 this.errorMsg = this.errors.PwdLong;
21450 if (this.ClientSideStrongPassword(value)) {
21452 } else if (this.ClientSideMediumPassword(value)) {
21454 } else if (this.ClientSideWeakPassword(value)) {
21461 if (strength < 2) {
21462 //this.markInvalid(this.errors.TooWeak);
21463 this.errorMsg = this.errors.TooWeak;
21468 console.log('strength2: ' + strength);
21470 //var pm = this.trigger.child('div/div/div').dom;
21472 var pm = this.trigger.child('div/div');
21473 pm.removeClass(this.meterClass);
21474 pm.addClass(this.meterClass[strength]);
21476 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21478 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21480 this.errorMsg = '';
21484 CharacterSetChecks: function (type)
21487 this.fResult = false;
21490 isctype: function (character, type)
21493 case this.kCapitalLetter:
21494 if (character >= 'A' && character <= 'Z') {
21499 case this.kSmallLetter:
21500 if (character >= 'a' && character <= 'z') {
21506 if (character >= '0' && character <= '9') {
21511 case this.kPunctuation:
21512 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21523 IsLongEnough: function (pwd, size)
21525 return !(pwd == null || isNaN(size) || pwd.length < size);
21528 SpansEnoughCharacterSets: function (word, nb)
21530 if (!this.IsLongEnough(word, nb))
21535 var characterSetChecks = new Array(
21536 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21537 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21540 for (var index = 0; index < word.length; ++index) {
21541 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21542 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21543 characterSetChecks[nCharSet].fResult = true;
21550 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21551 if (characterSetChecks[nCharSet].fResult) {
21556 if (nCharSets < nb) {
21562 ClientSideStrongPassword: function (pwd)
21564 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21567 ClientSideMediumPassword: function (pwd)
21569 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21572 ClientSideWeakPassword: function (pwd)
21574 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21577 })//<script type="text/javascript">
21580 * Based Ext JS Library 1.1.1
21581 * Copyright(c) 2006-2007, Ext JS, LLC.
21587 * @class Roo.HtmlEditorCore
21588 * @extends Roo.Component
21589 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21591 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21594 Roo.HtmlEditorCore = function(config){
21597 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21602 * @event initialize
21603 * Fires when the editor is fully initialized (including the iframe)
21604 * @param {Roo.HtmlEditorCore} this
21609 * Fires when the editor is first receives the focus. Any insertion must wait
21610 * until after this event.
21611 * @param {Roo.HtmlEditorCore} this
21615 * @event beforesync
21616 * Fires before the textarea is updated with content from the editor iframe. Return false
21617 * to cancel the sync.
21618 * @param {Roo.HtmlEditorCore} this
21619 * @param {String} html
21623 * @event beforepush
21624 * Fires before the iframe editor is updated with content from the textarea. Return false
21625 * to cancel the push.
21626 * @param {Roo.HtmlEditorCore} this
21627 * @param {String} html
21632 * Fires when the textarea is updated with content from the editor iframe.
21633 * @param {Roo.HtmlEditorCore} this
21634 * @param {String} html
21639 * Fires when the iframe editor is updated with content from the textarea.
21640 * @param {Roo.HtmlEditorCore} this
21641 * @param {String} html
21646 * @event editorevent
21647 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21648 * @param {Roo.HtmlEditorCore} this
21654 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21656 // defaults : white / black...
21657 this.applyBlacklists();
21664 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21668 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21674 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21679 * @cfg {Number} height (in pixels)
21683 * @cfg {Number} width (in pixels)
21688 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21691 stylesheets: false,
21696 // private properties
21697 validationEvent : false,
21699 initialized : false,
21701 sourceEditMode : false,
21702 onFocus : Roo.emptyFn,
21704 hideMode:'offsets',
21708 // blacklist + whitelisted elements..
21715 * Protected method that will not generally be called directly. It
21716 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21717 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21719 getDocMarkup : function(){
21723 // inherit styels from page...??
21724 if (this.stylesheets === false) {
21726 Roo.get(document.head).select('style').each(function(node) {
21727 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21730 Roo.get(document.head).select('link').each(function(node) {
21731 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21734 } else if (!this.stylesheets.length) {
21736 st = '<style type="text/css">' +
21737 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21740 st = '<style type="text/css">' +
21745 st += '<style type="text/css">' +
21746 'IMG { cursor: pointer } ' +
21749 var cls = 'roo-htmleditor-body';
21751 if(this.bodyCls.length){
21752 cls += ' ' + this.bodyCls;
21755 return '<html><head>' + st +
21756 //<style type="text/css">' +
21757 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21759 ' </head><body class="' + cls + '"></body></html>';
21763 onRender : function(ct, position)
21766 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21767 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21770 this.el.dom.style.border = '0 none';
21771 this.el.dom.setAttribute('tabIndex', -1);
21772 this.el.addClass('x-hidden hide');
21776 if(Roo.isIE){ // fix IE 1px bogus margin
21777 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21781 this.frameId = Roo.id();
21785 var iframe = this.owner.wrap.createChild({
21787 cls: 'form-control', // bootstrap..
21789 name: this.frameId,
21790 frameBorder : 'no',
21791 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21796 this.iframe = iframe.dom;
21798 this.assignDocWin();
21800 this.doc.designMode = 'on';
21803 this.doc.write(this.getDocMarkup());
21807 var task = { // must defer to wait for browser to be ready
21809 //console.log("run task?" + this.doc.readyState);
21810 this.assignDocWin();
21811 if(this.doc.body || this.doc.readyState == 'complete'){
21813 this.doc.designMode="on";
21817 Roo.TaskMgr.stop(task);
21818 this.initEditor.defer(10, this);
21825 Roo.TaskMgr.start(task);
21830 onResize : function(w, h)
21832 Roo.log('resize: ' +w + ',' + h );
21833 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21837 if(typeof w == 'number'){
21839 this.iframe.style.width = w + 'px';
21841 if(typeof h == 'number'){
21843 this.iframe.style.height = h + 'px';
21845 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21852 * Toggles the editor between standard and source edit mode.
21853 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21855 toggleSourceEdit : function(sourceEditMode){
21857 this.sourceEditMode = sourceEditMode === true;
21859 if(this.sourceEditMode){
21861 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21864 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21865 //this.iframe.className = '';
21868 //this.setSize(this.owner.wrap.getSize());
21869 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21876 * Protected method that will not generally be called directly. If you need/want
21877 * custom HTML cleanup, this is the method you should override.
21878 * @param {String} html The HTML to be cleaned
21879 * return {String} The cleaned HTML
21881 cleanHtml : function(html){
21882 html = String(html);
21883 if(html.length > 5){
21884 if(Roo.isSafari){ // strip safari nonsense
21885 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21888 if(html == ' '){
21895 * HTML Editor -> Textarea
21896 * Protected method that will not generally be called directly. Syncs the contents
21897 * of the editor iframe with the textarea.
21899 syncValue : function(){
21900 if(this.initialized){
21901 var bd = (this.doc.body || this.doc.documentElement);
21902 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21903 var html = bd.innerHTML;
21905 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21906 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21908 html = '<div style="'+m[0]+'">' + html + '</div>';
21911 html = this.cleanHtml(html);
21912 // fix up the special chars.. normaly like back quotes in word...
21913 // however we do not want to do this with chinese..
21914 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21915 var cc = b.charCodeAt();
21917 (cc >= 0x4E00 && cc < 0xA000 ) ||
21918 (cc >= 0x3400 && cc < 0x4E00 ) ||
21919 (cc >= 0xf900 && cc < 0xfb00 )
21925 if(this.owner.fireEvent('beforesync', this, html) !== false){
21926 this.el.dom.value = html;
21927 this.owner.fireEvent('sync', this, html);
21933 * Protected method that will not generally be called directly. Pushes the value of the textarea
21934 * into the iframe editor.
21936 pushValue : function(){
21937 if(this.initialized){
21938 var v = this.el.dom.value.trim();
21940 // if(v.length < 1){
21944 if(this.owner.fireEvent('beforepush', this, v) !== false){
21945 var d = (this.doc.body || this.doc.documentElement);
21947 this.cleanUpPaste();
21948 this.el.dom.value = d.innerHTML;
21949 this.owner.fireEvent('push', this, v);
21955 deferFocus : function(){
21956 this.focus.defer(10, this);
21960 focus : function(){
21961 if(this.win && !this.sourceEditMode){
21968 assignDocWin: function()
21970 var iframe = this.iframe;
21973 this.doc = iframe.contentWindow.document;
21974 this.win = iframe.contentWindow;
21976 // if (!Roo.get(this.frameId)) {
21979 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21980 // this.win = Roo.get(this.frameId).dom.contentWindow;
21982 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21986 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21987 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21992 initEditor : function(){
21993 //console.log("INIT EDITOR");
21994 this.assignDocWin();
21998 this.doc.designMode="on";
22000 this.doc.write(this.getDocMarkup());
22003 var dbody = (this.doc.body || this.doc.documentElement);
22004 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22005 // this copies styles from the containing element into thsi one..
22006 // not sure why we need all of this..
22007 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22009 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22010 //ss['background-attachment'] = 'fixed'; // w3c
22011 dbody.bgProperties = 'fixed'; // ie
22012 //Roo.DomHelper.applyStyles(dbody, ss);
22013 Roo.EventManager.on(this.doc, {
22014 //'mousedown': this.onEditorEvent,
22015 'mouseup': this.onEditorEvent,
22016 'dblclick': this.onEditorEvent,
22017 'click': this.onEditorEvent,
22018 'keyup': this.onEditorEvent,
22023 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22025 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22026 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22028 this.initialized = true;
22030 this.owner.fireEvent('initialize', this);
22035 onDestroy : function(){
22041 //for (var i =0; i < this.toolbars.length;i++) {
22042 // // fixme - ask toolbars for heights?
22043 // this.toolbars[i].onDestroy();
22046 //this.wrap.dom.innerHTML = '';
22047 //this.wrap.remove();
22052 onFirstFocus : function(){
22054 this.assignDocWin();
22057 this.activated = true;
22060 if(Roo.isGecko){ // prevent silly gecko errors
22062 var s = this.win.getSelection();
22063 if(!s.focusNode || s.focusNode.nodeType != 3){
22064 var r = s.getRangeAt(0);
22065 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22070 this.execCmd('useCSS', true);
22071 this.execCmd('styleWithCSS', false);
22074 this.owner.fireEvent('activate', this);
22078 adjustFont: function(btn){
22079 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22080 //if(Roo.isSafari){ // safari
22083 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22084 if(Roo.isSafari){ // safari
22085 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22086 v = (v < 10) ? 10 : v;
22087 v = (v > 48) ? 48 : v;
22088 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22093 v = Math.max(1, v+adjust);
22095 this.execCmd('FontSize', v );
22098 onEditorEvent : function(e)
22100 this.owner.fireEvent('editorevent', this, e);
22101 // this.updateToolbar();
22102 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22105 insertTag : function(tg)
22107 // could be a bit smarter... -> wrap the current selected tRoo..
22108 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22110 range = this.createRange(this.getSelection());
22111 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22112 wrappingNode.appendChild(range.extractContents());
22113 range.insertNode(wrappingNode);
22120 this.execCmd("formatblock", tg);
22124 insertText : function(txt)
22128 var range = this.createRange();
22129 range.deleteContents();
22130 //alert(Sender.getAttribute('label'));
22132 range.insertNode(this.doc.createTextNode(txt));
22138 * Executes a Midas editor command on the editor document and performs necessary focus and
22139 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22140 * @param {String} cmd The Midas command
22141 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22143 relayCmd : function(cmd, value){
22145 this.execCmd(cmd, value);
22146 this.owner.fireEvent('editorevent', this);
22147 //this.updateToolbar();
22148 this.owner.deferFocus();
22152 * Executes a Midas editor command directly on the editor document.
22153 * For visual commands, you should use {@link #relayCmd} instead.
22154 * <b>This should only be called after the editor is initialized.</b>
22155 * @param {String} cmd The Midas command
22156 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22158 execCmd : function(cmd, value){
22159 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22166 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22168 * @param {String} text | dom node..
22170 insertAtCursor : function(text)
22173 if(!this.activated){
22179 var r = this.doc.selection.createRange();
22190 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22194 // from jquery ui (MIT licenced)
22196 var win = this.win;
22198 if (win.getSelection && win.getSelection().getRangeAt) {
22199 range = win.getSelection().getRangeAt(0);
22200 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22201 range.insertNode(node);
22202 } else if (win.document.selection && win.document.selection.createRange) {
22203 // no firefox support
22204 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22205 win.document.selection.createRange().pasteHTML(txt);
22207 // no firefox support
22208 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22209 this.execCmd('InsertHTML', txt);
22218 mozKeyPress : function(e){
22220 var c = e.getCharCode(), cmd;
22223 c = String.fromCharCode(c).toLowerCase();
22237 this.cleanUpPaste.defer(100, this);
22245 e.preventDefault();
22253 fixKeys : function(){ // load time branching for fastest keydown performance
22255 return function(e){
22256 var k = e.getKey(), r;
22259 r = this.doc.selection.createRange();
22262 r.pasteHTML('    ');
22269 r = this.doc.selection.createRange();
22271 var target = r.parentElement();
22272 if(!target || target.tagName.toLowerCase() != 'li'){
22274 r.pasteHTML('<br />');
22280 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22281 this.cleanUpPaste.defer(100, this);
22287 }else if(Roo.isOpera){
22288 return function(e){
22289 var k = e.getKey();
22293 this.execCmd('InsertHTML','    ');
22296 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22297 this.cleanUpPaste.defer(100, this);
22302 }else if(Roo.isSafari){
22303 return function(e){
22304 var k = e.getKey();
22308 this.execCmd('InsertText','\t');
22312 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22313 this.cleanUpPaste.defer(100, this);
22321 getAllAncestors: function()
22323 var p = this.getSelectedNode();
22326 a.push(p); // push blank onto stack..
22327 p = this.getParentElement();
22331 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22335 a.push(this.doc.body);
22339 lastSelNode : false,
22342 getSelection : function()
22344 this.assignDocWin();
22345 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22348 getSelectedNode: function()
22350 // this may only work on Gecko!!!
22352 // should we cache this!!!!
22357 var range = this.createRange(this.getSelection()).cloneRange();
22360 var parent = range.parentElement();
22362 var testRange = range.duplicate();
22363 testRange.moveToElementText(parent);
22364 if (testRange.inRange(range)) {
22367 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22370 parent = parent.parentElement;
22375 // is ancestor a text element.
22376 var ac = range.commonAncestorContainer;
22377 if (ac.nodeType == 3) {
22378 ac = ac.parentNode;
22381 var ar = ac.childNodes;
22384 var other_nodes = [];
22385 var has_other_nodes = false;
22386 for (var i=0;i<ar.length;i++) {
22387 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22390 // fullly contained node.
22392 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22397 // probably selected..
22398 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22399 other_nodes.push(ar[i]);
22403 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22408 has_other_nodes = true;
22410 if (!nodes.length && other_nodes.length) {
22411 nodes= other_nodes;
22413 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22419 createRange: function(sel)
22421 // this has strange effects when using with
22422 // top toolbar - not sure if it's a great idea.
22423 //this.editor.contentWindow.focus();
22424 if (typeof sel != "undefined") {
22426 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22428 return this.doc.createRange();
22431 return this.doc.createRange();
22434 getParentElement: function()
22437 this.assignDocWin();
22438 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22440 var range = this.createRange(sel);
22443 var p = range.commonAncestorContainer;
22444 while (p.nodeType == 3) { // text node
22455 * Range intersection.. the hard stuff...
22459 * [ -- selected range --- ]
22463 * if end is before start or hits it. fail.
22464 * if start is after end or hits it fail.
22466 * if either hits (but other is outside. - then it's not
22472 // @see http://www.thismuchiknow.co.uk/?p=64.
22473 rangeIntersectsNode : function(range, node)
22475 var nodeRange = node.ownerDocument.createRange();
22477 nodeRange.selectNode(node);
22479 nodeRange.selectNodeContents(node);
22482 var rangeStartRange = range.cloneRange();
22483 rangeStartRange.collapse(true);
22485 var rangeEndRange = range.cloneRange();
22486 rangeEndRange.collapse(false);
22488 var nodeStartRange = nodeRange.cloneRange();
22489 nodeStartRange.collapse(true);
22491 var nodeEndRange = nodeRange.cloneRange();
22492 nodeEndRange.collapse(false);
22494 return rangeStartRange.compareBoundaryPoints(
22495 Range.START_TO_START, nodeEndRange) == -1 &&
22496 rangeEndRange.compareBoundaryPoints(
22497 Range.START_TO_START, nodeStartRange) == 1;
22501 rangeCompareNode : function(range, node)
22503 var nodeRange = node.ownerDocument.createRange();
22505 nodeRange.selectNode(node);
22507 nodeRange.selectNodeContents(node);
22511 range.collapse(true);
22513 nodeRange.collapse(true);
22515 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22516 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22518 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22520 var nodeIsBefore = ss == 1;
22521 var nodeIsAfter = ee == -1;
22523 if (nodeIsBefore && nodeIsAfter) {
22526 if (!nodeIsBefore && nodeIsAfter) {
22527 return 1; //right trailed.
22530 if (nodeIsBefore && !nodeIsAfter) {
22531 return 2; // left trailed.
22537 // private? - in a new class?
22538 cleanUpPaste : function()
22540 // cleans up the whole document..
22541 Roo.log('cleanuppaste');
22543 this.cleanUpChildren(this.doc.body);
22544 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22545 if (clean != this.doc.body.innerHTML) {
22546 this.doc.body.innerHTML = clean;
22551 cleanWordChars : function(input) {// change the chars to hex code
22552 var he = Roo.HtmlEditorCore;
22554 var output = input;
22555 Roo.each(he.swapCodes, function(sw) {
22556 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22558 output = output.replace(swapper, sw[1]);
22565 cleanUpChildren : function (n)
22567 if (!n.childNodes.length) {
22570 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22571 this.cleanUpChild(n.childNodes[i]);
22578 cleanUpChild : function (node)
22581 //console.log(node);
22582 if (node.nodeName == "#text") {
22583 // clean up silly Windows -- stuff?
22586 if (node.nodeName == "#comment") {
22587 node.parentNode.removeChild(node);
22588 // clean up silly Windows -- stuff?
22591 var lcname = node.tagName.toLowerCase();
22592 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22593 // whitelist of tags..
22595 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22597 node.parentNode.removeChild(node);
22602 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22604 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22605 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22607 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22608 // remove_keep_children = true;
22611 if (remove_keep_children) {
22612 this.cleanUpChildren(node);
22613 // inserts everything just before this node...
22614 while (node.childNodes.length) {
22615 var cn = node.childNodes[0];
22616 node.removeChild(cn);
22617 node.parentNode.insertBefore(cn, node);
22619 node.parentNode.removeChild(node);
22623 if (!node.attributes || !node.attributes.length) {
22624 this.cleanUpChildren(node);
22628 function cleanAttr(n,v)
22631 if (v.match(/^\./) || v.match(/^\//)) {
22634 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22637 if (v.match(/^#/)) {
22640 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22641 node.removeAttribute(n);
22645 var cwhite = this.cwhite;
22646 var cblack = this.cblack;
22648 function cleanStyle(n,v)
22650 if (v.match(/expression/)) { //XSS?? should we even bother..
22651 node.removeAttribute(n);
22655 var parts = v.split(/;/);
22658 Roo.each(parts, function(p) {
22659 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22663 var l = p.split(':').shift().replace(/\s+/g,'');
22664 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22666 if ( cwhite.length && cblack.indexOf(l) > -1) {
22667 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22668 //node.removeAttribute(n);
22672 // only allow 'c whitelisted system attributes'
22673 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22674 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22675 //node.removeAttribute(n);
22685 if (clean.length) {
22686 node.setAttribute(n, clean.join(';'));
22688 node.removeAttribute(n);
22694 for (var i = node.attributes.length-1; i > -1 ; i--) {
22695 var a = node.attributes[i];
22698 if (a.name.toLowerCase().substr(0,2)=='on') {
22699 node.removeAttribute(a.name);
22702 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22703 node.removeAttribute(a.name);
22706 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22707 cleanAttr(a.name,a.value); // fixme..
22710 if (a.name == 'style') {
22711 cleanStyle(a.name,a.value);
22714 /// clean up MS crap..
22715 // tecnically this should be a list of valid class'es..
22718 if (a.name == 'class') {
22719 if (a.value.match(/^Mso/)) {
22720 node.className = '';
22723 if (a.value.match(/^body$/)) {
22724 node.className = '';
22735 this.cleanUpChildren(node);
22741 * Clean up MS wordisms...
22743 cleanWord : function(node)
22748 this.cleanWord(this.doc.body);
22751 if (node.nodeName == "#text") {
22752 // clean up silly Windows -- stuff?
22755 if (node.nodeName == "#comment") {
22756 node.parentNode.removeChild(node);
22757 // clean up silly Windows -- stuff?
22761 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22762 node.parentNode.removeChild(node);
22766 // remove - but keep children..
22767 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22768 while (node.childNodes.length) {
22769 var cn = node.childNodes[0];
22770 node.removeChild(cn);
22771 node.parentNode.insertBefore(cn, node);
22773 node.parentNode.removeChild(node);
22774 this.iterateChildren(node, this.cleanWord);
22778 if (node.className.length) {
22780 var cn = node.className.split(/\W+/);
22782 Roo.each(cn, function(cls) {
22783 if (cls.match(/Mso[a-zA-Z]+/)) {
22788 node.className = cna.length ? cna.join(' ') : '';
22790 node.removeAttribute("class");
22794 if (node.hasAttribute("lang")) {
22795 node.removeAttribute("lang");
22798 if (node.hasAttribute("style")) {
22800 var styles = node.getAttribute("style").split(";");
22802 Roo.each(styles, function(s) {
22803 if (!s.match(/:/)) {
22806 var kv = s.split(":");
22807 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22810 // what ever is left... we allow.
22813 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22814 if (!nstyle.length) {
22815 node.removeAttribute('style');
22818 this.iterateChildren(node, this.cleanWord);
22824 * iterateChildren of a Node, calling fn each time, using this as the scole..
22825 * @param {DomNode} node node to iterate children of.
22826 * @param {Function} fn method of this class to call on each item.
22828 iterateChildren : function(node, fn)
22830 if (!node.childNodes.length) {
22833 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22834 fn.call(this, node.childNodes[i])
22840 * cleanTableWidths.
22842 * Quite often pasting from word etc.. results in tables with column and widths.
22843 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22846 cleanTableWidths : function(node)
22851 this.cleanTableWidths(this.doc.body);
22856 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22859 Roo.log(node.tagName);
22860 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22861 this.iterateChildren(node, this.cleanTableWidths);
22864 if (node.hasAttribute('width')) {
22865 node.removeAttribute('width');
22869 if (node.hasAttribute("style")) {
22872 var styles = node.getAttribute("style").split(";");
22874 Roo.each(styles, function(s) {
22875 if (!s.match(/:/)) {
22878 var kv = s.split(":");
22879 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22882 // what ever is left... we allow.
22885 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22886 if (!nstyle.length) {
22887 node.removeAttribute('style');
22891 this.iterateChildren(node, this.cleanTableWidths);
22899 domToHTML : function(currentElement, depth, nopadtext) {
22901 depth = depth || 0;
22902 nopadtext = nopadtext || false;
22904 if (!currentElement) {
22905 return this.domToHTML(this.doc.body);
22908 //Roo.log(currentElement);
22910 var allText = false;
22911 var nodeName = currentElement.nodeName;
22912 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22914 if (nodeName == '#text') {
22916 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22921 if (nodeName != 'BODY') {
22924 // Prints the node tagName, such as <A>, <IMG>, etc
22927 for(i = 0; i < currentElement.attributes.length;i++) {
22929 var aname = currentElement.attributes.item(i).name;
22930 if (!currentElement.attributes.item(i).value.length) {
22933 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22936 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22945 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22948 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22953 // Traverse the tree
22955 var currentElementChild = currentElement.childNodes.item(i);
22956 var allText = true;
22957 var innerHTML = '';
22959 while (currentElementChild) {
22960 // Formatting code (indent the tree so it looks nice on the screen)
22961 var nopad = nopadtext;
22962 if (lastnode == 'SPAN') {
22966 if (currentElementChild.nodeName == '#text') {
22967 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22968 toadd = nopadtext ? toadd : toadd.trim();
22969 if (!nopad && toadd.length > 80) {
22970 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22972 innerHTML += toadd;
22975 currentElementChild = currentElement.childNodes.item(i);
22981 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22983 // Recursively traverse the tree structure of the child node
22984 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22985 lastnode = currentElementChild.nodeName;
22987 currentElementChild=currentElement.childNodes.item(i);
22993 // The remaining code is mostly for formatting the tree
22994 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22999 ret+= "</"+tagName+">";
23005 applyBlacklists : function()
23007 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23008 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23012 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23013 if (b.indexOf(tag) > -1) {
23016 this.white.push(tag);
23020 Roo.each(w, function(tag) {
23021 if (b.indexOf(tag) > -1) {
23024 if (this.white.indexOf(tag) > -1) {
23027 this.white.push(tag);
23032 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23033 if (w.indexOf(tag) > -1) {
23036 this.black.push(tag);
23040 Roo.each(b, function(tag) {
23041 if (w.indexOf(tag) > -1) {
23044 if (this.black.indexOf(tag) > -1) {
23047 this.black.push(tag);
23052 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23053 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23057 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23058 if (b.indexOf(tag) > -1) {
23061 this.cwhite.push(tag);
23065 Roo.each(w, function(tag) {
23066 if (b.indexOf(tag) > -1) {
23069 if (this.cwhite.indexOf(tag) > -1) {
23072 this.cwhite.push(tag);
23077 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23078 if (w.indexOf(tag) > -1) {
23081 this.cblack.push(tag);
23085 Roo.each(b, function(tag) {
23086 if (w.indexOf(tag) > -1) {
23089 if (this.cblack.indexOf(tag) > -1) {
23092 this.cblack.push(tag);
23097 setStylesheets : function(stylesheets)
23099 if(typeof(stylesheets) == 'string'){
23100 Roo.get(this.iframe.contentDocument.head).createChild({
23102 rel : 'stylesheet',
23111 Roo.each(stylesheets, function(s) {
23116 Roo.get(_this.iframe.contentDocument.head).createChild({
23118 rel : 'stylesheet',
23127 removeStylesheets : function()
23131 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23136 setStyle : function(style)
23138 Roo.get(this.iframe.contentDocument.head).createChild({
23147 // hide stuff that is not compatible
23161 * @event specialkey
23165 * @cfg {String} fieldClass @hide
23168 * @cfg {String} focusClass @hide
23171 * @cfg {String} autoCreate @hide
23174 * @cfg {String} inputType @hide
23177 * @cfg {String} invalidClass @hide
23180 * @cfg {String} invalidText @hide
23183 * @cfg {String} msgFx @hide
23186 * @cfg {String} validateOnBlur @hide
23190 Roo.HtmlEditorCore.white = [
23191 'area', 'br', 'img', 'input', 'hr', 'wbr',
23193 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23194 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23195 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23196 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23197 'table', 'ul', 'xmp',
23199 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23202 'dir', 'menu', 'ol', 'ul', 'dl',
23208 Roo.HtmlEditorCore.black = [
23209 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23211 'base', 'basefont', 'bgsound', 'blink', 'body',
23212 'frame', 'frameset', 'head', 'html', 'ilayer',
23213 'iframe', 'layer', 'link', 'meta', 'object',
23214 'script', 'style' ,'title', 'xml' // clean later..
23216 Roo.HtmlEditorCore.clean = [
23217 'script', 'style', 'title', 'xml'
23219 Roo.HtmlEditorCore.remove = [
23224 Roo.HtmlEditorCore.ablack = [
23228 Roo.HtmlEditorCore.aclean = [
23229 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23233 Roo.HtmlEditorCore.pwhite= [
23234 'http', 'https', 'mailto'
23237 // white listed style attributes.
23238 Roo.HtmlEditorCore.cwhite= [
23239 // 'text-align', /// default is to allow most things..
23245 // black listed style attributes.
23246 Roo.HtmlEditorCore.cblack= [
23247 // 'font-size' -- this can be set by the project
23251 Roo.HtmlEditorCore.swapCodes =[
23270 * @class Roo.bootstrap.HtmlEditor
23271 * @extends Roo.bootstrap.TextArea
23272 * Bootstrap HtmlEditor class
23275 * Create a new HtmlEditor
23276 * @param {Object} config The config object
23279 Roo.bootstrap.HtmlEditor = function(config){
23280 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23281 if (!this.toolbars) {
23282 this.toolbars = [];
23285 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23288 * @event initialize
23289 * Fires when the editor is fully initialized (including the iframe)
23290 * @param {HtmlEditor} this
23295 * Fires when the editor is first receives the focus. Any insertion must wait
23296 * until after this event.
23297 * @param {HtmlEditor} this
23301 * @event beforesync
23302 * Fires before the textarea is updated with content from the editor iframe. Return false
23303 * to cancel the sync.
23304 * @param {HtmlEditor} this
23305 * @param {String} html
23309 * @event beforepush
23310 * Fires before the iframe editor is updated with content from the textarea. Return false
23311 * to cancel the push.
23312 * @param {HtmlEditor} this
23313 * @param {String} html
23318 * Fires when the textarea is updated with content from the editor iframe.
23319 * @param {HtmlEditor} this
23320 * @param {String} html
23325 * Fires when the iframe editor is updated with content from the textarea.
23326 * @param {HtmlEditor} this
23327 * @param {String} html
23331 * @event editmodechange
23332 * Fires when the editor switches edit modes
23333 * @param {HtmlEditor} this
23334 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23336 editmodechange: true,
23338 * @event editorevent
23339 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23340 * @param {HtmlEditor} this
23344 * @event firstfocus
23345 * Fires when on first focus - needed by toolbars..
23346 * @param {HtmlEditor} this
23351 * Auto save the htmlEditor value as a file into Events
23352 * @param {HtmlEditor} this
23356 * @event savedpreview
23357 * preview the saved version of htmlEditor
23358 * @param {HtmlEditor} this
23365 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23369 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23374 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23379 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23384 * @cfg {Number} height (in pixels)
23388 * @cfg {Number} width (in pixels)
23393 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23396 stylesheets: false,
23401 // private properties
23402 validationEvent : false,
23404 initialized : false,
23407 onFocus : Roo.emptyFn,
23409 hideMode:'offsets',
23411 tbContainer : false,
23415 toolbarContainer :function() {
23416 return this.wrap.select('.x-html-editor-tb',true).first();
23420 * Protected method that will not generally be called directly. It
23421 * is called when the editor creates its toolbar. Override this method if you need to
23422 * add custom toolbar buttons.
23423 * @param {HtmlEditor} editor
23425 createToolbar : function(){
23426 Roo.log('renewing');
23427 Roo.log("create toolbars");
23429 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23430 this.toolbars[0].render(this.toolbarContainer());
23434 // if (!editor.toolbars || !editor.toolbars.length) {
23435 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23438 // for (var i =0 ; i < editor.toolbars.length;i++) {
23439 // editor.toolbars[i] = Roo.factory(
23440 // typeof(editor.toolbars[i]) == 'string' ?
23441 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23442 // Roo.bootstrap.HtmlEditor);
23443 // editor.toolbars[i].init(editor);
23449 onRender : function(ct, position)
23451 // Roo.log("Call onRender: " + this.xtype);
23453 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23455 this.wrap = this.inputEl().wrap({
23456 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23459 this.editorcore.onRender(ct, position);
23461 if (this.resizable) {
23462 this.resizeEl = new Roo.Resizable(this.wrap, {
23466 minHeight : this.height,
23467 height: this.height,
23468 handles : this.resizable,
23471 resize : function(r, w, h) {
23472 _t.onResize(w,h); // -something
23478 this.createToolbar(this);
23481 if(!this.width && this.resizable){
23482 this.setSize(this.wrap.getSize());
23484 if (this.resizeEl) {
23485 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23486 // should trigger onReize..
23492 onResize : function(w, h)
23494 Roo.log('resize: ' +w + ',' + h );
23495 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23499 if(this.inputEl() ){
23500 if(typeof w == 'number'){
23501 var aw = w - this.wrap.getFrameWidth('lr');
23502 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23505 if(typeof h == 'number'){
23506 var tbh = -11; // fixme it needs to tool bar size!
23507 for (var i =0; i < this.toolbars.length;i++) {
23508 // fixme - ask toolbars for heights?
23509 tbh += this.toolbars[i].el.getHeight();
23510 //if (this.toolbars[i].footer) {
23511 // tbh += this.toolbars[i].footer.el.getHeight();
23519 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23520 ah -= 5; // knock a few pixes off for look..
23521 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23525 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23526 this.editorcore.onResize(ew,eh);
23531 * Toggles the editor between standard and source edit mode.
23532 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23534 toggleSourceEdit : function(sourceEditMode)
23536 this.editorcore.toggleSourceEdit(sourceEditMode);
23538 if(this.editorcore.sourceEditMode){
23539 Roo.log('editor - showing textarea');
23542 // Roo.log(this.syncValue());
23544 this.inputEl().removeClass(['hide', 'x-hidden']);
23545 this.inputEl().dom.removeAttribute('tabIndex');
23546 this.inputEl().focus();
23548 Roo.log('editor - hiding textarea');
23550 // Roo.log(this.pushValue());
23553 this.inputEl().addClass(['hide', 'x-hidden']);
23554 this.inputEl().dom.setAttribute('tabIndex', -1);
23555 //this.deferFocus();
23558 if(this.resizable){
23559 this.setSize(this.wrap.getSize());
23562 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23565 // private (for BoxComponent)
23566 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23568 // private (for BoxComponent)
23569 getResizeEl : function(){
23573 // private (for BoxComponent)
23574 getPositionEl : function(){
23579 initEvents : function(){
23580 this.originalValue = this.getValue();
23584 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23587 // markInvalid : Roo.emptyFn,
23589 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23592 // clearInvalid : Roo.emptyFn,
23594 setValue : function(v){
23595 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23596 this.editorcore.pushValue();
23601 deferFocus : function(){
23602 this.focus.defer(10, this);
23606 focus : function(){
23607 this.editorcore.focus();
23613 onDestroy : function(){
23619 for (var i =0; i < this.toolbars.length;i++) {
23620 // fixme - ask toolbars for heights?
23621 this.toolbars[i].onDestroy();
23624 this.wrap.dom.innerHTML = '';
23625 this.wrap.remove();
23630 onFirstFocus : function(){
23631 //Roo.log("onFirstFocus");
23632 this.editorcore.onFirstFocus();
23633 for (var i =0; i < this.toolbars.length;i++) {
23634 this.toolbars[i].onFirstFocus();
23640 syncValue : function()
23642 this.editorcore.syncValue();
23645 pushValue : function()
23647 this.editorcore.pushValue();
23651 // hide stuff that is not compatible
23665 * @event specialkey
23669 * @cfg {String} fieldClass @hide
23672 * @cfg {String} focusClass @hide
23675 * @cfg {String} autoCreate @hide
23678 * @cfg {String} inputType @hide
23681 * @cfg {String} invalidClass @hide
23684 * @cfg {String} invalidText @hide
23687 * @cfg {String} msgFx @hide
23690 * @cfg {String} validateOnBlur @hide
23699 Roo.namespace('Roo.bootstrap.htmleditor');
23701 * @class Roo.bootstrap.HtmlEditorToolbar1
23706 new Roo.bootstrap.HtmlEditor({
23709 new Roo.bootstrap.HtmlEditorToolbar1({
23710 disable : { fonts: 1 , format: 1, ..., ... , ...],
23716 * @cfg {Object} disable List of elements to disable..
23717 * @cfg {Array} btns List of additional buttons.
23721 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23724 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23727 Roo.apply(this, config);
23729 // default disabled, based on 'good practice'..
23730 this.disable = this.disable || {};
23731 Roo.applyIf(this.disable, {
23734 specialElements : true
23736 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23738 this.editor = config.editor;
23739 this.editorcore = config.editor.editorcore;
23741 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23743 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23744 // dont call parent... till later.
23746 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23751 editorcore : false,
23756 "h1","h2","h3","h4","h5","h6",
23758 "abbr", "acronym", "address", "cite", "samp", "var",
23762 onRender : function(ct, position)
23764 // Roo.log("Call onRender: " + this.xtype);
23766 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23768 this.el.dom.style.marginBottom = '0';
23770 var editorcore = this.editorcore;
23771 var editor= this.editor;
23774 var btn = function(id,cmd , toggle, handler, html){
23776 var event = toggle ? 'toggle' : 'click';
23781 xns: Roo.bootstrap,
23784 enableToggle:toggle !== false,
23786 pressed : toggle ? false : null,
23789 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23790 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23796 // var cb_box = function...
23801 xns: Roo.bootstrap,
23802 glyphicon : 'font',
23806 xns: Roo.bootstrap,
23810 Roo.each(this.formats, function(f) {
23811 style.menu.items.push({
23813 xns: Roo.bootstrap,
23814 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23819 editorcore.insertTag(this.tagname);
23826 children.push(style);
23828 btn('bold',false,true);
23829 btn('italic',false,true);
23830 btn('align-left', 'justifyleft',true);
23831 btn('align-center', 'justifycenter',true);
23832 btn('align-right' , 'justifyright',true);
23833 btn('link', false, false, function(btn) {
23834 //Roo.log("create link?");
23835 var url = prompt(this.createLinkText, this.defaultLinkValue);
23836 if(url && url != 'http:/'+'/'){
23837 this.editorcore.relayCmd('createlink', url);
23840 btn('list','insertunorderedlist',true);
23841 btn('pencil', false,true, function(btn){
23843 this.toggleSourceEdit(btn.pressed);
23846 if (this.editor.btns.length > 0) {
23847 for (var i = 0; i<this.editor.btns.length; i++) {
23848 children.push(this.editor.btns[i]);
23856 xns: Roo.bootstrap,
23861 xns: Roo.bootstrap,
23866 cog.menu.items.push({
23868 xns: Roo.bootstrap,
23869 html : Clean styles,
23874 editorcore.insertTag(this.tagname);
23883 this.xtype = 'NavSimplebar';
23885 for(var i=0;i< children.length;i++) {
23887 this.buttons.add(this.addxtypeChild(children[i]));
23891 editor.on('editorevent', this.updateToolbar, this);
23893 onBtnClick : function(id)
23895 this.editorcore.relayCmd(id);
23896 this.editorcore.focus();
23900 * Protected method that will not generally be called directly. It triggers
23901 * a toolbar update by reading the markup state of the current selection in the editor.
23903 updateToolbar: function(){
23905 if(!this.editorcore.activated){
23906 this.editor.onFirstFocus(); // is this neeed?
23910 var btns = this.buttons;
23911 var doc = this.editorcore.doc;
23912 btns.get('bold').setActive(doc.queryCommandState('bold'));
23913 btns.get('italic').setActive(doc.queryCommandState('italic'));
23914 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23916 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23917 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23918 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23920 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23921 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23924 var ans = this.editorcore.getAllAncestors();
23925 if (this.formatCombo) {
23928 var store = this.formatCombo.store;
23929 this.formatCombo.setValue("");
23930 for (var i =0; i < ans.length;i++) {
23931 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23933 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23941 // hides menus... - so this cant be on a menu...
23942 Roo.bootstrap.MenuMgr.hideAll();
23944 Roo.bootstrap.MenuMgr.hideAll();
23945 //this.editorsyncValue();
23947 onFirstFocus: function() {
23948 this.buttons.each(function(item){
23952 toggleSourceEdit : function(sourceEditMode){
23955 if(sourceEditMode){
23956 Roo.log("disabling buttons");
23957 this.buttons.each( function(item){
23958 if(item.cmd != 'pencil'){
23964 Roo.log("enabling buttons");
23965 if(this.editorcore.initialized){
23966 this.buttons.each( function(item){
23972 Roo.log("calling toggole on editor");
23973 // tell the editor that it's been pressed..
23974 this.editor.toggleSourceEdit(sourceEditMode);
23984 * @class Roo.bootstrap.Table.AbstractSelectionModel
23985 * @extends Roo.util.Observable
23986 * Abstract base class for grid SelectionModels. It provides the interface that should be
23987 * implemented by descendant classes. This class should not be directly instantiated.
23990 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23991 this.locked = false;
23992 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23996 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23997 /** @ignore Called by the grid automatically. Do not call directly. */
23998 init : function(grid){
24004 * Locks the selections.
24007 this.locked = true;
24011 * Unlocks the selections.
24013 unlock : function(){
24014 this.locked = false;
24018 * Returns true if the selections are locked.
24019 * @return {Boolean}
24021 isLocked : function(){
24022 return this.locked;
24026 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24027 * @class Roo.bootstrap.Table.RowSelectionModel
24028 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24029 * It supports multiple selections and keyboard selection/navigation.
24031 * @param {Object} config
24034 Roo.bootstrap.Table.RowSelectionModel = function(config){
24035 Roo.apply(this, config);
24036 this.selections = new Roo.util.MixedCollection(false, function(o){
24041 this.lastActive = false;
24045 * @event selectionchange
24046 * Fires when the selection changes
24047 * @param {SelectionModel} this
24049 "selectionchange" : true,
24051 * @event afterselectionchange
24052 * Fires after the selection changes (eg. by key press or clicking)
24053 * @param {SelectionModel} this
24055 "afterselectionchange" : true,
24057 * @event beforerowselect
24058 * Fires when a row is selected being selected, return false to cancel.
24059 * @param {SelectionModel} this
24060 * @param {Number} rowIndex The selected index
24061 * @param {Boolean} keepExisting False if other selections will be cleared
24063 "beforerowselect" : true,
24066 * Fires when a row is selected.
24067 * @param {SelectionModel} this
24068 * @param {Number} rowIndex The selected index
24069 * @param {Roo.data.Record} r The record
24071 "rowselect" : true,
24073 * @event rowdeselect
24074 * Fires when a row is deselected.
24075 * @param {SelectionModel} this
24076 * @param {Number} rowIndex The selected index
24078 "rowdeselect" : true
24080 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24081 this.locked = false;
24084 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24086 * @cfg {Boolean} singleSelect
24087 * True to allow selection of only one row at a time (defaults to false)
24089 singleSelect : false,
24092 initEvents : function()
24095 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24096 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24097 //}else{ // allow click to work like normal
24098 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24100 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24101 this.grid.on("rowclick", this.handleMouseDown, this);
24103 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24104 "up" : function(e){
24106 this.selectPrevious(e.shiftKey);
24107 }else if(this.last !== false && this.lastActive !== false){
24108 var last = this.last;
24109 this.selectRange(this.last, this.lastActive-1);
24110 this.grid.getView().focusRow(this.lastActive);
24111 if(last !== false){
24115 this.selectFirstRow();
24117 this.fireEvent("afterselectionchange", this);
24119 "down" : function(e){
24121 this.selectNext(e.shiftKey);
24122 }else if(this.last !== false && this.lastActive !== false){
24123 var last = this.last;
24124 this.selectRange(this.last, this.lastActive+1);
24125 this.grid.getView().focusRow(this.lastActive);
24126 if(last !== false){
24130 this.selectFirstRow();
24132 this.fireEvent("afterselectionchange", this);
24136 this.grid.store.on('load', function(){
24137 this.selections.clear();
24140 var view = this.grid.view;
24141 view.on("refresh", this.onRefresh, this);
24142 view.on("rowupdated", this.onRowUpdated, this);
24143 view.on("rowremoved", this.onRemove, this);
24148 onRefresh : function()
24150 var ds = this.grid.store, i, v = this.grid.view;
24151 var s = this.selections;
24152 s.each(function(r){
24153 if((i = ds.indexOfId(r.id)) != -1){
24162 onRemove : function(v, index, r){
24163 this.selections.remove(r);
24167 onRowUpdated : function(v, index, r){
24168 if(this.isSelected(r)){
24169 v.onRowSelect(index);
24175 * @param {Array} records The records to select
24176 * @param {Boolean} keepExisting (optional) True to keep existing selections
24178 selectRecords : function(records, keepExisting)
24181 this.clearSelections();
24183 var ds = this.grid.store;
24184 for(var i = 0, len = records.length; i < len; i++){
24185 this.selectRow(ds.indexOf(records[i]), true);
24190 * Gets the number of selected rows.
24193 getCount : function(){
24194 return this.selections.length;
24198 * Selects the first row in the grid.
24200 selectFirstRow : function(){
24205 * Select the last row.
24206 * @param {Boolean} keepExisting (optional) True to keep existing selections
24208 selectLastRow : function(keepExisting){
24209 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24210 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24214 * Selects the row immediately following the last selected row.
24215 * @param {Boolean} keepExisting (optional) True to keep existing selections
24217 selectNext : function(keepExisting)
24219 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24220 this.selectRow(this.last+1, keepExisting);
24221 this.grid.getView().focusRow(this.last);
24226 * Selects the row that precedes the last selected row.
24227 * @param {Boolean} keepExisting (optional) True to keep existing selections
24229 selectPrevious : function(keepExisting){
24231 this.selectRow(this.last-1, keepExisting);
24232 this.grid.getView().focusRow(this.last);
24237 * Returns the selected records
24238 * @return {Array} Array of selected records
24240 getSelections : function(){
24241 return [].concat(this.selections.items);
24245 * Returns the first selected record.
24248 getSelected : function(){
24249 return this.selections.itemAt(0);
24254 * Clears all selections.
24256 clearSelections : function(fast)
24262 var ds = this.grid.store;
24263 var s = this.selections;
24264 s.each(function(r){
24265 this.deselectRow(ds.indexOfId(r.id));
24269 this.selections.clear();
24276 * Selects all rows.
24278 selectAll : function(){
24282 this.selections.clear();
24283 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24284 this.selectRow(i, true);
24289 * Returns True if there is a selection.
24290 * @return {Boolean}
24292 hasSelection : function(){
24293 return this.selections.length > 0;
24297 * Returns True if the specified row is selected.
24298 * @param {Number/Record} record The record or index of the record to check
24299 * @return {Boolean}
24301 isSelected : function(index){
24302 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24303 return (r && this.selections.key(r.id) ? true : false);
24307 * Returns True if the specified record id is selected.
24308 * @param {String} id The id of record to check
24309 * @return {Boolean}
24311 isIdSelected : function(id){
24312 return (this.selections.key(id) ? true : false);
24317 handleMouseDBClick : function(e, t){
24321 handleMouseDown : function(e, t)
24323 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24324 if(this.isLocked() || rowIndex < 0 ){
24327 if(e.shiftKey && this.last !== false){
24328 var last = this.last;
24329 this.selectRange(last, rowIndex, e.ctrlKey);
24330 this.last = last; // reset the last
24334 var isSelected = this.isSelected(rowIndex);
24335 //Roo.log("select row:" + rowIndex);
24337 this.deselectRow(rowIndex);
24339 this.selectRow(rowIndex, true);
24343 if(e.button !== 0 && isSelected){
24344 alert('rowIndex 2: ' + rowIndex);
24345 view.focusRow(rowIndex);
24346 }else if(e.ctrlKey && isSelected){
24347 this.deselectRow(rowIndex);
24348 }else if(!isSelected){
24349 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24350 view.focusRow(rowIndex);
24354 this.fireEvent("afterselectionchange", this);
24357 handleDragableRowClick : function(grid, rowIndex, e)
24359 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24360 this.selectRow(rowIndex, false);
24361 grid.view.focusRow(rowIndex);
24362 this.fireEvent("afterselectionchange", this);
24367 * Selects multiple rows.
24368 * @param {Array} rows Array of the indexes of the row to select
24369 * @param {Boolean} keepExisting (optional) True to keep existing selections
24371 selectRows : function(rows, keepExisting){
24373 this.clearSelections();
24375 for(var i = 0, len = rows.length; i < len; i++){
24376 this.selectRow(rows[i], true);
24381 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24382 * @param {Number} startRow The index of the first row in the range
24383 * @param {Number} endRow The index of the last row in the range
24384 * @param {Boolean} keepExisting (optional) True to retain existing selections
24386 selectRange : function(startRow, endRow, keepExisting){
24391 this.clearSelections();
24393 if(startRow <= endRow){
24394 for(var i = startRow; i <= endRow; i++){
24395 this.selectRow(i, true);
24398 for(var i = startRow; i >= endRow; i--){
24399 this.selectRow(i, true);
24405 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24406 * @param {Number} startRow The index of the first row in the range
24407 * @param {Number} endRow The index of the last row in the range
24409 deselectRange : function(startRow, endRow, preventViewNotify){
24413 for(var i = startRow; i <= endRow; i++){
24414 this.deselectRow(i, preventViewNotify);
24420 * @param {Number} row The index of the row to select
24421 * @param {Boolean} keepExisting (optional) True to keep existing selections
24423 selectRow : function(index, keepExisting, preventViewNotify)
24425 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24428 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24429 if(!keepExisting || this.singleSelect){
24430 this.clearSelections();
24433 var r = this.grid.store.getAt(index);
24434 //console.log('selectRow - record id :' + r.id);
24436 this.selections.add(r);
24437 this.last = this.lastActive = index;
24438 if(!preventViewNotify){
24439 var proxy = new Roo.Element(
24440 this.grid.getRowDom(index)
24442 proxy.addClass('bg-info info');
24444 this.fireEvent("rowselect", this, index, r);
24445 this.fireEvent("selectionchange", this);
24451 * @param {Number} row The index of the row to deselect
24453 deselectRow : function(index, preventViewNotify)
24458 if(this.last == index){
24461 if(this.lastActive == index){
24462 this.lastActive = false;
24465 var r = this.grid.store.getAt(index);
24470 this.selections.remove(r);
24471 //.console.log('deselectRow - record id :' + r.id);
24472 if(!preventViewNotify){
24474 var proxy = new Roo.Element(
24475 this.grid.getRowDom(index)
24477 proxy.removeClass('bg-info info');
24479 this.fireEvent("rowdeselect", this, index);
24480 this.fireEvent("selectionchange", this);
24484 restoreLast : function(){
24486 this.last = this._last;
24491 acceptsNav : function(row, col, cm){
24492 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24496 onEditorKey : function(field, e){
24497 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24502 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24504 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24506 }else if(k == e.ENTER && !e.ctrlKey){
24510 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24512 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24514 }else if(k == e.ESC){
24518 g.startEditing(newCell[0], newCell[1]);
24524 * Ext JS Library 1.1.1
24525 * Copyright(c) 2006-2007, Ext JS, LLC.
24527 * Originally Released Under LGPL - original licence link has changed is not relivant.
24530 * <script type="text/javascript">
24534 * @class Roo.bootstrap.PagingToolbar
24535 * @extends Roo.bootstrap.NavSimplebar
24536 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24538 * Create a new PagingToolbar
24539 * @param {Object} config The config object
24540 * @param {Roo.data.Store} store
24542 Roo.bootstrap.PagingToolbar = function(config)
24544 // old args format still supported... - xtype is prefered..
24545 // created from xtype...
24547 this.ds = config.dataSource;
24549 if (config.store && !this.ds) {
24550 this.store= Roo.factory(config.store, Roo.data);
24551 this.ds = this.store;
24552 this.ds.xmodule = this.xmodule || false;
24555 this.toolbarItems = [];
24556 if (config.items) {
24557 this.toolbarItems = config.items;
24560 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24565 this.bind(this.ds);
24568 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24572 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24574 * @cfg {Roo.data.Store} dataSource
24575 * The underlying data store providing the paged data
24578 * @cfg {String/HTMLElement/Element} container
24579 * container The id or element that will contain the toolbar
24582 * @cfg {Boolean} displayInfo
24583 * True to display the displayMsg (defaults to false)
24586 * @cfg {Number} pageSize
24587 * The number of records to display per page (defaults to 20)
24591 * @cfg {String} displayMsg
24592 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24594 displayMsg : 'Displaying {0} - {1} of {2}',
24596 * @cfg {String} emptyMsg
24597 * The message to display when no records are found (defaults to "No data to display")
24599 emptyMsg : 'No data to display',
24601 * Customizable piece of the default paging text (defaults to "Page")
24604 beforePageText : "Page",
24606 * Customizable piece of the default paging text (defaults to "of %0")
24609 afterPageText : "of {0}",
24611 * Customizable piece of the default paging text (defaults to "First Page")
24614 firstText : "First Page",
24616 * Customizable piece of the default paging text (defaults to "Previous Page")
24619 prevText : "Previous Page",
24621 * Customizable piece of the default paging text (defaults to "Next Page")
24624 nextText : "Next Page",
24626 * Customizable piece of the default paging text (defaults to "Last Page")
24629 lastText : "Last Page",
24631 * Customizable piece of the default paging text (defaults to "Refresh")
24634 refreshText : "Refresh",
24638 onRender : function(ct, position)
24640 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24641 this.navgroup.parentId = this.id;
24642 this.navgroup.onRender(this.el, null);
24643 // add the buttons to the navgroup
24645 if(this.displayInfo){
24646 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24647 this.displayEl = this.el.select('.x-paging-info', true).first();
24648 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24649 // this.displayEl = navel.el.select('span',true).first();
24655 Roo.each(_this.buttons, function(e){ // this might need to use render????
24656 Roo.factory(e).render(_this.el);
24660 Roo.each(_this.toolbarItems, function(e) {
24661 _this.navgroup.addItem(e);
24665 this.first = this.navgroup.addItem({
24666 tooltip: this.firstText,
24668 icon : 'fa fa-backward',
24670 preventDefault: true,
24671 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24674 this.prev = this.navgroup.addItem({
24675 tooltip: this.prevText,
24677 icon : 'fa fa-step-backward',
24679 preventDefault: true,
24680 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24682 //this.addSeparator();
24685 var field = this.navgroup.addItem( {
24687 cls : 'x-paging-position',
24689 html : this.beforePageText +
24690 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24691 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24694 this.field = field.el.select('input', true).first();
24695 this.field.on("keydown", this.onPagingKeydown, this);
24696 this.field.on("focus", function(){this.dom.select();});
24699 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24700 //this.field.setHeight(18);
24701 //this.addSeparator();
24702 this.next = this.navgroup.addItem({
24703 tooltip: this.nextText,
24705 html : ' <i class="fa fa-step-forward">',
24707 preventDefault: true,
24708 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24710 this.last = this.navgroup.addItem({
24711 tooltip: this.lastText,
24712 icon : 'fa fa-forward',
24715 preventDefault: true,
24716 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24718 //this.addSeparator();
24719 this.loading = this.navgroup.addItem({
24720 tooltip: this.refreshText,
24721 icon: 'fa fa-refresh',
24722 preventDefault: true,
24723 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24729 updateInfo : function(){
24730 if(this.displayEl){
24731 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24732 var msg = count == 0 ?
24736 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24738 this.displayEl.update(msg);
24743 onLoad : function(ds, r, o)
24745 this.cursor = o.params.start ? o.params.start : 0;
24747 var d = this.getPageData(),
24752 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24753 this.field.dom.value = ap;
24754 this.first.setDisabled(ap == 1);
24755 this.prev.setDisabled(ap == 1);
24756 this.next.setDisabled(ap == ps);
24757 this.last.setDisabled(ap == ps);
24758 this.loading.enable();
24763 getPageData : function(){
24764 var total = this.ds.getTotalCount();
24767 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24768 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24773 onLoadError : function(){
24774 this.loading.enable();
24778 onPagingKeydown : function(e){
24779 var k = e.getKey();
24780 var d = this.getPageData();
24782 var v = this.field.dom.value, pageNum;
24783 if(!v || isNaN(pageNum = parseInt(v, 10))){
24784 this.field.dom.value = d.activePage;
24787 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24788 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24791 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))
24793 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24794 this.field.dom.value = pageNum;
24795 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24798 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24800 var v = this.field.dom.value, pageNum;
24801 var increment = (e.shiftKey) ? 10 : 1;
24802 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24805 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24806 this.field.dom.value = d.activePage;
24809 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24811 this.field.dom.value = parseInt(v, 10) + increment;
24812 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24813 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24820 beforeLoad : function(){
24822 this.loading.disable();
24827 onClick : function(which){
24836 ds.load({params:{start: 0, limit: this.pageSize}});
24839 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24842 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24845 var total = ds.getTotalCount();
24846 var extra = total % this.pageSize;
24847 var lastStart = extra ? (total - extra) : total-this.pageSize;
24848 ds.load({params:{start: lastStart, limit: this.pageSize}});
24851 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24857 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24858 * @param {Roo.data.Store} store The data store to unbind
24860 unbind : function(ds){
24861 ds.un("beforeload", this.beforeLoad, this);
24862 ds.un("load", this.onLoad, this);
24863 ds.un("loadexception", this.onLoadError, this);
24864 ds.un("remove", this.updateInfo, this);
24865 ds.un("add", this.updateInfo, this);
24866 this.ds = undefined;
24870 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24871 * @param {Roo.data.Store} store The data store to bind
24873 bind : function(ds){
24874 ds.on("beforeload", this.beforeLoad, this);
24875 ds.on("load", this.onLoad, this);
24876 ds.on("loadexception", this.onLoadError, this);
24877 ds.on("remove", this.updateInfo, this);
24878 ds.on("add", this.updateInfo, this);
24889 * @class Roo.bootstrap.MessageBar
24890 * @extends Roo.bootstrap.Component
24891 * Bootstrap MessageBar class
24892 * @cfg {String} html contents of the MessageBar
24893 * @cfg {String} weight (info | success | warning | danger) default info
24894 * @cfg {String} beforeClass insert the bar before the given class
24895 * @cfg {Boolean} closable (true | false) default false
24896 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24899 * Create a new Element
24900 * @param {Object} config The config object
24903 Roo.bootstrap.MessageBar = function(config){
24904 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24907 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24913 beforeClass: 'bootstrap-sticky-wrap',
24915 getAutoCreate : function(){
24919 cls: 'alert alert-dismissable alert-' + this.weight,
24924 html: this.html || ''
24930 cfg.cls += ' alert-messages-fixed';
24944 onRender : function(ct, position)
24946 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24949 var cfg = Roo.apply({}, this.getAutoCreate());
24953 cfg.cls += ' ' + this.cls;
24956 cfg.style = this.style;
24958 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24960 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24963 this.el.select('>button.close').on('click', this.hide, this);
24969 if (!this.rendered) {
24975 this.fireEvent('show', this);
24981 if (!this.rendered) {
24987 this.fireEvent('hide', this);
24990 update : function()
24992 // var e = this.el.dom.firstChild;
24994 // if(this.closable){
24995 // e = e.nextSibling;
24998 // e.data = this.html || '';
25000 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25016 * @class Roo.bootstrap.Graph
25017 * @extends Roo.bootstrap.Component
25018 * Bootstrap Graph class
25022 @cfg {String} graphtype bar | vbar | pie
25023 @cfg {number} g_x coodinator | centre x (pie)
25024 @cfg {number} g_y coodinator | centre y (pie)
25025 @cfg {number} g_r radius (pie)
25026 @cfg {number} g_height height of the chart (respected by all elements in the set)
25027 @cfg {number} g_width width of the chart (respected by all elements in the set)
25028 @cfg {Object} title The title of the chart
25031 -opts (object) options for the chart
25033 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25034 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25036 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.
25037 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25039 o stretch (boolean)
25041 -opts (object) options for the pie
25044 o startAngle (number)
25045 o endAngle (number)
25049 * Create a new Input
25050 * @param {Object} config The config object
25053 Roo.bootstrap.Graph = function(config){
25054 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25060 * The img click event for the img.
25061 * @param {Roo.EventObject} e
25067 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25078 //g_colors: this.colors,
25085 getAutoCreate : function(){
25096 onRender : function(ct,position){
25099 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25101 if (typeof(Raphael) == 'undefined') {
25102 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25106 this.raphael = Raphael(this.el.dom);
25108 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25109 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25110 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25111 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25113 r.text(160, 10, "Single Series Chart").attr(txtattr);
25114 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25115 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25116 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25118 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25119 r.barchart(330, 10, 300, 220, data1);
25120 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25121 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25124 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25125 // r.barchart(30, 30, 560, 250, xdata, {
25126 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25127 // axis : "0 0 1 1",
25128 // axisxlabels : xdata
25129 // //yvalues : cols,
25132 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25134 // this.load(null,xdata,{
25135 // axis : "0 0 1 1",
25136 // axisxlabels : xdata
25141 load : function(graphtype,xdata,opts)
25143 this.raphael.clear();
25145 graphtype = this.graphtype;
25150 var r = this.raphael,
25151 fin = function () {
25152 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25154 fout = function () {
25155 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25157 pfin = function() {
25158 this.sector.stop();
25159 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25162 this.label[0].stop();
25163 this.label[0].attr({ r: 7.5 });
25164 this.label[1].attr({ "font-weight": 800 });
25167 pfout = function() {
25168 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25171 this.label[0].animate({ r: 5 }, 500, "bounce");
25172 this.label[1].attr({ "font-weight": 400 });
25178 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25181 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25184 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25185 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25187 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25194 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25199 setTitle: function(o)
25204 initEvents: function() {
25207 this.el.on('click', this.onClick, this);
25211 onClick : function(e)
25213 Roo.log('img onclick');
25214 this.fireEvent('click', this, e);
25226 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25229 * @class Roo.bootstrap.dash.NumberBox
25230 * @extends Roo.bootstrap.Component
25231 * Bootstrap NumberBox class
25232 * @cfg {String} headline Box headline
25233 * @cfg {String} content Box content
25234 * @cfg {String} icon Box icon
25235 * @cfg {String} footer Footer text
25236 * @cfg {String} fhref Footer href
25239 * Create a new NumberBox
25240 * @param {Object} config The config object
25244 Roo.bootstrap.dash.NumberBox = function(config){
25245 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25249 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25258 getAutoCreate : function(){
25262 cls : 'small-box ',
25270 cls : 'roo-headline',
25271 html : this.headline
25275 cls : 'roo-content',
25276 html : this.content
25290 cls : 'ion ' + this.icon
25299 cls : 'small-box-footer',
25300 href : this.fhref || '#',
25304 cfg.cn.push(footer);
25311 onRender : function(ct,position){
25312 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25319 setHeadline: function (value)
25321 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25324 setFooter: function (value, href)
25326 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25329 this.el.select('a.small-box-footer',true).first().attr('href', href);
25334 setContent: function (value)
25336 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25339 initEvents: function()
25353 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25356 * @class Roo.bootstrap.dash.TabBox
25357 * @extends Roo.bootstrap.Component
25358 * Bootstrap TabBox class
25359 * @cfg {String} title Title of the TabBox
25360 * @cfg {String} icon Icon of the TabBox
25361 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25362 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25365 * Create a new TabBox
25366 * @param {Object} config The config object
25370 Roo.bootstrap.dash.TabBox = function(config){
25371 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25376 * When a pane is added
25377 * @param {Roo.bootstrap.dash.TabPane} pane
25381 * @event activatepane
25382 * When a pane is activated
25383 * @param {Roo.bootstrap.dash.TabPane} pane
25385 "activatepane" : true
25393 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25398 tabScrollable : false,
25400 getChildContainer : function()
25402 return this.el.select('.tab-content', true).first();
25405 getAutoCreate : function(){
25409 cls: 'pull-left header',
25417 cls: 'fa ' + this.icon
25423 cls: 'nav nav-tabs pull-right',
25429 if(this.tabScrollable){
25436 cls: 'nav nav-tabs pull-right',
25447 cls: 'nav-tabs-custom',
25452 cls: 'tab-content no-padding',
25460 initEvents : function()
25462 //Roo.log('add add pane handler');
25463 this.on('addpane', this.onAddPane, this);
25466 * Updates the box title
25467 * @param {String} html to set the title to.
25469 setTitle : function(value)
25471 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25473 onAddPane : function(pane)
25475 this.panes.push(pane);
25476 //Roo.log('addpane');
25478 // tabs are rendere left to right..
25479 if(!this.showtabs){
25483 var ctr = this.el.select('.nav-tabs', true).first();
25486 var existing = ctr.select('.nav-tab',true);
25487 var qty = existing.getCount();;
25490 var tab = ctr.createChild({
25492 cls : 'nav-tab' + (qty ? '' : ' active'),
25500 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25503 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25505 pane.el.addClass('active');
25510 onTabClick : function(ev,un,ob,pane)
25512 //Roo.log('tab - prev default');
25513 ev.preventDefault();
25516 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25517 pane.tab.addClass('active');
25518 //Roo.log(pane.title);
25519 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25520 // technically we should have a deactivate event.. but maybe add later.
25521 // and it should not de-activate the selected tab...
25522 this.fireEvent('activatepane', pane);
25523 pane.el.addClass('active');
25524 pane.fireEvent('activate');
25529 getActivePane : function()
25532 Roo.each(this.panes, function(p) {
25533 if(p.el.hasClass('active')){
25554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25556 * @class Roo.bootstrap.TabPane
25557 * @extends Roo.bootstrap.Component
25558 * Bootstrap TabPane class
25559 * @cfg {Boolean} active (false | true) Default false
25560 * @cfg {String} title title of panel
25564 * Create a new TabPane
25565 * @param {Object} config The config object
25568 Roo.bootstrap.dash.TabPane = function(config){
25569 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25575 * When a pane is activated
25576 * @param {Roo.bootstrap.dash.TabPane} pane
25583 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25588 // the tabBox that this is attached to.
25591 getAutoCreate : function()
25599 cfg.cls += ' active';
25604 initEvents : function()
25606 //Roo.log('trigger add pane handler');
25607 this.parent().fireEvent('addpane', this)
25611 * Updates the tab title
25612 * @param {String} html to set the title to.
25614 setTitle: function(str)
25620 this.tab.select('a', true).first().dom.innerHTML = str;
25637 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25640 * @class Roo.bootstrap.menu.Menu
25641 * @extends Roo.bootstrap.Component
25642 * Bootstrap Menu class - container for Menu
25643 * @cfg {String} html Text of the menu
25644 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25645 * @cfg {String} icon Font awesome icon
25646 * @cfg {String} pos Menu align to (top | bottom) default bottom
25650 * Create a new Menu
25651 * @param {Object} config The config object
25655 Roo.bootstrap.menu.Menu = function(config){
25656 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25660 * @event beforeshow
25661 * Fires before this menu is displayed
25662 * @param {Roo.bootstrap.menu.Menu} this
25666 * @event beforehide
25667 * Fires before this menu is hidden
25668 * @param {Roo.bootstrap.menu.Menu} this
25673 * Fires after this menu is displayed
25674 * @param {Roo.bootstrap.menu.Menu} this
25679 * Fires after this menu is hidden
25680 * @param {Roo.bootstrap.menu.Menu} this
25685 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25686 * @param {Roo.bootstrap.menu.Menu} this
25687 * @param {Roo.EventObject} e
25694 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25698 weight : 'default',
25703 getChildContainer : function() {
25704 if(this.isSubMenu){
25708 return this.el.select('ul.dropdown-menu', true).first();
25711 getAutoCreate : function()
25716 cls : 'roo-menu-text',
25724 cls : 'fa ' + this.icon
25735 cls : 'dropdown-button btn btn-' + this.weight,
25740 cls : 'dropdown-toggle btn btn-' + this.weight,
25750 cls : 'dropdown-menu'
25756 if(this.pos == 'top'){
25757 cfg.cls += ' dropup';
25760 if(this.isSubMenu){
25763 cls : 'dropdown-menu'
25770 onRender : function(ct, position)
25772 this.isSubMenu = ct.hasClass('dropdown-submenu');
25774 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25777 initEvents : function()
25779 if(this.isSubMenu){
25783 this.hidden = true;
25785 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25786 this.triggerEl.on('click', this.onTriggerPress, this);
25788 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25789 this.buttonEl.on('click', this.onClick, this);
25795 if(this.isSubMenu){
25799 return this.el.select('ul.dropdown-menu', true).first();
25802 onClick : function(e)
25804 this.fireEvent("click", this, e);
25807 onTriggerPress : function(e)
25809 if (this.isVisible()) {
25816 isVisible : function(){
25817 return !this.hidden;
25822 this.fireEvent("beforeshow", this);
25824 this.hidden = false;
25825 this.el.addClass('open');
25827 Roo.get(document).on("mouseup", this.onMouseUp, this);
25829 this.fireEvent("show", this);
25836 this.fireEvent("beforehide", this);
25838 this.hidden = true;
25839 this.el.removeClass('open');
25841 Roo.get(document).un("mouseup", this.onMouseUp);
25843 this.fireEvent("hide", this);
25846 onMouseUp : function()
25860 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25863 * @class Roo.bootstrap.menu.Item
25864 * @extends Roo.bootstrap.Component
25865 * Bootstrap MenuItem class
25866 * @cfg {Boolean} submenu (true | false) default false
25867 * @cfg {String} html text of the item
25868 * @cfg {String} href the link
25869 * @cfg {Boolean} disable (true | false) default false
25870 * @cfg {Boolean} preventDefault (true | false) default true
25871 * @cfg {String} icon Font awesome icon
25872 * @cfg {String} pos Submenu align to (left | right) default right
25876 * Create a new Item
25877 * @param {Object} config The config object
25881 Roo.bootstrap.menu.Item = function(config){
25882 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25886 * Fires when the mouse is hovering over this menu
25887 * @param {Roo.bootstrap.menu.Item} this
25888 * @param {Roo.EventObject} e
25893 * Fires when the mouse exits this menu
25894 * @param {Roo.bootstrap.menu.Item} this
25895 * @param {Roo.EventObject} e
25901 * The raw click event for the entire grid.
25902 * @param {Roo.EventObject} e
25908 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25913 preventDefault: true,
25918 getAutoCreate : function()
25923 cls : 'roo-menu-item-text',
25931 cls : 'fa ' + this.icon
25940 href : this.href || '#',
25947 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25951 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25953 if(this.pos == 'left'){
25954 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25961 initEvents : function()
25963 this.el.on('mouseover', this.onMouseOver, this);
25964 this.el.on('mouseout', this.onMouseOut, this);
25966 this.el.select('a', true).first().on('click', this.onClick, this);
25970 onClick : function(e)
25972 if(this.preventDefault){
25973 e.preventDefault();
25976 this.fireEvent("click", this, e);
25979 onMouseOver : function(e)
25981 if(this.submenu && this.pos == 'left'){
25982 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25985 this.fireEvent("mouseover", this, e);
25988 onMouseOut : function(e)
25990 this.fireEvent("mouseout", this, e);
26002 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26005 * @class Roo.bootstrap.menu.Separator
26006 * @extends Roo.bootstrap.Component
26007 * Bootstrap Separator class
26010 * Create a new Separator
26011 * @param {Object} config The config object
26015 Roo.bootstrap.menu.Separator = function(config){
26016 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26019 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26021 getAutoCreate : function(){
26042 * @class Roo.bootstrap.Tooltip
26043 * Bootstrap Tooltip class
26044 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26045 * to determine which dom element triggers the tooltip.
26047 * It needs to add support for additional attributes like tooltip-position
26050 * Create a new Toolti
26051 * @param {Object} config The config object
26054 Roo.bootstrap.Tooltip = function(config){
26055 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26057 this.alignment = Roo.bootstrap.Tooltip.alignment;
26059 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26060 this.alignment = config.alignment;
26065 Roo.apply(Roo.bootstrap.Tooltip, {
26067 * @function init initialize tooltip monitoring.
26071 currentTip : false,
26072 currentRegion : false,
26078 Roo.get(document).on('mouseover', this.enter ,this);
26079 Roo.get(document).on('mouseout', this.leave, this);
26082 this.currentTip = new Roo.bootstrap.Tooltip();
26085 enter : function(ev)
26087 var dom = ev.getTarget();
26089 //Roo.log(['enter',dom]);
26090 var el = Roo.fly(dom);
26091 if (this.currentEl) {
26093 //Roo.log(this.currentEl);
26094 //Roo.log(this.currentEl.contains(dom));
26095 if (this.currentEl == el) {
26098 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26104 if (this.currentTip.el) {
26105 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26109 if(!el || el.dom == document){
26115 // you can not look for children, as if el is the body.. then everythign is the child..
26116 if (!el.attr('tooltip')) { //
26117 if (!el.select("[tooltip]").elements.length) {
26120 // is the mouse over this child...?
26121 bindEl = el.select("[tooltip]").first();
26122 var xy = ev.getXY();
26123 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26124 //Roo.log("not in region.");
26127 //Roo.log("child element over..");
26130 this.currentEl = bindEl;
26131 this.currentTip.bind(bindEl);
26132 this.currentRegion = Roo.lib.Region.getRegion(dom);
26133 this.currentTip.enter();
26136 leave : function(ev)
26138 var dom = ev.getTarget();
26139 //Roo.log(['leave',dom]);
26140 if (!this.currentEl) {
26145 if (dom != this.currentEl.dom) {
26148 var xy = ev.getXY();
26149 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26152 // only activate leave if mouse cursor is outside... bounding box..
26157 if (this.currentTip) {
26158 this.currentTip.leave();
26160 //Roo.log('clear currentEl');
26161 this.currentEl = false;
26166 'left' : ['r-l', [-2,0], 'right'],
26167 'right' : ['l-r', [2,0], 'left'],
26168 'bottom' : ['t-b', [0,2], 'top'],
26169 'top' : [ 'b-t', [0,-2], 'bottom']
26175 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26180 delay : null, // can be { show : 300 , hide: 500}
26184 hoverState : null, //???
26186 placement : 'bottom',
26190 getAutoCreate : function(){
26197 cls : 'tooltip-arrow'
26200 cls : 'tooltip-inner'
26207 bind : function(el)
26213 enter : function () {
26215 if (this.timeout != null) {
26216 clearTimeout(this.timeout);
26219 this.hoverState = 'in';
26220 //Roo.log("enter - show");
26221 if (!this.delay || !this.delay.show) {
26226 this.timeout = setTimeout(function () {
26227 if (_t.hoverState == 'in') {
26230 }, this.delay.show);
26234 clearTimeout(this.timeout);
26236 this.hoverState = 'out';
26237 if (!this.delay || !this.delay.hide) {
26243 this.timeout = setTimeout(function () {
26244 //Roo.log("leave - timeout");
26246 if (_t.hoverState == 'out') {
26248 Roo.bootstrap.Tooltip.currentEl = false;
26253 show : function (msg)
26256 this.render(document.body);
26259 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26261 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26263 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26265 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26267 var placement = typeof this.placement == 'function' ?
26268 this.placement.call(this, this.el, on_el) :
26271 var autoToken = /\s?auto?\s?/i;
26272 var autoPlace = autoToken.test(placement);
26274 placement = placement.replace(autoToken, '') || 'top';
26278 //this.el.setXY([0,0]);
26280 //this.el.dom.style.display='block';
26282 //this.el.appendTo(on_el);
26284 var p = this.getPosition();
26285 var box = this.el.getBox();
26291 var align = this.alignment[placement];
26293 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26295 if(placement == 'top' || placement == 'bottom'){
26297 placement = 'right';
26300 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26301 placement = 'left';
26304 var scroll = Roo.select('body', true).first().getScroll();
26306 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26310 align = this.alignment[placement];
26313 this.el.alignTo(this.bindEl, align[0],align[1]);
26314 //var arrow = this.el.select('.arrow',true).first();
26315 //arrow.set(align[2],
26317 this.el.addClass(placement);
26319 this.el.addClass('in fade');
26321 this.hoverState = null;
26323 if (this.el.hasClass('fade')) {
26334 //this.el.setXY([0,0]);
26335 this.el.removeClass('in');
26351 * @class Roo.bootstrap.LocationPicker
26352 * @extends Roo.bootstrap.Component
26353 * Bootstrap LocationPicker class
26354 * @cfg {Number} latitude Position when init default 0
26355 * @cfg {Number} longitude Position when init default 0
26356 * @cfg {Number} zoom default 15
26357 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26358 * @cfg {Boolean} mapTypeControl default false
26359 * @cfg {Boolean} disableDoubleClickZoom default false
26360 * @cfg {Boolean} scrollwheel default true
26361 * @cfg {Boolean} streetViewControl default false
26362 * @cfg {Number} radius default 0
26363 * @cfg {String} locationName
26364 * @cfg {Boolean} draggable default true
26365 * @cfg {Boolean} enableAutocomplete default false
26366 * @cfg {Boolean} enableReverseGeocode default true
26367 * @cfg {String} markerTitle
26370 * Create a new LocationPicker
26371 * @param {Object} config The config object
26375 Roo.bootstrap.LocationPicker = function(config){
26377 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26382 * Fires when the picker initialized.
26383 * @param {Roo.bootstrap.LocationPicker} this
26384 * @param {Google Location} location
26388 * @event positionchanged
26389 * Fires when the picker position changed.
26390 * @param {Roo.bootstrap.LocationPicker} this
26391 * @param {Google Location} location
26393 positionchanged : true,
26396 * Fires when the map resize.
26397 * @param {Roo.bootstrap.LocationPicker} this
26402 * Fires when the map show.
26403 * @param {Roo.bootstrap.LocationPicker} this
26408 * Fires when the map hide.
26409 * @param {Roo.bootstrap.LocationPicker} this
26414 * Fires when click the map.
26415 * @param {Roo.bootstrap.LocationPicker} this
26416 * @param {Map event} e
26420 * @event mapRightClick
26421 * Fires when right click the map.
26422 * @param {Roo.bootstrap.LocationPicker} this
26423 * @param {Map event} e
26425 mapRightClick : true,
26427 * @event markerClick
26428 * Fires when click the marker.
26429 * @param {Roo.bootstrap.LocationPicker} this
26430 * @param {Map event} e
26432 markerClick : true,
26434 * @event markerRightClick
26435 * Fires when right click the marker.
26436 * @param {Roo.bootstrap.LocationPicker} this
26437 * @param {Map event} e
26439 markerRightClick : true,
26441 * @event OverlayViewDraw
26442 * Fires when OverlayView Draw
26443 * @param {Roo.bootstrap.LocationPicker} this
26445 OverlayViewDraw : true,
26447 * @event OverlayViewOnAdd
26448 * Fires when OverlayView Draw
26449 * @param {Roo.bootstrap.LocationPicker} this
26451 OverlayViewOnAdd : true,
26453 * @event OverlayViewOnRemove
26454 * Fires when OverlayView Draw
26455 * @param {Roo.bootstrap.LocationPicker} this
26457 OverlayViewOnRemove : true,
26459 * @event OverlayViewShow
26460 * Fires when OverlayView Draw
26461 * @param {Roo.bootstrap.LocationPicker} this
26462 * @param {Pixel} cpx
26464 OverlayViewShow : true,
26466 * @event OverlayViewHide
26467 * Fires when OverlayView Draw
26468 * @param {Roo.bootstrap.LocationPicker} this
26470 OverlayViewHide : true,
26472 * @event loadexception
26473 * Fires when load google lib failed.
26474 * @param {Roo.bootstrap.LocationPicker} this
26476 loadexception : true
26481 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26483 gMapContext: false,
26489 mapTypeControl: false,
26490 disableDoubleClickZoom: false,
26492 streetViewControl: false,
26496 enableAutocomplete: false,
26497 enableReverseGeocode: true,
26500 getAutoCreate: function()
26505 cls: 'roo-location-picker'
26511 initEvents: function(ct, position)
26513 if(!this.el.getWidth() || this.isApplied()){
26517 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26522 initial: function()
26524 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26525 this.fireEvent('loadexception', this);
26529 if(!this.mapTypeId){
26530 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26533 this.gMapContext = this.GMapContext();
26535 this.initOverlayView();
26537 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26541 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26542 _this.setPosition(_this.gMapContext.marker.position);
26545 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26546 _this.fireEvent('mapClick', this, event);
26550 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26551 _this.fireEvent('mapRightClick', this, event);
26555 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26556 _this.fireEvent('markerClick', this, event);
26560 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26561 _this.fireEvent('markerRightClick', this, event);
26565 this.setPosition(this.gMapContext.location);
26567 this.fireEvent('initial', this, this.gMapContext.location);
26570 initOverlayView: function()
26574 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26578 _this.fireEvent('OverlayViewDraw', _this);
26583 _this.fireEvent('OverlayViewOnAdd', _this);
26586 onRemove: function()
26588 _this.fireEvent('OverlayViewOnRemove', _this);
26591 show: function(cpx)
26593 _this.fireEvent('OverlayViewShow', _this, cpx);
26598 _this.fireEvent('OverlayViewHide', _this);
26604 fromLatLngToContainerPixel: function(event)
26606 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26609 isApplied: function()
26611 return this.getGmapContext() == false ? false : true;
26614 getGmapContext: function()
26616 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26619 GMapContext: function()
26621 var position = new google.maps.LatLng(this.latitude, this.longitude);
26623 var _map = new google.maps.Map(this.el.dom, {
26626 mapTypeId: this.mapTypeId,
26627 mapTypeControl: this.mapTypeControl,
26628 disableDoubleClickZoom: this.disableDoubleClickZoom,
26629 scrollwheel: this.scrollwheel,
26630 streetViewControl: this.streetViewControl,
26631 locationName: this.locationName,
26632 draggable: this.draggable,
26633 enableAutocomplete: this.enableAutocomplete,
26634 enableReverseGeocode: this.enableReverseGeocode
26637 var _marker = new google.maps.Marker({
26638 position: position,
26640 title: this.markerTitle,
26641 draggable: this.draggable
26648 location: position,
26649 radius: this.radius,
26650 locationName: this.locationName,
26651 addressComponents: {
26652 formatted_address: null,
26653 addressLine1: null,
26654 addressLine2: null,
26656 streetNumber: null,
26660 stateOrProvince: null
26663 domContainer: this.el.dom,
26664 geodecoder: new google.maps.Geocoder()
26668 drawCircle: function(center, radius, options)
26670 if (this.gMapContext.circle != null) {
26671 this.gMapContext.circle.setMap(null);
26675 options = Roo.apply({}, options, {
26676 strokeColor: "#0000FF",
26677 strokeOpacity: .35,
26679 fillColor: "#0000FF",
26683 options.map = this.gMapContext.map;
26684 options.radius = radius;
26685 options.center = center;
26686 this.gMapContext.circle = new google.maps.Circle(options);
26687 return this.gMapContext.circle;
26693 setPosition: function(location)
26695 this.gMapContext.location = location;
26696 this.gMapContext.marker.setPosition(location);
26697 this.gMapContext.map.panTo(location);
26698 this.drawCircle(location, this.gMapContext.radius, {});
26702 if (this.gMapContext.settings.enableReverseGeocode) {
26703 this.gMapContext.geodecoder.geocode({
26704 latLng: this.gMapContext.location
26705 }, function(results, status) {
26707 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26708 _this.gMapContext.locationName = results[0].formatted_address;
26709 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26711 _this.fireEvent('positionchanged', this, location);
26718 this.fireEvent('positionchanged', this, location);
26723 google.maps.event.trigger(this.gMapContext.map, "resize");
26725 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26727 this.fireEvent('resize', this);
26730 setPositionByLatLng: function(latitude, longitude)
26732 this.setPosition(new google.maps.LatLng(latitude, longitude));
26735 getCurrentPosition: function()
26738 latitude: this.gMapContext.location.lat(),
26739 longitude: this.gMapContext.location.lng()
26743 getAddressName: function()
26745 return this.gMapContext.locationName;
26748 getAddressComponents: function()
26750 return this.gMapContext.addressComponents;
26753 address_component_from_google_geocode: function(address_components)
26757 for (var i = 0; i < address_components.length; i++) {
26758 var component = address_components[i];
26759 if (component.types.indexOf("postal_code") >= 0) {
26760 result.postalCode = component.short_name;
26761 } else if (component.types.indexOf("street_number") >= 0) {
26762 result.streetNumber = component.short_name;
26763 } else if (component.types.indexOf("route") >= 0) {
26764 result.streetName = component.short_name;
26765 } else if (component.types.indexOf("neighborhood") >= 0) {
26766 result.city = component.short_name;
26767 } else if (component.types.indexOf("locality") >= 0) {
26768 result.city = component.short_name;
26769 } else if (component.types.indexOf("sublocality") >= 0) {
26770 result.district = component.short_name;
26771 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26772 result.stateOrProvince = component.short_name;
26773 } else if (component.types.indexOf("country") >= 0) {
26774 result.country = component.short_name;
26778 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26779 result.addressLine2 = "";
26783 setZoomLevel: function(zoom)
26785 this.gMapContext.map.setZoom(zoom);
26798 this.fireEvent('show', this);
26809 this.fireEvent('hide', this);
26814 Roo.apply(Roo.bootstrap.LocationPicker, {
26816 OverlayView : function(map, options)
26818 options = options || {};
26832 * @class Roo.bootstrap.Alert
26833 * @extends Roo.bootstrap.Component
26834 * Bootstrap Alert class
26835 * @cfg {String} title The title of alert
26836 * @cfg {String} html The content of alert
26837 * @cfg {String} weight ( success | info | warning | danger )
26838 * @cfg {String} faicon font-awesomeicon
26841 * Create a new alert
26842 * @param {Object} config The config object
26846 Roo.bootstrap.Alert = function(config){
26847 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26851 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26858 getAutoCreate : function()
26867 cls : 'roo-alert-icon'
26872 cls : 'roo-alert-title',
26877 cls : 'roo-alert-text',
26884 cfg.cn[0].cls += ' fa ' + this.faicon;
26888 cfg.cls += ' alert-' + this.weight;
26894 initEvents: function()
26896 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26899 setTitle : function(str)
26901 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26904 setText : function(str)
26906 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26909 setWeight : function(weight)
26912 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26915 this.weight = weight;
26917 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26920 setIcon : function(icon)
26923 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26926 this.faicon = icon;
26928 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26949 * @class Roo.bootstrap.UploadCropbox
26950 * @extends Roo.bootstrap.Component
26951 * Bootstrap UploadCropbox class
26952 * @cfg {String} emptyText show when image has been loaded
26953 * @cfg {String} rotateNotify show when image too small to rotate
26954 * @cfg {Number} errorTimeout default 3000
26955 * @cfg {Number} minWidth default 300
26956 * @cfg {Number} minHeight default 300
26957 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26958 * @cfg {Boolean} isDocument (true|false) default false
26959 * @cfg {String} url action url
26960 * @cfg {String} paramName default 'imageUpload'
26961 * @cfg {String} method default POST
26962 * @cfg {Boolean} loadMask (true|false) default true
26963 * @cfg {Boolean} loadingText default 'Loading...'
26966 * Create a new UploadCropbox
26967 * @param {Object} config The config object
26970 Roo.bootstrap.UploadCropbox = function(config){
26971 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26975 * @event beforeselectfile
26976 * Fire before select file
26977 * @param {Roo.bootstrap.UploadCropbox} this
26979 "beforeselectfile" : true,
26982 * Fire after initEvent
26983 * @param {Roo.bootstrap.UploadCropbox} this
26988 * Fire after initEvent
26989 * @param {Roo.bootstrap.UploadCropbox} this
26990 * @param {String} data
26995 * Fire when preparing the file data
26996 * @param {Roo.bootstrap.UploadCropbox} this
26997 * @param {Object} file
27002 * Fire when get exception
27003 * @param {Roo.bootstrap.UploadCropbox} this
27004 * @param {XMLHttpRequest} xhr
27006 "exception" : true,
27008 * @event beforeloadcanvas
27009 * Fire before load the canvas
27010 * @param {Roo.bootstrap.UploadCropbox} this
27011 * @param {String} src
27013 "beforeloadcanvas" : true,
27016 * Fire when trash image
27017 * @param {Roo.bootstrap.UploadCropbox} this
27022 * Fire when download the image
27023 * @param {Roo.bootstrap.UploadCropbox} this
27027 * @event footerbuttonclick
27028 * Fire when footerbuttonclick
27029 * @param {Roo.bootstrap.UploadCropbox} this
27030 * @param {String} type
27032 "footerbuttonclick" : true,
27036 * @param {Roo.bootstrap.UploadCropbox} this
27041 * Fire when rotate the image
27042 * @param {Roo.bootstrap.UploadCropbox} this
27043 * @param {String} pos
27048 * Fire when inspect the file
27049 * @param {Roo.bootstrap.UploadCropbox} this
27050 * @param {Object} file
27055 * Fire when xhr upload the file
27056 * @param {Roo.bootstrap.UploadCropbox} this
27057 * @param {Object} data
27062 * Fire when arrange the file data
27063 * @param {Roo.bootstrap.UploadCropbox} this
27064 * @param {Object} formData
27069 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27072 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27074 emptyText : 'Click to upload image',
27075 rotateNotify : 'Image is too small to rotate',
27076 errorTimeout : 3000,
27090 cropType : 'image/jpeg',
27092 canvasLoaded : false,
27093 isDocument : false,
27095 paramName : 'imageUpload',
27097 loadingText : 'Loading...',
27100 getAutoCreate : function()
27104 cls : 'roo-upload-cropbox',
27108 cls : 'roo-upload-cropbox-selector',
27113 cls : 'roo-upload-cropbox-body',
27114 style : 'cursor:pointer',
27118 cls : 'roo-upload-cropbox-preview'
27122 cls : 'roo-upload-cropbox-thumb'
27126 cls : 'roo-upload-cropbox-empty-notify',
27127 html : this.emptyText
27131 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27132 html : this.rotateNotify
27138 cls : 'roo-upload-cropbox-footer',
27141 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27151 onRender : function(ct, position)
27153 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27155 if (this.buttons.length) {
27157 Roo.each(this.buttons, function(bb) {
27159 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27161 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27167 this.maskEl = this.el;
27171 initEvents : function()
27173 this.urlAPI = (window.createObjectURL && window) ||
27174 (window.URL && URL.revokeObjectURL && URL) ||
27175 (window.webkitURL && webkitURL);
27177 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27178 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27180 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27181 this.selectorEl.hide();
27183 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27184 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27186 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27187 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27188 this.thumbEl.hide();
27190 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27191 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27193 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27194 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27195 this.errorEl.hide();
27197 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27198 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27199 this.footerEl.hide();
27201 this.setThumbBoxSize();
27207 this.fireEvent('initial', this);
27214 window.addEventListener("resize", function() { _this.resize(); } );
27216 this.bodyEl.on('click', this.beforeSelectFile, this);
27219 this.bodyEl.on('touchstart', this.onTouchStart, this);
27220 this.bodyEl.on('touchmove', this.onTouchMove, this);
27221 this.bodyEl.on('touchend', this.onTouchEnd, this);
27225 this.bodyEl.on('mousedown', this.onMouseDown, this);
27226 this.bodyEl.on('mousemove', this.onMouseMove, this);
27227 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27228 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27229 Roo.get(document).on('mouseup', this.onMouseUp, this);
27232 this.selectorEl.on('change', this.onFileSelected, this);
27238 this.baseScale = 1;
27240 this.baseRotate = 1;
27241 this.dragable = false;
27242 this.pinching = false;
27245 this.cropData = false;
27246 this.notifyEl.dom.innerHTML = this.emptyText;
27248 this.selectorEl.dom.value = '';
27252 resize : function()
27254 if(this.fireEvent('resize', this) != false){
27255 this.setThumbBoxPosition();
27256 this.setCanvasPosition();
27260 onFooterButtonClick : function(e, el, o, type)
27263 case 'rotate-left' :
27264 this.onRotateLeft(e);
27266 case 'rotate-right' :
27267 this.onRotateRight(e);
27270 this.beforeSelectFile(e);
27285 this.fireEvent('footerbuttonclick', this, type);
27288 beforeSelectFile : function(e)
27290 e.preventDefault();
27292 if(this.fireEvent('beforeselectfile', this) != false){
27293 this.selectorEl.dom.click();
27297 onFileSelected : function(e)
27299 e.preventDefault();
27301 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27305 var file = this.selectorEl.dom.files[0];
27307 if(this.fireEvent('inspect', this, file) != false){
27308 this.prepare(file);
27313 trash : function(e)
27315 this.fireEvent('trash', this);
27318 download : function(e)
27320 this.fireEvent('download', this);
27323 loadCanvas : function(src)
27325 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27329 this.imageEl = document.createElement('img');
27333 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27335 this.imageEl.src = src;
27339 onLoadCanvas : function()
27341 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27342 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27344 this.bodyEl.un('click', this.beforeSelectFile, this);
27346 this.notifyEl.hide();
27347 this.thumbEl.show();
27348 this.footerEl.show();
27350 this.baseRotateLevel();
27352 if(this.isDocument){
27353 this.setThumbBoxSize();
27356 this.setThumbBoxPosition();
27358 this.baseScaleLevel();
27364 this.canvasLoaded = true;
27367 this.maskEl.unmask();
27372 setCanvasPosition : function()
27374 if(!this.canvasEl){
27378 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27379 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27381 this.previewEl.setLeft(pw);
27382 this.previewEl.setTop(ph);
27386 onMouseDown : function(e)
27390 this.dragable = true;
27391 this.pinching = false;
27393 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27394 this.dragable = false;
27398 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27399 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27403 onMouseMove : function(e)
27407 if(!this.canvasLoaded){
27411 if (!this.dragable){
27415 var minX = Math.ceil(this.thumbEl.getLeft(true));
27416 var minY = Math.ceil(this.thumbEl.getTop(true));
27418 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27419 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27421 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27422 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27424 x = x - this.mouseX;
27425 y = y - this.mouseY;
27427 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27428 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27430 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27431 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27433 this.previewEl.setLeft(bgX);
27434 this.previewEl.setTop(bgY);
27436 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27437 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27440 onMouseUp : function(e)
27444 this.dragable = false;
27447 onMouseWheel : function(e)
27451 this.startScale = this.scale;
27453 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27455 if(!this.zoomable()){
27456 this.scale = this.startScale;
27465 zoomable : function()
27467 var minScale = this.thumbEl.getWidth() / this.minWidth;
27469 if(this.minWidth < this.minHeight){
27470 minScale = this.thumbEl.getHeight() / this.minHeight;
27473 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27474 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27478 (this.rotate == 0 || this.rotate == 180) &&
27480 width > this.imageEl.OriginWidth ||
27481 height > this.imageEl.OriginHeight ||
27482 (width < this.minWidth && height < this.minHeight)
27490 (this.rotate == 90 || this.rotate == 270) &&
27492 width > this.imageEl.OriginWidth ||
27493 height > this.imageEl.OriginHeight ||
27494 (width < this.minHeight && height < this.minWidth)
27501 !this.isDocument &&
27502 (this.rotate == 0 || this.rotate == 180) &&
27504 width < this.minWidth ||
27505 width > this.imageEl.OriginWidth ||
27506 height < this.minHeight ||
27507 height > this.imageEl.OriginHeight
27514 !this.isDocument &&
27515 (this.rotate == 90 || this.rotate == 270) &&
27517 width < this.minHeight ||
27518 width > this.imageEl.OriginWidth ||
27519 height < this.minWidth ||
27520 height > this.imageEl.OriginHeight
27530 onRotateLeft : function(e)
27532 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27534 var minScale = this.thumbEl.getWidth() / this.minWidth;
27536 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27537 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27539 this.startScale = this.scale;
27541 while (this.getScaleLevel() < minScale){
27543 this.scale = this.scale + 1;
27545 if(!this.zoomable()){
27550 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27551 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27556 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27563 this.scale = this.startScale;
27565 this.onRotateFail();
27570 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27572 if(this.isDocument){
27573 this.setThumbBoxSize();
27574 this.setThumbBoxPosition();
27575 this.setCanvasPosition();
27580 this.fireEvent('rotate', this, 'left');
27584 onRotateRight : function(e)
27586 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27588 var minScale = this.thumbEl.getWidth() / this.minWidth;
27590 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27591 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27593 this.startScale = this.scale;
27595 while (this.getScaleLevel() < minScale){
27597 this.scale = this.scale + 1;
27599 if(!this.zoomable()){
27604 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27605 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27610 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27617 this.scale = this.startScale;
27619 this.onRotateFail();
27624 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27626 if(this.isDocument){
27627 this.setThumbBoxSize();
27628 this.setThumbBoxPosition();
27629 this.setCanvasPosition();
27634 this.fireEvent('rotate', this, 'right');
27637 onRotateFail : function()
27639 this.errorEl.show(true);
27643 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27648 this.previewEl.dom.innerHTML = '';
27650 var canvasEl = document.createElement("canvas");
27652 var contextEl = canvasEl.getContext("2d");
27654 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27655 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27656 var center = this.imageEl.OriginWidth / 2;
27658 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27659 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27660 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27661 center = this.imageEl.OriginHeight / 2;
27664 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27666 contextEl.translate(center, center);
27667 contextEl.rotate(this.rotate * Math.PI / 180);
27669 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27671 this.canvasEl = document.createElement("canvas");
27673 this.contextEl = this.canvasEl.getContext("2d");
27675 switch (this.rotate) {
27678 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27679 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27681 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27686 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27687 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27689 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27690 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);
27694 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27699 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27700 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27702 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27703 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);
27707 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);
27712 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27713 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27715 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27716 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27720 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);
27727 this.previewEl.appendChild(this.canvasEl);
27729 this.setCanvasPosition();
27734 if(!this.canvasLoaded){
27738 var imageCanvas = document.createElement("canvas");
27740 var imageContext = imageCanvas.getContext("2d");
27742 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27743 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27745 var center = imageCanvas.width / 2;
27747 imageContext.translate(center, center);
27749 imageContext.rotate(this.rotate * Math.PI / 180);
27751 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27753 var canvas = document.createElement("canvas");
27755 var context = canvas.getContext("2d");
27757 canvas.width = this.minWidth;
27758 canvas.height = this.minHeight;
27760 switch (this.rotate) {
27763 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27764 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27766 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27767 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27769 var targetWidth = this.minWidth - 2 * x;
27770 var targetHeight = this.minHeight - 2 * y;
27774 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27775 scale = targetWidth / width;
27778 if(x > 0 && y == 0){
27779 scale = targetHeight / height;
27782 if(x > 0 && y > 0){
27783 scale = targetWidth / width;
27785 if(width < height){
27786 scale = targetHeight / height;
27790 context.scale(scale, scale);
27792 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27793 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27795 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27796 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27798 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27803 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27804 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27806 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27807 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27809 var targetWidth = this.minWidth - 2 * x;
27810 var targetHeight = this.minHeight - 2 * y;
27814 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27815 scale = targetWidth / width;
27818 if(x > 0 && y == 0){
27819 scale = targetHeight / height;
27822 if(x > 0 && y > 0){
27823 scale = targetWidth / width;
27825 if(width < height){
27826 scale = targetHeight / height;
27830 context.scale(scale, scale);
27832 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27833 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27835 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27836 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27838 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27840 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27845 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27846 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27848 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27849 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27851 var targetWidth = this.minWidth - 2 * x;
27852 var targetHeight = this.minHeight - 2 * y;
27856 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27857 scale = targetWidth / width;
27860 if(x > 0 && y == 0){
27861 scale = targetHeight / height;
27864 if(x > 0 && y > 0){
27865 scale = targetWidth / width;
27867 if(width < height){
27868 scale = targetHeight / height;
27872 context.scale(scale, scale);
27874 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27875 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27877 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27878 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27880 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27881 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27883 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27888 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27889 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27891 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27892 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27894 var targetWidth = this.minWidth - 2 * x;
27895 var targetHeight = this.minHeight - 2 * y;
27899 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27900 scale = targetWidth / width;
27903 if(x > 0 && y == 0){
27904 scale = targetHeight / height;
27907 if(x > 0 && y > 0){
27908 scale = targetWidth / width;
27910 if(width < height){
27911 scale = targetHeight / height;
27915 context.scale(scale, scale);
27917 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27918 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27920 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27921 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27923 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27925 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27932 this.cropData = canvas.toDataURL(this.cropType);
27934 if(this.fireEvent('crop', this, this.cropData) !== false){
27935 this.process(this.file, this.cropData);
27942 setThumbBoxSize : function()
27946 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27947 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27948 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27950 this.minWidth = width;
27951 this.minHeight = height;
27953 if(this.rotate == 90 || this.rotate == 270){
27954 this.minWidth = height;
27955 this.minHeight = width;
27960 width = Math.ceil(this.minWidth * height / this.minHeight);
27962 if(this.minWidth > this.minHeight){
27964 height = Math.ceil(this.minHeight * width / this.minWidth);
27967 this.thumbEl.setStyle({
27968 width : width + 'px',
27969 height : height + 'px'
27976 setThumbBoxPosition : function()
27978 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27979 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27981 this.thumbEl.setLeft(x);
27982 this.thumbEl.setTop(y);
27986 baseRotateLevel : function()
27988 this.baseRotate = 1;
27991 typeof(this.exif) != 'undefined' &&
27992 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27993 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27995 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27998 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28002 baseScaleLevel : function()
28006 if(this.isDocument){
28008 if(this.baseRotate == 6 || this.baseRotate == 8){
28010 height = this.thumbEl.getHeight();
28011 this.baseScale = height / this.imageEl.OriginWidth;
28013 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28014 width = this.thumbEl.getWidth();
28015 this.baseScale = width / this.imageEl.OriginHeight;
28021 height = this.thumbEl.getHeight();
28022 this.baseScale = height / this.imageEl.OriginHeight;
28024 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28025 width = this.thumbEl.getWidth();
28026 this.baseScale = width / this.imageEl.OriginWidth;
28032 if(this.baseRotate == 6 || this.baseRotate == 8){
28034 width = this.thumbEl.getHeight();
28035 this.baseScale = width / this.imageEl.OriginHeight;
28037 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28038 height = this.thumbEl.getWidth();
28039 this.baseScale = height / this.imageEl.OriginHeight;
28042 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28043 height = this.thumbEl.getWidth();
28044 this.baseScale = height / this.imageEl.OriginHeight;
28046 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28047 width = this.thumbEl.getHeight();
28048 this.baseScale = width / this.imageEl.OriginWidth;
28055 width = this.thumbEl.getWidth();
28056 this.baseScale = width / this.imageEl.OriginWidth;
28058 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28059 height = this.thumbEl.getHeight();
28060 this.baseScale = height / this.imageEl.OriginHeight;
28063 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28065 height = this.thumbEl.getHeight();
28066 this.baseScale = height / this.imageEl.OriginHeight;
28068 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28069 width = this.thumbEl.getWidth();
28070 this.baseScale = width / this.imageEl.OriginWidth;
28078 getScaleLevel : function()
28080 return this.baseScale * Math.pow(1.1, this.scale);
28083 onTouchStart : function(e)
28085 if(!this.canvasLoaded){
28086 this.beforeSelectFile(e);
28090 var touches = e.browserEvent.touches;
28096 if(touches.length == 1){
28097 this.onMouseDown(e);
28101 if(touches.length != 2){
28107 for(var i = 0, finger; finger = touches[i]; i++){
28108 coords.push(finger.pageX, finger.pageY);
28111 var x = Math.pow(coords[0] - coords[2], 2);
28112 var y = Math.pow(coords[1] - coords[3], 2);
28114 this.startDistance = Math.sqrt(x + y);
28116 this.startScale = this.scale;
28118 this.pinching = true;
28119 this.dragable = false;
28123 onTouchMove : function(e)
28125 if(!this.pinching && !this.dragable){
28129 var touches = e.browserEvent.touches;
28136 this.onMouseMove(e);
28142 for(var i = 0, finger; finger = touches[i]; i++){
28143 coords.push(finger.pageX, finger.pageY);
28146 var x = Math.pow(coords[0] - coords[2], 2);
28147 var y = Math.pow(coords[1] - coords[3], 2);
28149 this.endDistance = Math.sqrt(x + y);
28151 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28153 if(!this.zoomable()){
28154 this.scale = this.startScale;
28162 onTouchEnd : function(e)
28164 this.pinching = false;
28165 this.dragable = false;
28169 process : function(file, crop)
28172 this.maskEl.mask(this.loadingText);
28175 this.xhr = new XMLHttpRequest();
28177 file.xhr = this.xhr;
28179 this.xhr.open(this.method, this.url, true);
28182 "Accept": "application/json",
28183 "Cache-Control": "no-cache",
28184 "X-Requested-With": "XMLHttpRequest"
28187 for (var headerName in headers) {
28188 var headerValue = headers[headerName];
28190 this.xhr.setRequestHeader(headerName, headerValue);
28196 this.xhr.onload = function()
28198 _this.xhrOnLoad(_this.xhr);
28201 this.xhr.onerror = function()
28203 _this.xhrOnError(_this.xhr);
28206 var formData = new FormData();
28208 formData.append('returnHTML', 'NO');
28211 formData.append('crop', crop);
28214 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28215 formData.append(this.paramName, file, file.name);
28218 if(typeof(file.filename) != 'undefined'){
28219 formData.append('filename', file.filename);
28222 if(typeof(file.mimetype) != 'undefined'){
28223 formData.append('mimetype', file.mimetype);
28226 if(this.fireEvent('arrange', this, formData) != false){
28227 this.xhr.send(formData);
28231 xhrOnLoad : function(xhr)
28234 this.maskEl.unmask();
28237 if (xhr.readyState !== 4) {
28238 this.fireEvent('exception', this, xhr);
28242 var response = Roo.decode(xhr.responseText);
28244 if(!response.success){
28245 this.fireEvent('exception', this, xhr);
28249 var response = Roo.decode(xhr.responseText);
28251 this.fireEvent('upload', this, response);
28255 xhrOnError : function()
28258 this.maskEl.unmask();
28261 Roo.log('xhr on error');
28263 var response = Roo.decode(xhr.responseText);
28269 prepare : function(file)
28272 this.maskEl.mask(this.loadingText);
28278 if(typeof(file) === 'string'){
28279 this.loadCanvas(file);
28283 if(!file || !this.urlAPI){
28288 this.cropType = file.type;
28292 if(this.fireEvent('prepare', this, this.file) != false){
28294 var reader = new FileReader();
28296 reader.onload = function (e) {
28297 if (e.target.error) {
28298 Roo.log(e.target.error);
28302 var buffer = e.target.result,
28303 dataView = new DataView(buffer),
28305 maxOffset = dataView.byteLength - 4,
28309 if (dataView.getUint16(0) === 0xffd8) {
28310 while (offset < maxOffset) {
28311 markerBytes = dataView.getUint16(offset);
28313 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28314 markerLength = dataView.getUint16(offset + 2) + 2;
28315 if (offset + markerLength > dataView.byteLength) {
28316 Roo.log('Invalid meta data: Invalid segment size.');
28320 if(markerBytes == 0xffe1){
28321 _this.parseExifData(
28328 offset += markerLength;
28338 var url = _this.urlAPI.createObjectURL(_this.file);
28340 _this.loadCanvas(url);
28345 reader.readAsArrayBuffer(this.file);
28351 parseExifData : function(dataView, offset, length)
28353 var tiffOffset = offset + 10,
28357 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28358 // No Exif data, might be XMP data instead
28362 // Check for the ASCII code for "Exif" (0x45786966):
28363 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28364 // No Exif data, might be XMP data instead
28367 if (tiffOffset + 8 > dataView.byteLength) {
28368 Roo.log('Invalid Exif data: Invalid segment size.');
28371 // Check for the two null bytes:
28372 if (dataView.getUint16(offset + 8) !== 0x0000) {
28373 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28376 // Check the byte alignment:
28377 switch (dataView.getUint16(tiffOffset)) {
28379 littleEndian = true;
28382 littleEndian = false;
28385 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28388 // Check for the TIFF tag marker (0x002A):
28389 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28390 Roo.log('Invalid Exif data: Missing TIFF marker.');
28393 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28394 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28396 this.parseExifTags(
28399 tiffOffset + dirOffset,
28404 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28409 if (dirOffset + 6 > dataView.byteLength) {
28410 Roo.log('Invalid Exif data: Invalid directory offset.');
28413 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28414 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28415 if (dirEndOffset + 4 > dataView.byteLength) {
28416 Roo.log('Invalid Exif data: Invalid directory size.');
28419 for (i = 0; i < tagsNumber; i += 1) {
28423 dirOffset + 2 + 12 * i, // tag offset
28427 // Return the offset to the next directory:
28428 return dataView.getUint32(dirEndOffset, littleEndian);
28431 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28433 var tag = dataView.getUint16(offset, littleEndian);
28435 this.exif[tag] = this.getExifValue(
28439 dataView.getUint16(offset + 2, littleEndian), // tag type
28440 dataView.getUint32(offset + 4, littleEndian), // tag length
28445 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28447 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28456 Roo.log('Invalid Exif data: Invalid tag type.');
28460 tagSize = tagType.size * length;
28461 // Determine if the value is contained in the dataOffset bytes,
28462 // or if the value at the dataOffset is a pointer to the actual data:
28463 dataOffset = tagSize > 4 ?
28464 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28465 if (dataOffset + tagSize > dataView.byteLength) {
28466 Roo.log('Invalid Exif data: Invalid data offset.');
28469 if (length === 1) {
28470 return tagType.getValue(dataView, dataOffset, littleEndian);
28473 for (i = 0; i < length; i += 1) {
28474 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28477 if (tagType.ascii) {
28479 // Concatenate the chars:
28480 for (i = 0; i < values.length; i += 1) {
28482 // Ignore the terminating NULL byte(s):
28483 if (c === '\u0000') {
28495 Roo.apply(Roo.bootstrap.UploadCropbox, {
28497 'Orientation': 0x0112
28501 1: 0, //'top-left',
28503 3: 180, //'bottom-right',
28504 // 4: 'bottom-left',
28506 6: 90, //'right-top',
28507 // 7: 'right-bottom',
28508 8: 270 //'left-bottom'
28512 // byte, 8-bit unsigned int:
28514 getValue: function (dataView, dataOffset) {
28515 return dataView.getUint8(dataOffset);
28519 // ascii, 8-bit byte:
28521 getValue: function (dataView, dataOffset) {
28522 return String.fromCharCode(dataView.getUint8(dataOffset));
28527 // short, 16 bit int:
28529 getValue: function (dataView, dataOffset, littleEndian) {
28530 return dataView.getUint16(dataOffset, littleEndian);
28534 // long, 32 bit int:
28536 getValue: function (dataView, dataOffset, littleEndian) {
28537 return dataView.getUint32(dataOffset, littleEndian);
28541 // rational = two long values, first is numerator, second is denominator:
28543 getValue: function (dataView, dataOffset, littleEndian) {
28544 return dataView.getUint32(dataOffset, littleEndian) /
28545 dataView.getUint32(dataOffset + 4, littleEndian);
28549 // slong, 32 bit signed int:
28551 getValue: function (dataView, dataOffset, littleEndian) {
28552 return dataView.getInt32(dataOffset, littleEndian);
28556 // srational, two slongs, first is numerator, second is denominator:
28558 getValue: function (dataView, dataOffset, littleEndian) {
28559 return dataView.getInt32(dataOffset, littleEndian) /
28560 dataView.getInt32(dataOffset + 4, littleEndian);
28570 cls : 'btn-group roo-upload-cropbox-rotate-left',
28571 action : 'rotate-left',
28575 cls : 'btn btn-default',
28576 html : '<i class="fa fa-undo"></i>'
28582 cls : 'btn-group roo-upload-cropbox-picture',
28583 action : 'picture',
28587 cls : 'btn btn-default',
28588 html : '<i class="fa fa-picture-o"></i>'
28594 cls : 'btn-group roo-upload-cropbox-rotate-right',
28595 action : 'rotate-right',
28599 cls : 'btn btn-default',
28600 html : '<i class="fa fa-repeat"></i>'
28608 cls : 'btn-group roo-upload-cropbox-rotate-left',
28609 action : 'rotate-left',
28613 cls : 'btn btn-default',
28614 html : '<i class="fa fa-undo"></i>'
28620 cls : 'btn-group roo-upload-cropbox-download',
28621 action : 'download',
28625 cls : 'btn btn-default',
28626 html : '<i class="fa fa-download"></i>'
28632 cls : 'btn-group roo-upload-cropbox-crop',
28637 cls : 'btn btn-default',
28638 html : '<i class="fa fa-crop"></i>'
28644 cls : 'btn-group roo-upload-cropbox-trash',
28649 cls : 'btn btn-default',
28650 html : '<i class="fa fa-trash"></i>'
28656 cls : 'btn-group roo-upload-cropbox-rotate-right',
28657 action : 'rotate-right',
28661 cls : 'btn btn-default',
28662 html : '<i class="fa fa-repeat"></i>'
28670 cls : 'btn-group roo-upload-cropbox-rotate-left',
28671 action : 'rotate-left',
28675 cls : 'btn btn-default',
28676 html : '<i class="fa fa-undo"></i>'
28682 cls : 'btn-group roo-upload-cropbox-rotate-right',
28683 action : 'rotate-right',
28687 cls : 'btn btn-default',
28688 html : '<i class="fa fa-repeat"></i>'
28701 * @class Roo.bootstrap.DocumentManager
28702 * @extends Roo.bootstrap.Component
28703 * Bootstrap DocumentManager class
28704 * @cfg {String} paramName default 'imageUpload'
28705 * @cfg {String} toolTipName default 'filename'
28706 * @cfg {String} method default POST
28707 * @cfg {String} url action url
28708 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28709 * @cfg {Boolean} multiple multiple upload default true
28710 * @cfg {Number} thumbSize default 300
28711 * @cfg {String} fieldLabel
28712 * @cfg {Number} labelWidth default 4
28713 * @cfg {String} labelAlign (left|top) default left
28714 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28715 * @cfg {Number} labellg set the width of label (1-12)
28716 * @cfg {Number} labelmd set the width of label (1-12)
28717 * @cfg {Number} labelsm set the width of label (1-12)
28718 * @cfg {Number} labelxs set the width of label (1-12)
28721 * Create a new DocumentManager
28722 * @param {Object} config The config object
28725 Roo.bootstrap.DocumentManager = function(config){
28726 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28729 this.delegates = [];
28734 * Fire when initial the DocumentManager
28735 * @param {Roo.bootstrap.DocumentManager} this
28740 * inspect selected file
28741 * @param {Roo.bootstrap.DocumentManager} this
28742 * @param {File} file
28747 * Fire when xhr load exception
28748 * @param {Roo.bootstrap.DocumentManager} this
28749 * @param {XMLHttpRequest} xhr
28751 "exception" : true,
28753 * @event afterupload
28754 * Fire when xhr load exception
28755 * @param {Roo.bootstrap.DocumentManager} this
28756 * @param {XMLHttpRequest} xhr
28758 "afterupload" : true,
28761 * prepare the form data
28762 * @param {Roo.bootstrap.DocumentManager} this
28763 * @param {Object} formData
28768 * Fire when remove the file
28769 * @param {Roo.bootstrap.DocumentManager} this
28770 * @param {Object} file
28775 * Fire after refresh the file
28776 * @param {Roo.bootstrap.DocumentManager} this
28781 * Fire after click the image
28782 * @param {Roo.bootstrap.DocumentManager} this
28783 * @param {Object} file
28788 * Fire when upload a image and editable set to true
28789 * @param {Roo.bootstrap.DocumentManager} this
28790 * @param {Object} file
28794 * @event beforeselectfile
28795 * Fire before select file
28796 * @param {Roo.bootstrap.DocumentManager} this
28798 "beforeselectfile" : true,
28801 * Fire before process file
28802 * @param {Roo.bootstrap.DocumentManager} this
28803 * @param {Object} file
28807 * @event previewrendered
28808 * Fire when preview rendered
28809 * @param {Roo.bootstrap.DocumentManager} this
28810 * @param {Object} file
28812 "previewrendered" : true,
28815 "previewResize" : true
28820 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28829 paramName : 'imageUpload',
28830 toolTipName : 'filename',
28833 labelAlign : 'left',
28843 getAutoCreate : function()
28845 var managerWidget = {
28847 cls : 'roo-document-manager',
28851 cls : 'roo-document-manager-selector',
28856 cls : 'roo-document-manager-uploader',
28860 cls : 'roo-document-manager-upload-btn',
28861 html : '<i class="fa fa-plus"></i>'
28872 cls : 'column col-md-12',
28877 if(this.fieldLabel.length){
28882 cls : 'column col-md-12',
28883 html : this.fieldLabel
28887 cls : 'column col-md-12',
28892 if(this.labelAlign == 'left'){
28897 html : this.fieldLabel
28906 if(this.labelWidth > 12){
28907 content[0].style = "width: " + this.labelWidth + 'px';
28910 if(this.labelWidth < 13 && this.labelmd == 0){
28911 this.labelmd = this.labelWidth;
28914 if(this.labellg > 0){
28915 content[0].cls += ' col-lg-' + this.labellg;
28916 content[1].cls += ' col-lg-' + (12 - this.labellg);
28919 if(this.labelmd > 0){
28920 content[0].cls += ' col-md-' + this.labelmd;
28921 content[1].cls += ' col-md-' + (12 - this.labelmd);
28924 if(this.labelsm > 0){
28925 content[0].cls += ' col-sm-' + this.labelsm;
28926 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28929 if(this.labelxs > 0){
28930 content[0].cls += ' col-xs-' + this.labelxs;
28931 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28939 cls : 'row clearfix',
28947 initEvents : function()
28949 this.managerEl = this.el.select('.roo-document-manager', true).first();
28950 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28952 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28953 this.selectorEl.hide();
28956 this.selectorEl.attr('multiple', 'multiple');
28959 this.selectorEl.on('change', this.onFileSelected, this);
28961 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28962 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28964 this.uploader.on('click', this.onUploaderClick, this);
28966 this.renderProgressDialog();
28970 window.addEventListener("resize", function() { _this.refresh(); } );
28972 this.fireEvent('initial', this);
28975 renderProgressDialog : function()
28979 this.progressDialog = new Roo.bootstrap.Modal({
28980 cls : 'roo-document-manager-progress-dialog',
28981 allow_close : false,
28991 btnclick : function() {
28992 _this.uploadCancel();
28998 this.progressDialog.render(Roo.get(document.body));
29000 this.progress = new Roo.bootstrap.Progress({
29001 cls : 'roo-document-manager-progress',
29006 this.progress.render(this.progressDialog.getChildContainer());
29008 this.progressBar = new Roo.bootstrap.ProgressBar({
29009 cls : 'roo-document-manager-progress-bar',
29012 aria_valuemax : 12,
29016 this.progressBar.render(this.progress.getChildContainer());
29019 onUploaderClick : function(e)
29021 e.preventDefault();
29023 if(this.fireEvent('beforeselectfile', this) != false){
29024 this.selectorEl.dom.click();
29029 onFileSelected : function(e)
29031 e.preventDefault();
29033 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29037 Roo.each(this.selectorEl.dom.files, function(file){
29038 if(this.fireEvent('inspect', this, file) != false){
29039 this.files.push(file);
29049 this.selectorEl.dom.value = '';
29051 if(!this.files || !this.files.length){
29055 if(this.boxes > 0 && this.files.length > this.boxes){
29056 this.files = this.files.slice(0, this.boxes);
29059 this.uploader.show();
29061 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29062 this.uploader.hide();
29071 Roo.each(this.files, function(file){
29073 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29074 var f = this.renderPreview(file);
29079 if(file.type.indexOf('image') != -1){
29080 this.delegates.push(
29082 _this.process(file);
29083 }).createDelegate(this)
29091 _this.process(file);
29092 }).createDelegate(this)
29097 this.files = files;
29099 this.delegates = this.delegates.concat(docs);
29101 if(!this.delegates.length){
29106 this.progressBar.aria_valuemax = this.delegates.length;
29113 arrange : function()
29115 if(!this.delegates.length){
29116 this.progressDialog.hide();
29121 var delegate = this.delegates.shift();
29123 this.progressDialog.show();
29125 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29127 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29132 refresh : function()
29134 this.uploader.show();
29136 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29137 this.uploader.hide();
29140 Roo.isTouch ? this.closable(false) : this.closable(true);
29142 this.fireEvent('refresh', this);
29145 onRemove : function(e, el, o)
29147 e.preventDefault();
29149 this.fireEvent('remove', this, o);
29153 remove : function(o)
29157 Roo.each(this.files, function(file){
29158 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29167 this.files = files;
29174 Roo.each(this.files, function(file){
29179 file.target.remove();
29188 onClick : function(e, el, o)
29190 e.preventDefault();
29192 this.fireEvent('click', this, o);
29196 closable : function(closable)
29198 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29200 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29212 xhrOnLoad : function(xhr)
29214 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29218 if (xhr.readyState !== 4) {
29220 this.fireEvent('exception', this, xhr);
29224 var response = Roo.decode(xhr.responseText);
29226 if(!response.success){
29228 this.fireEvent('exception', this, xhr);
29232 var file = this.renderPreview(response.data);
29234 this.files.push(file);
29238 this.fireEvent('afterupload', this, xhr);
29242 xhrOnError : function(xhr)
29244 Roo.log('xhr on error');
29246 var response = Roo.decode(xhr.responseText);
29253 process : function(file)
29255 if(this.fireEvent('process', this, file) !== false){
29256 if(this.editable && file.type.indexOf('image') != -1){
29257 this.fireEvent('edit', this, file);
29261 this.uploadStart(file, false);
29268 uploadStart : function(file, crop)
29270 this.xhr = new XMLHttpRequest();
29272 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29277 file.xhr = this.xhr;
29279 this.managerEl.createChild({
29281 cls : 'roo-document-manager-loading',
29285 tooltip : file.name,
29286 cls : 'roo-document-manager-thumb',
29287 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29293 this.xhr.open(this.method, this.url, true);
29296 "Accept": "application/json",
29297 "Cache-Control": "no-cache",
29298 "X-Requested-With": "XMLHttpRequest"
29301 for (var headerName in headers) {
29302 var headerValue = headers[headerName];
29304 this.xhr.setRequestHeader(headerName, headerValue);
29310 this.xhr.onload = function()
29312 _this.xhrOnLoad(_this.xhr);
29315 this.xhr.onerror = function()
29317 _this.xhrOnError(_this.xhr);
29320 var formData = new FormData();
29322 formData.append('returnHTML', 'NO');
29325 formData.append('crop', crop);
29328 formData.append(this.paramName, file, file.name);
29335 if(this.fireEvent('prepare', this, formData, options) != false){
29337 if(options.manually){
29341 this.xhr.send(formData);
29345 this.uploadCancel();
29348 uploadCancel : function()
29354 this.delegates = [];
29356 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29363 renderPreview : function(file)
29365 if(typeof(file.target) != 'undefined' && file.target){
29369 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29371 var previewEl = this.managerEl.createChild({
29373 cls : 'roo-document-manager-preview',
29377 tooltip : file[this.toolTipName],
29378 cls : 'roo-document-manager-thumb',
29379 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29384 html : '<i class="fa fa-times-circle"></i>'
29389 var close = previewEl.select('button.close', true).first();
29391 close.on('click', this.onRemove, this, file);
29393 file.target = previewEl;
29395 var image = previewEl.select('img', true).first();
29399 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29401 image.on('click', this.onClick, this, file);
29403 this.fireEvent('previewrendered', this, file);
29409 onPreviewLoad : function(file, image)
29411 if(typeof(file.target) == 'undefined' || !file.target){
29415 var width = image.dom.naturalWidth || image.dom.width;
29416 var height = image.dom.naturalHeight || image.dom.height;
29418 if(!this.previewResize) {
29422 if(width > height){
29423 file.target.addClass('wide');
29427 file.target.addClass('tall');
29432 uploadFromSource : function(file, crop)
29434 this.xhr = new XMLHttpRequest();
29436 this.managerEl.createChild({
29438 cls : 'roo-document-manager-loading',
29442 tooltip : file.name,
29443 cls : 'roo-document-manager-thumb',
29444 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29450 this.xhr.open(this.method, this.url, true);
29453 "Accept": "application/json",
29454 "Cache-Control": "no-cache",
29455 "X-Requested-With": "XMLHttpRequest"
29458 for (var headerName in headers) {
29459 var headerValue = headers[headerName];
29461 this.xhr.setRequestHeader(headerName, headerValue);
29467 this.xhr.onload = function()
29469 _this.xhrOnLoad(_this.xhr);
29472 this.xhr.onerror = function()
29474 _this.xhrOnError(_this.xhr);
29477 var formData = new FormData();
29479 formData.append('returnHTML', 'NO');
29481 formData.append('crop', crop);
29483 if(typeof(file.filename) != 'undefined'){
29484 formData.append('filename', file.filename);
29487 if(typeof(file.mimetype) != 'undefined'){
29488 formData.append('mimetype', file.mimetype);
29493 if(this.fireEvent('prepare', this, formData) != false){
29494 this.xhr.send(formData);
29504 * @class Roo.bootstrap.DocumentViewer
29505 * @extends Roo.bootstrap.Component
29506 * Bootstrap DocumentViewer class
29507 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29508 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29511 * Create a new DocumentViewer
29512 * @param {Object} config The config object
29515 Roo.bootstrap.DocumentViewer = function(config){
29516 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29521 * Fire after initEvent
29522 * @param {Roo.bootstrap.DocumentViewer} this
29528 * @param {Roo.bootstrap.DocumentViewer} this
29533 * Fire after download button
29534 * @param {Roo.bootstrap.DocumentViewer} this
29539 * Fire after trash button
29540 * @param {Roo.bootstrap.DocumentViewer} this
29547 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29549 showDownload : true,
29553 getAutoCreate : function()
29557 cls : 'roo-document-viewer',
29561 cls : 'roo-document-viewer-body',
29565 cls : 'roo-document-viewer-thumb',
29569 cls : 'roo-document-viewer-image'
29577 cls : 'roo-document-viewer-footer',
29580 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29584 cls : 'btn-group roo-document-viewer-download',
29588 cls : 'btn btn-default',
29589 html : '<i class="fa fa-download"></i>'
29595 cls : 'btn-group roo-document-viewer-trash',
29599 cls : 'btn btn-default',
29600 html : '<i class="fa fa-trash"></i>'
29613 initEvents : function()
29615 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29616 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29618 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29619 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29621 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29622 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29624 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29625 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29627 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29628 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29630 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29631 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29633 this.bodyEl.on('click', this.onClick, this);
29634 this.downloadBtn.on('click', this.onDownload, this);
29635 this.trashBtn.on('click', this.onTrash, this);
29637 this.downloadBtn.hide();
29638 this.trashBtn.hide();
29640 if(this.showDownload){
29641 this.downloadBtn.show();
29644 if(this.showTrash){
29645 this.trashBtn.show();
29648 if(!this.showDownload && !this.showTrash) {
29649 this.footerEl.hide();
29654 initial : function()
29656 this.fireEvent('initial', this);
29660 onClick : function(e)
29662 e.preventDefault();
29664 this.fireEvent('click', this);
29667 onDownload : function(e)
29669 e.preventDefault();
29671 this.fireEvent('download', this);
29674 onTrash : function(e)
29676 e.preventDefault();
29678 this.fireEvent('trash', this);
29690 * @class Roo.bootstrap.NavProgressBar
29691 * @extends Roo.bootstrap.Component
29692 * Bootstrap NavProgressBar class
29695 * Create a new nav progress bar
29696 * @param {Object} config The config object
29699 Roo.bootstrap.NavProgressBar = function(config){
29700 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29702 this.bullets = this.bullets || [];
29704 // Roo.bootstrap.NavProgressBar.register(this);
29708 * Fires when the active item changes
29709 * @param {Roo.bootstrap.NavProgressBar} this
29710 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29711 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29718 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29723 getAutoCreate : function()
29725 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29729 cls : 'roo-navigation-bar-group',
29733 cls : 'roo-navigation-top-bar'
29737 cls : 'roo-navigation-bullets-bar',
29741 cls : 'roo-navigation-bar'
29748 cls : 'roo-navigation-bottom-bar'
29758 initEvents: function()
29763 onRender : function(ct, position)
29765 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29767 if(this.bullets.length){
29768 Roo.each(this.bullets, function(b){
29777 addItem : function(cfg)
29779 var item = new Roo.bootstrap.NavProgressItem(cfg);
29781 item.parentId = this.id;
29782 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29785 var top = new Roo.bootstrap.Element({
29787 cls : 'roo-navigation-bar-text'
29790 var bottom = new Roo.bootstrap.Element({
29792 cls : 'roo-navigation-bar-text'
29795 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29796 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29798 var topText = new Roo.bootstrap.Element({
29800 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29803 var bottomText = new Roo.bootstrap.Element({
29805 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29808 topText.onRender(top.el, null);
29809 bottomText.onRender(bottom.el, null);
29812 item.bottomEl = bottom;
29815 this.barItems.push(item);
29820 getActive : function()
29822 var active = false;
29824 Roo.each(this.barItems, function(v){
29826 if (!v.isActive()) {
29838 setActiveItem : function(item)
29842 Roo.each(this.barItems, function(v){
29843 if (v.rid == item.rid) {
29847 if (v.isActive()) {
29848 v.setActive(false);
29853 item.setActive(true);
29855 this.fireEvent('changed', this, item, prev);
29858 getBarItem: function(rid)
29862 Roo.each(this.barItems, function(e) {
29863 if (e.rid != rid) {
29874 indexOfItem : function(item)
29878 Roo.each(this.barItems, function(v, i){
29880 if (v.rid != item.rid) {
29891 setActiveNext : function()
29893 var i = this.indexOfItem(this.getActive());
29895 if (i > this.barItems.length) {
29899 this.setActiveItem(this.barItems[i+1]);
29902 setActivePrev : function()
29904 var i = this.indexOfItem(this.getActive());
29910 this.setActiveItem(this.barItems[i-1]);
29913 format : function()
29915 if(!this.barItems.length){
29919 var width = 100 / this.barItems.length;
29921 Roo.each(this.barItems, function(i){
29922 i.el.setStyle('width', width + '%');
29923 i.topEl.el.setStyle('width', width + '%');
29924 i.bottomEl.el.setStyle('width', width + '%');
29933 * Nav Progress Item
29938 * @class Roo.bootstrap.NavProgressItem
29939 * @extends Roo.bootstrap.Component
29940 * Bootstrap NavProgressItem class
29941 * @cfg {String} rid the reference id
29942 * @cfg {Boolean} active (true|false) Is item active default false
29943 * @cfg {Boolean} disabled (true|false) Is item active default false
29944 * @cfg {String} html
29945 * @cfg {String} position (top|bottom) text position default bottom
29946 * @cfg {String} icon show icon instead of number
29949 * Create a new NavProgressItem
29950 * @param {Object} config The config object
29952 Roo.bootstrap.NavProgressItem = function(config){
29953 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29958 * The raw click event for the entire grid.
29959 * @param {Roo.bootstrap.NavProgressItem} this
29960 * @param {Roo.EventObject} e
29967 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29973 position : 'bottom',
29976 getAutoCreate : function()
29978 var iconCls = 'roo-navigation-bar-item-icon';
29980 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29984 cls: 'roo-navigation-bar-item',
29994 cfg.cls += ' active';
29997 cfg.cls += ' disabled';
30003 disable : function()
30005 this.setDisabled(true);
30008 enable : function()
30010 this.setDisabled(false);
30013 initEvents: function()
30015 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30017 this.iconEl.on('click', this.onClick, this);
30020 onClick : function(e)
30022 e.preventDefault();
30028 if(this.fireEvent('click', this, e) === false){
30032 this.parent().setActiveItem(this);
30035 isActive: function ()
30037 return this.active;
30040 setActive : function(state)
30042 if(this.active == state){
30046 this.active = state;
30049 this.el.addClass('active');
30053 this.el.removeClass('active');
30058 setDisabled : function(state)
30060 if(this.disabled == state){
30064 this.disabled = state;
30067 this.el.addClass('disabled');
30071 this.el.removeClass('disabled');
30074 tooltipEl : function()
30076 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30089 * @class Roo.bootstrap.FieldLabel
30090 * @extends Roo.bootstrap.Component
30091 * Bootstrap FieldLabel class
30092 * @cfg {String} html contents of the element
30093 * @cfg {String} tag tag of the element default label
30094 * @cfg {String} cls class of the element
30095 * @cfg {String} target label target
30096 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30097 * @cfg {String} invalidClass default "text-warning"
30098 * @cfg {String} validClass default "text-success"
30099 * @cfg {String} iconTooltip default "This field is required"
30100 * @cfg {String} indicatorpos (left|right) default left
30103 * Create a new FieldLabel
30104 * @param {Object} config The config object
30107 Roo.bootstrap.FieldLabel = function(config){
30108 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30113 * Fires after the field has been marked as invalid.
30114 * @param {Roo.form.FieldLabel} this
30115 * @param {String} msg The validation message
30120 * Fires after the field has been validated with no errors.
30121 * @param {Roo.form.FieldLabel} this
30127 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30134 invalidClass : 'has-warning',
30135 validClass : 'has-success',
30136 iconTooltip : 'This field is required',
30137 indicatorpos : 'left',
30139 getAutoCreate : function(){
30142 if (!this.allowBlank) {
30148 cls : 'roo-bootstrap-field-label ' + this.cls,
30153 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30154 tooltip : this.iconTooltip
30163 if(this.indicatorpos == 'right'){
30166 cls : 'roo-bootstrap-field-label ' + this.cls,
30175 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30176 tooltip : this.iconTooltip
30185 initEvents: function()
30187 Roo.bootstrap.Element.superclass.initEvents.call(this);
30189 this.indicator = this.indicatorEl();
30191 if(this.indicator){
30192 this.indicator.removeClass('visible');
30193 this.indicator.addClass('invisible');
30196 Roo.bootstrap.FieldLabel.register(this);
30199 indicatorEl : function()
30201 var indicator = this.el.select('i.roo-required-indicator',true).first();
30212 * Mark this field as valid
30214 markValid : function()
30216 if(this.indicator){
30217 this.indicator.removeClass('visible');
30218 this.indicator.addClass('invisible');
30221 this.el.removeClass(this.invalidClass);
30223 this.el.addClass(this.validClass);
30225 this.fireEvent('valid', this);
30229 * Mark this field as invalid
30230 * @param {String} msg The validation message
30232 markInvalid : function(msg)
30234 if(this.indicator){
30235 this.indicator.removeClass('invisible');
30236 this.indicator.addClass('visible');
30239 this.el.removeClass(this.validClass);
30241 this.el.addClass(this.invalidClass);
30243 this.fireEvent('invalid', this, msg);
30249 Roo.apply(Roo.bootstrap.FieldLabel, {
30254 * register a FieldLabel Group
30255 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30257 register : function(label)
30259 if(this.groups.hasOwnProperty(label.target)){
30263 this.groups[label.target] = label;
30267 * fetch a FieldLabel Group based on the target
30268 * @param {string} target
30269 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30271 get: function(target) {
30272 if (typeof(this.groups[target]) == 'undefined') {
30276 return this.groups[target] ;
30285 * page DateSplitField.
30291 * @class Roo.bootstrap.DateSplitField
30292 * @extends Roo.bootstrap.Component
30293 * Bootstrap DateSplitField class
30294 * @cfg {string} fieldLabel - the label associated
30295 * @cfg {Number} labelWidth set the width of label (0-12)
30296 * @cfg {String} labelAlign (top|left)
30297 * @cfg {Boolean} dayAllowBlank (true|false) default false
30298 * @cfg {Boolean} monthAllowBlank (true|false) default false
30299 * @cfg {Boolean} yearAllowBlank (true|false) default false
30300 * @cfg {string} dayPlaceholder
30301 * @cfg {string} monthPlaceholder
30302 * @cfg {string} yearPlaceholder
30303 * @cfg {string} dayFormat default 'd'
30304 * @cfg {string} monthFormat default 'm'
30305 * @cfg {string} yearFormat default 'Y'
30306 * @cfg {Number} labellg set the width of label (1-12)
30307 * @cfg {Number} labelmd set the width of label (1-12)
30308 * @cfg {Number} labelsm set the width of label (1-12)
30309 * @cfg {Number} labelxs set the width of label (1-12)
30313 * Create a new DateSplitField
30314 * @param {Object} config The config object
30317 Roo.bootstrap.DateSplitField = function(config){
30318 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30324 * getting the data of years
30325 * @param {Roo.bootstrap.DateSplitField} this
30326 * @param {Object} years
30331 * getting the data of days
30332 * @param {Roo.bootstrap.DateSplitField} this
30333 * @param {Object} days
30338 * Fires after the field has been marked as invalid.
30339 * @param {Roo.form.Field} this
30340 * @param {String} msg The validation message
30345 * Fires after the field has been validated with no errors.
30346 * @param {Roo.form.Field} this
30352 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30355 labelAlign : 'top',
30357 dayAllowBlank : false,
30358 monthAllowBlank : false,
30359 yearAllowBlank : false,
30360 dayPlaceholder : '',
30361 monthPlaceholder : '',
30362 yearPlaceholder : '',
30366 isFormField : true,
30372 getAutoCreate : function()
30376 cls : 'row roo-date-split-field-group',
30381 cls : 'form-hidden-field roo-date-split-field-group-value',
30387 var labelCls = 'col-md-12';
30388 var contentCls = 'col-md-4';
30390 if(this.fieldLabel){
30394 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30398 html : this.fieldLabel
30403 if(this.labelAlign == 'left'){
30405 if(this.labelWidth > 12){
30406 label.style = "width: " + this.labelWidth + 'px';
30409 if(this.labelWidth < 13 && this.labelmd == 0){
30410 this.labelmd = this.labelWidth;
30413 if(this.labellg > 0){
30414 labelCls = ' col-lg-' + this.labellg;
30415 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30418 if(this.labelmd > 0){
30419 labelCls = ' col-md-' + this.labelmd;
30420 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30423 if(this.labelsm > 0){
30424 labelCls = ' col-sm-' + this.labelsm;
30425 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30428 if(this.labelxs > 0){
30429 labelCls = ' col-xs-' + this.labelxs;
30430 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30434 label.cls += ' ' + labelCls;
30436 cfg.cn.push(label);
30439 Roo.each(['day', 'month', 'year'], function(t){
30442 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30449 inputEl: function ()
30451 return this.el.select('.roo-date-split-field-group-value', true).first();
30454 onRender : function(ct, position)
30458 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30460 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30462 this.dayField = new Roo.bootstrap.ComboBox({
30463 allowBlank : this.dayAllowBlank,
30464 alwaysQuery : true,
30465 displayField : 'value',
30468 forceSelection : true,
30470 placeholder : this.dayPlaceholder,
30471 selectOnFocus : true,
30472 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30473 triggerAction : 'all',
30475 valueField : 'value',
30476 store : new Roo.data.SimpleStore({
30477 data : (function() {
30479 _this.fireEvent('days', _this, days);
30482 fields : [ 'value' ]
30485 select : function (_self, record, index)
30487 _this.setValue(_this.getValue());
30492 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30494 this.monthField = new Roo.bootstrap.MonthField({
30495 after : '<i class=\"fa fa-calendar\"></i>',
30496 allowBlank : this.monthAllowBlank,
30497 placeholder : this.monthPlaceholder,
30500 render : function (_self)
30502 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30503 e.preventDefault();
30507 select : function (_self, oldvalue, newvalue)
30509 _this.setValue(_this.getValue());
30514 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30516 this.yearField = new Roo.bootstrap.ComboBox({
30517 allowBlank : this.yearAllowBlank,
30518 alwaysQuery : true,
30519 displayField : 'value',
30522 forceSelection : true,
30524 placeholder : this.yearPlaceholder,
30525 selectOnFocus : true,
30526 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30527 triggerAction : 'all',
30529 valueField : 'value',
30530 store : new Roo.data.SimpleStore({
30531 data : (function() {
30533 _this.fireEvent('years', _this, years);
30536 fields : [ 'value' ]
30539 select : function (_self, record, index)
30541 _this.setValue(_this.getValue());
30546 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30549 setValue : function(v, format)
30551 this.inputEl.dom.value = v;
30553 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30555 var d = Date.parseDate(v, f);
30562 this.setDay(d.format(this.dayFormat));
30563 this.setMonth(d.format(this.monthFormat));
30564 this.setYear(d.format(this.yearFormat));
30571 setDay : function(v)
30573 this.dayField.setValue(v);
30574 this.inputEl.dom.value = this.getValue();
30579 setMonth : function(v)
30581 this.monthField.setValue(v, true);
30582 this.inputEl.dom.value = this.getValue();
30587 setYear : function(v)
30589 this.yearField.setValue(v);
30590 this.inputEl.dom.value = this.getValue();
30595 getDay : function()
30597 return this.dayField.getValue();
30600 getMonth : function()
30602 return this.monthField.getValue();
30605 getYear : function()
30607 return this.yearField.getValue();
30610 getValue : function()
30612 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30614 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30624 this.inputEl.dom.value = '';
30629 validate : function()
30631 var d = this.dayField.validate();
30632 var m = this.monthField.validate();
30633 var y = this.yearField.validate();
30638 (!this.dayAllowBlank && !d) ||
30639 (!this.monthAllowBlank && !m) ||
30640 (!this.yearAllowBlank && !y)
30645 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30654 this.markInvalid();
30659 markValid : function()
30662 var label = this.el.select('label', true).first();
30663 var icon = this.el.select('i.fa-star', true).first();
30669 this.fireEvent('valid', this);
30673 * Mark this field as invalid
30674 * @param {String} msg The validation message
30676 markInvalid : function(msg)
30679 var label = this.el.select('label', true).first();
30680 var icon = this.el.select('i.fa-star', true).first();
30682 if(label && !icon){
30683 this.el.select('.roo-date-split-field-label', true).createChild({
30685 cls : 'text-danger fa fa-lg fa-star',
30686 tooltip : 'This field is required',
30687 style : 'margin-right:5px;'
30691 this.fireEvent('invalid', this, msg);
30694 clearInvalid : function()
30696 var label = this.el.select('label', true).first();
30697 var icon = this.el.select('i.fa-star', true).first();
30703 this.fireEvent('valid', this);
30706 getName: function()
30716 * http://masonry.desandro.com
30718 * The idea is to render all the bricks based on vertical width...
30720 * The original code extends 'outlayer' - we might need to use that....
30726 * @class Roo.bootstrap.LayoutMasonry
30727 * @extends Roo.bootstrap.Component
30728 * Bootstrap Layout Masonry class
30731 * Create a new Element
30732 * @param {Object} config The config object
30735 Roo.bootstrap.LayoutMasonry = function(config){
30737 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30741 Roo.bootstrap.LayoutMasonry.register(this);
30747 * Fire after layout the items
30748 * @param {Roo.bootstrap.LayoutMasonry} this
30749 * @param {Roo.EventObject} e
30756 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30759 * @cfg {Boolean} isLayoutInstant = no animation?
30761 isLayoutInstant : false, // needed?
30764 * @cfg {Number} boxWidth width of the columns
30769 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30774 * @cfg {Number} padWidth padding below box..
30779 * @cfg {Number} gutter gutter width..
30784 * @cfg {Number} maxCols maximum number of columns
30790 * @cfg {Boolean} isAutoInitial defalut true
30792 isAutoInitial : true,
30797 * @cfg {Boolean} isHorizontal defalut false
30799 isHorizontal : false,
30801 currentSize : null,
30807 bricks: null, //CompositeElement
30811 _isLayoutInited : false,
30813 // isAlternative : false, // only use for vertical layout...
30816 * @cfg {Number} alternativePadWidth padding below box..
30818 alternativePadWidth : 50,
30820 selectedBrick : [],
30822 getAutoCreate : function(){
30824 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30828 cls: 'blog-masonary-wrapper ' + this.cls,
30830 cls : 'mas-boxes masonary'
30837 getChildContainer: function( )
30839 if (this.boxesEl) {
30840 return this.boxesEl;
30843 this.boxesEl = this.el.select('.mas-boxes').first();
30845 return this.boxesEl;
30849 initEvents : function()
30853 if(this.isAutoInitial){
30854 Roo.log('hook children rendered');
30855 this.on('childrenrendered', function() {
30856 Roo.log('children rendered');
30862 initial : function()
30864 this.selectedBrick = [];
30866 this.currentSize = this.el.getBox(true);
30868 Roo.EventManager.onWindowResize(this.resize, this);
30870 if(!this.isAutoInitial){
30878 //this.layout.defer(500,this);
30882 resize : function()
30884 var cs = this.el.getBox(true);
30887 this.currentSize.width == cs.width &&
30888 this.currentSize.x == cs.x &&
30889 this.currentSize.height == cs.height &&
30890 this.currentSize.y == cs.y
30892 Roo.log("no change in with or X or Y");
30896 this.currentSize = cs;
30902 layout : function()
30904 this._resetLayout();
30906 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30908 this.layoutItems( isInstant );
30910 this._isLayoutInited = true;
30912 this.fireEvent('layout', this);
30916 _resetLayout : function()
30918 if(this.isHorizontal){
30919 this.horizontalMeasureColumns();
30923 this.verticalMeasureColumns();
30927 verticalMeasureColumns : function()
30929 this.getContainerWidth();
30931 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30932 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30936 var boxWidth = this.boxWidth + this.padWidth;
30938 if(this.containerWidth < this.boxWidth){
30939 boxWidth = this.containerWidth
30942 var containerWidth = this.containerWidth;
30944 var cols = Math.floor(containerWidth / boxWidth);
30946 this.cols = Math.max( cols, 1 );
30948 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30950 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30952 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30954 this.colWidth = boxWidth + avail - this.padWidth;
30956 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30957 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30960 horizontalMeasureColumns : function()
30962 this.getContainerWidth();
30964 var boxWidth = this.boxWidth;
30966 if(this.containerWidth < boxWidth){
30967 boxWidth = this.containerWidth;
30970 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30972 this.el.setHeight(boxWidth);
30976 getContainerWidth : function()
30978 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30981 layoutItems : function( isInstant )
30983 Roo.log(this.bricks);
30985 var items = Roo.apply([], this.bricks);
30987 if(this.isHorizontal){
30988 this._horizontalLayoutItems( items , isInstant );
30992 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30993 // this._verticalAlternativeLayoutItems( items , isInstant );
30997 this._verticalLayoutItems( items , isInstant );
31001 _verticalLayoutItems : function ( items , isInstant)
31003 if ( !items || !items.length ) {
31008 ['xs', 'xs', 'xs', 'tall'],
31009 ['xs', 'xs', 'tall'],
31010 ['xs', 'xs', 'sm'],
31011 ['xs', 'xs', 'xs'],
31017 ['sm', 'xs', 'xs'],
31021 ['tall', 'xs', 'xs', 'xs'],
31022 ['tall', 'xs', 'xs'],
31034 Roo.each(items, function(item, k){
31036 switch (item.size) {
31037 // these layouts take up a full box,
31048 boxes.push([item]);
31071 var filterPattern = function(box, length)
31079 var pattern = box.slice(0, length);
31083 Roo.each(pattern, function(i){
31084 format.push(i.size);
31087 Roo.each(standard, function(s){
31089 if(String(s) != String(format)){
31098 if(!match && length == 1){
31103 filterPattern(box, length - 1);
31107 queue.push(pattern);
31109 box = box.slice(length, box.length);
31111 filterPattern(box, 4);
31117 Roo.each(boxes, function(box, k){
31123 if(box.length == 1){
31128 filterPattern(box, 4);
31132 this._processVerticalLayoutQueue( queue, isInstant );
31136 // _verticalAlternativeLayoutItems : function( items , isInstant )
31138 // if ( !items || !items.length ) {
31142 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31146 _horizontalLayoutItems : function ( items , isInstant)
31148 if ( !items || !items.length || items.length < 3) {
31154 var eItems = items.slice(0, 3);
31156 items = items.slice(3, items.length);
31159 ['xs', 'xs', 'xs', 'wide'],
31160 ['xs', 'xs', 'wide'],
31161 ['xs', 'xs', 'sm'],
31162 ['xs', 'xs', 'xs'],
31168 ['sm', 'xs', 'xs'],
31172 ['wide', 'xs', 'xs', 'xs'],
31173 ['wide', 'xs', 'xs'],
31186 Roo.each(items, function(item, k){
31188 switch (item.size) {
31199 boxes.push([item]);
31223 var filterPattern = function(box, length)
31231 var pattern = box.slice(0, length);
31235 Roo.each(pattern, function(i){
31236 format.push(i.size);
31239 Roo.each(standard, function(s){
31241 if(String(s) != String(format)){
31250 if(!match && length == 1){
31255 filterPattern(box, length - 1);
31259 queue.push(pattern);
31261 box = box.slice(length, box.length);
31263 filterPattern(box, 4);
31269 Roo.each(boxes, function(box, k){
31275 if(box.length == 1){
31280 filterPattern(box, 4);
31287 var pos = this.el.getBox(true);
31291 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31293 var hit_end = false;
31295 Roo.each(queue, function(box){
31299 Roo.each(box, function(b){
31301 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31311 Roo.each(box, function(b){
31313 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31316 mx = Math.max(mx, b.x);
31320 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31324 Roo.each(box, function(b){
31326 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31340 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31343 /** Sets position of item in DOM
31344 * @param {Element} item
31345 * @param {Number} x - horizontal position
31346 * @param {Number} y - vertical position
31347 * @param {Boolean} isInstant - disables transitions
31349 _processVerticalLayoutQueue : function( queue, isInstant )
31351 var pos = this.el.getBox(true);
31356 for (var i = 0; i < this.cols; i++){
31360 Roo.each(queue, function(box, k){
31362 var col = k % this.cols;
31364 Roo.each(box, function(b,kk){
31366 b.el.position('absolute');
31368 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31369 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31371 if(b.size == 'md-left' || b.size == 'md-right'){
31372 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31373 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31376 b.el.setWidth(width);
31377 b.el.setHeight(height);
31379 b.el.select('iframe',true).setSize(width,height);
31383 for (var i = 0; i < this.cols; i++){
31385 if(maxY[i] < maxY[col]){
31390 col = Math.min(col, i);
31394 x = pos.x + col * (this.colWidth + this.padWidth);
31398 var positions = [];
31400 switch (box.length){
31402 positions = this.getVerticalOneBoxColPositions(x, y, box);
31405 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31408 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31411 positions = this.getVerticalFourBoxColPositions(x, y, box);
31417 Roo.each(box, function(b,kk){
31419 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31421 var sz = b.el.getSize();
31423 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31431 for (var i = 0; i < this.cols; i++){
31432 mY = Math.max(mY, maxY[i]);
31435 this.el.setHeight(mY - pos.y);
31439 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31441 // var pos = this.el.getBox(true);
31444 // var maxX = pos.right;
31446 // var maxHeight = 0;
31448 // Roo.each(items, function(item, k){
31452 // item.el.position('absolute');
31454 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31456 // item.el.setWidth(width);
31458 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31460 // item.el.setHeight(height);
31463 // item.el.setXY([x, y], isInstant ? false : true);
31465 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31468 // y = y + height + this.alternativePadWidth;
31470 // maxHeight = maxHeight + height + this.alternativePadWidth;
31474 // this.el.setHeight(maxHeight);
31478 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31480 var pos = this.el.getBox(true);
31485 var maxX = pos.right;
31487 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31489 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31491 Roo.each(queue, function(box, k){
31493 Roo.each(box, function(b, kk){
31495 b.el.position('absolute');
31497 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31498 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31500 if(b.size == 'md-left' || b.size == 'md-right'){
31501 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31502 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31505 b.el.setWidth(width);
31506 b.el.setHeight(height);
31514 var positions = [];
31516 switch (box.length){
31518 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31521 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31524 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31527 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31533 Roo.each(box, function(b,kk){
31535 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31537 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31545 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31547 Roo.each(eItems, function(b,k){
31549 b.size = (k == 0) ? 'sm' : 'xs';
31550 b.x = (k == 0) ? 2 : 1;
31551 b.y = (k == 0) ? 2 : 1;
31553 b.el.position('absolute');
31555 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31557 b.el.setWidth(width);
31559 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31561 b.el.setHeight(height);
31565 var positions = [];
31568 x : maxX - this.unitWidth * 2 - this.gutter,
31573 x : maxX - this.unitWidth,
31574 y : minY + (this.unitWidth + this.gutter) * 2
31578 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31582 Roo.each(eItems, function(b,k){
31584 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31590 getVerticalOneBoxColPositions : function(x, y, box)
31594 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31596 if(box[0].size == 'md-left'){
31600 if(box[0].size == 'md-right'){
31605 x : x + (this.unitWidth + this.gutter) * rand,
31612 getVerticalTwoBoxColPositions : function(x, y, box)
31616 if(box[0].size == 'xs'){
31620 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31624 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31638 x : x + (this.unitWidth + this.gutter) * 2,
31639 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31646 getVerticalThreeBoxColPositions : function(x, y, box)
31650 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31658 x : x + (this.unitWidth + this.gutter) * 1,
31663 x : x + (this.unitWidth + this.gutter) * 2,
31671 if(box[0].size == 'xs' && box[1].size == 'xs'){
31680 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31684 x : x + (this.unitWidth + this.gutter) * 1,
31698 x : x + (this.unitWidth + this.gutter) * 2,
31703 x : x + (this.unitWidth + this.gutter) * 2,
31704 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31711 getVerticalFourBoxColPositions : function(x, y, box)
31715 if(box[0].size == 'xs'){
31724 y : y + (this.unitHeight + this.gutter) * 1
31729 y : y + (this.unitHeight + this.gutter) * 2
31733 x : x + (this.unitWidth + this.gutter) * 1,
31747 x : x + (this.unitWidth + this.gutter) * 2,
31752 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31753 y : y + (this.unitHeight + this.gutter) * 1
31757 x : x + (this.unitWidth + this.gutter) * 2,
31758 y : y + (this.unitWidth + this.gutter) * 2
31765 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31769 if(box[0].size == 'md-left'){
31771 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31778 if(box[0].size == 'md-right'){
31780 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31781 y : minY + (this.unitWidth + this.gutter) * 1
31787 var rand = Math.floor(Math.random() * (4 - box[0].y));
31790 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31791 y : minY + (this.unitWidth + this.gutter) * rand
31798 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31802 if(box[0].size == 'xs'){
31805 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31810 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31811 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31819 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31824 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31825 y : minY + (this.unitWidth + this.gutter) * 2
31832 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31836 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31839 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31844 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31845 y : minY + (this.unitWidth + this.gutter) * 1
31849 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31850 y : minY + (this.unitWidth + this.gutter) * 2
31857 if(box[0].size == 'xs' && box[1].size == 'xs'){
31860 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31865 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31870 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31871 y : minY + (this.unitWidth + this.gutter) * 1
31879 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31884 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31885 y : minY + (this.unitWidth + this.gutter) * 2
31889 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31890 y : minY + (this.unitWidth + this.gutter) * 2
31897 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31901 if(box[0].size == 'xs'){
31904 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31909 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31914 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),
31919 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31920 y : minY + (this.unitWidth + this.gutter) * 1
31928 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31933 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31934 y : minY + (this.unitWidth + this.gutter) * 2
31938 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31939 y : minY + (this.unitWidth + this.gutter) * 2
31943 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),
31944 y : minY + (this.unitWidth + this.gutter) * 2
31952 * remove a Masonry Brick
31953 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31955 removeBrick : function(brick_id)
31961 for (var i = 0; i<this.bricks.length; i++) {
31962 if (this.bricks[i].id == brick_id) {
31963 this.bricks.splice(i,1);
31964 this.el.dom.removeChild(Roo.get(brick_id).dom);
31971 * adds a Masonry Brick
31972 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31974 addBrick : function(cfg)
31976 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31977 //this.register(cn);
31978 cn.parentId = this.id;
31979 cn.onRender(this.el, null);
31984 * register a Masonry Brick
31985 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31988 register : function(brick)
31990 this.bricks.push(brick);
31991 brick.masonryId = this.id;
31995 * clear all the Masonry Brick
31997 clearAll : function()
32000 //this.getChildContainer().dom.innerHTML = "";
32001 this.el.dom.innerHTML = '';
32004 getSelected : function()
32006 if (!this.selectedBrick) {
32010 return this.selectedBrick;
32014 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32018 * register a Masonry Layout
32019 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32022 register : function(layout)
32024 this.groups[layout.id] = layout;
32027 * fetch a Masonry Layout based on the masonry layout ID
32028 * @param {string} the masonry layout to add
32029 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32032 get: function(layout_id) {
32033 if (typeof(this.groups[layout_id]) == 'undefined') {
32036 return this.groups[layout_id] ;
32048 * http://masonry.desandro.com
32050 * The idea is to render all the bricks based on vertical width...
32052 * The original code extends 'outlayer' - we might need to use that....
32058 * @class Roo.bootstrap.LayoutMasonryAuto
32059 * @extends Roo.bootstrap.Component
32060 * Bootstrap Layout Masonry class
32063 * Create a new Element
32064 * @param {Object} config The config object
32067 Roo.bootstrap.LayoutMasonryAuto = function(config){
32068 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32071 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32074 * @cfg {Boolean} isFitWidth - resize the width..
32076 isFitWidth : false, // options..
32078 * @cfg {Boolean} isOriginLeft = left align?
32080 isOriginLeft : true,
32082 * @cfg {Boolean} isOriginTop = top align?
32084 isOriginTop : false,
32086 * @cfg {Boolean} isLayoutInstant = no animation?
32088 isLayoutInstant : false, // needed?
32090 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32092 isResizingContainer : true,
32094 * @cfg {Number} columnWidth width of the columns
32100 * @cfg {Number} maxCols maximum number of columns
32105 * @cfg {Number} padHeight padding below box..
32111 * @cfg {Boolean} isAutoInitial defalut true
32114 isAutoInitial : true,
32120 initialColumnWidth : 0,
32121 currentSize : null,
32123 colYs : null, // array.
32130 bricks: null, //CompositeElement
32131 cols : 0, // array?
32132 // element : null, // wrapped now this.el
32133 _isLayoutInited : null,
32136 getAutoCreate : function(){
32140 cls: 'blog-masonary-wrapper ' + this.cls,
32142 cls : 'mas-boxes masonary'
32149 getChildContainer: function( )
32151 if (this.boxesEl) {
32152 return this.boxesEl;
32155 this.boxesEl = this.el.select('.mas-boxes').first();
32157 return this.boxesEl;
32161 initEvents : function()
32165 if(this.isAutoInitial){
32166 Roo.log('hook children rendered');
32167 this.on('childrenrendered', function() {
32168 Roo.log('children rendered');
32175 initial : function()
32177 this.reloadItems();
32179 this.currentSize = this.el.getBox(true);
32181 /// was window resize... - let's see if this works..
32182 Roo.EventManager.onWindowResize(this.resize, this);
32184 if(!this.isAutoInitial){
32189 this.layout.defer(500,this);
32192 reloadItems: function()
32194 this.bricks = this.el.select('.masonry-brick', true);
32196 this.bricks.each(function(b) {
32197 //Roo.log(b.getSize());
32198 if (!b.attr('originalwidth')) {
32199 b.attr('originalwidth', b.getSize().width);
32204 Roo.log(this.bricks.elements.length);
32207 resize : function()
32210 var cs = this.el.getBox(true);
32212 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32213 Roo.log("no change in with or X");
32216 this.currentSize = cs;
32220 layout : function()
32223 this._resetLayout();
32224 //this._manageStamps();
32226 // don't animate first layout
32227 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32228 this.layoutItems( isInstant );
32230 // flag for initalized
32231 this._isLayoutInited = true;
32234 layoutItems : function( isInstant )
32236 //var items = this._getItemsForLayout( this.items );
32237 // original code supports filtering layout items.. we just ignore it..
32239 this._layoutItems( this.bricks , isInstant );
32241 this._postLayout();
32243 _layoutItems : function ( items , isInstant)
32245 //this.fireEvent( 'layout', this, items );
32248 if ( !items || !items.elements.length ) {
32249 // no items, emit event with empty array
32254 items.each(function(item) {
32255 Roo.log("layout item");
32257 // get x/y object from method
32258 var position = this._getItemLayoutPosition( item );
32260 position.item = item;
32261 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32262 queue.push( position );
32265 this._processLayoutQueue( queue );
32267 /** Sets position of item in DOM
32268 * @param {Element} item
32269 * @param {Number} x - horizontal position
32270 * @param {Number} y - vertical position
32271 * @param {Boolean} isInstant - disables transitions
32273 _processLayoutQueue : function( queue )
32275 for ( var i=0, len = queue.length; i < len; i++ ) {
32276 var obj = queue[i];
32277 obj.item.position('absolute');
32278 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32284 * Any logic you want to do after each layout,
32285 * i.e. size the container
32287 _postLayout : function()
32289 this.resizeContainer();
32292 resizeContainer : function()
32294 if ( !this.isResizingContainer ) {
32297 var size = this._getContainerSize();
32299 this.el.setSize(size.width,size.height);
32300 this.boxesEl.setSize(size.width,size.height);
32306 _resetLayout : function()
32308 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32309 this.colWidth = this.el.getWidth();
32310 //this.gutter = this.el.getWidth();
32312 this.measureColumns();
32318 this.colYs.push( 0 );
32324 measureColumns : function()
32326 this.getContainerWidth();
32327 // if columnWidth is 0, default to outerWidth of first item
32328 if ( !this.columnWidth ) {
32329 var firstItem = this.bricks.first();
32330 Roo.log(firstItem);
32331 this.columnWidth = this.containerWidth;
32332 if (firstItem && firstItem.attr('originalwidth') ) {
32333 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32335 // columnWidth fall back to item of first element
32336 Roo.log("set column width?");
32337 this.initialColumnWidth = this.columnWidth ;
32339 // if first elem has no width, default to size of container
32344 if (this.initialColumnWidth) {
32345 this.columnWidth = this.initialColumnWidth;
32350 // column width is fixed at the top - however if container width get's smaller we should
32353 // this bit calcs how man columns..
32355 var columnWidth = this.columnWidth += this.gutter;
32357 // calculate columns
32358 var containerWidth = this.containerWidth + this.gutter;
32360 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32361 // fix rounding errors, typically with gutters
32362 var excess = columnWidth - containerWidth % columnWidth;
32365 // if overshoot is less than a pixel, round up, otherwise floor it
32366 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32367 cols = Math[ mathMethod ]( cols );
32368 this.cols = Math.max( cols, 1 );
32369 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32371 // padding positioning..
32372 var totalColWidth = this.cols * this.columnWidth;
32373 var padavail = this.containerWidth - totalColWidth;
32374 // so for 2 columns - we need 3 'pads'
32376 var padNeeded = (1+this.cols) * this.padWidth;
32378 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32380 this.columnWidth += padExtra
32381 //this.padWidth = Math.floor(padavail / ( this.cols));
32383 // adjust colum width so that padding is fixed??
32385 // we have 3 columns ... total = width * 3
32386 // we have X left over... that should be used by
32388 //if (this.expandC) {
32396 getContainerWidth : function()
32398 /* // container is parent if fit width
32399 var container = this.isFitWidth ? this.element.parentNode : this.element;
32400 // check that this.size and size are there
32401 // IE8 triggers resize on body size change, so they might not be
32403 var size = getSize( container ); //FIXME
32404 this.containerWidth = size && size.innerWidth; //FIXME
32407 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32411 _getItemLayoutPosition : function( item ) // what is item?
32413 // we resize the item to our columnWidth..
32415 item.setWidth(this.columnWidth);
32416 item.autoBoxAdjust = false;
32418 var sz = item.getSize();
32420 // how many columns does this brick span
32421 var remainder = this.containerWidth % this.columnWidth;
32423 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32424 // round if off by 1 pixel, otherwise use ceil
32425 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32426 colSpan = Math.min( colSpan, this.cols );
32428 // normally this should be '1' as we dont' currently allow multi width columns..
32430 var colGroup = this._getColGroup( colSpan );
32431 // get the minimum Y value from the columns
32432 var minimumY = Math.min.apply( Math, colGroup );
32433 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32435 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32437 // position the brick
32439 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32440 y: this.currentSize.y + minimumY + this.padHeight
32444 // apply setHeight to necessary columns
32445 var setHeight = minimumY + sz.height + this.padHeight;
32446 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32448 var setSpan = this.cols + 1 - colGroup.length;
32449 for ( var i = 0; i < setSpan; i++ ) {
32450 this.colYs[ shortColIndex + i ] = setHeight ;
32457 * @param {Number} colSpan - number of columns the element spans
32458 * @returns {Array} colGroup
32460 _getColGroup : function( colSpan )
32462 if ( colSpan < 2 ) {
32463 // if brick spans only one column, use all the column Ys
32468 // how many different places could this brick fit horizontally
32469 var groupCount = this.cols + 1 - colSpan;
32470 // for each group potential horizontal position
32471 for ( var i = 0; i < groupCount; i++ ) {
32472 // make an array of colY values for that one group
32473 var groupColYs = this.colYs.slice( i, i + colSpan );
32474 // and get the max value of the array
32475 colGroup[i] = Math.max.apply( Math, groupColYs );
32480 _manageStamp : function( stamp )
32482 var stampSize = stamp.getSize();
32483 var offset = stamp.getBox();
32484 // get the columns that this stamp affects
32485 var firstX = this.isOriginLeft ? offset.x : offset.right;
32486 var lastX = firstX + stampSize.width;
32487 var firstCol = Math.floor( firstX / this.columnWidth );
32488 firstCol = Math.max( 0, firstCol );
32490 var lastCol = Math.floor( lastX / this.columnWidth );
32491 // lastCol should not go over if multiple of columnWidth #425
32492 lastCol -= lastX % this.columnWidth ? 0 : 1;
32493 lastCol = Math.min( this.cols - 1, lastCol );
32495 // set colYs to bottom of the stamp
32496 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32499 for ( var i = firstCol; i <= lastCol; i++ ) {
32500 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32505 _getContainerSize : function()
32507 this.maxY = Math.max.apply( Math, this.colYs );
32512 if ( this.isFitWidth ) {
32513 size.width = this._getContainerFitWidth();
32519 _getContainerFitWidth : function()
32521 var unusedCols = 0;
32522 // count unused columns
32525 if ( this.colYs[i] !== 0 ) {
32530 // fit container to columns that have been used
32531 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32534 needsResizeLayout : function()
32536 var previousWidth = this.containerWidth;
32537 this.getContainerWidth();
32538 return previousWidth !== this.containerWidth;
32553 * @class Roo.bootstrap.MasonryBrick
32554 * @extends Roo.bootstrap.Component
32555 * Bootstrap MasonryBrick class
32558 * Create a new MasonryBrick
32559 * @param {Object} config The config object
32562 Roo.bootstrap.MasonryBrick = function(config){
32564 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32566 Roo.bootstrap.MasonryBrick.register(this);
32572 * When a MasonryBrick is clcik
32573 * @param {Roo.bootstrap.MasonryBrick} this
32574 * @param {Roo.EventObject} e
32580 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32583 * @cfg {String} title
32587 * @cfg {String} html
32591 * @cfg {String} bgimage
32595 * @cfg {String} videourl
32599 * @cfg {String} cls
32603 * @cfg {String} href
32607 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32612 * @cfg {String} placetitle (center|bottom)
32617 * @cfg {Boolean} isFitContainer defalut true
32619 isFitContainer : true,
32622 * @cfg {Boolean} preventDefault defalut false
32624 preventDefault : false,
32627 * @cfg {Boolean} inverse defalut false
32629 maskInverse : false,
32631 getAutoCreate : function()
32633 if(!this.isFitContainer){
32634 return this.getSplitAutoCreate();
32637 var cls = 'masonry-brick masonry-brick-full';
32639 if(this.href.length){
32640 cls += ' masonry-brick-link';
32643 if(this.bgimage.length){
32644 cls += ' masonry-brick-image';
32647 if(this.maskInverse){
32648 cls += ' mask-inverse';
32651 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32652 cls += ' enable-mask';
32656 cls += ' masonry-' + this.size + '-brick';
32659 if(this.placetitle.length){
32661 switch (this.placetitle) {
32663 cls += ' masonry-center-title';
32666 cls += ' masonry-bottom-title';
32673 if(!this.html.length && !this.bgimage.length){
32674 cls += ' masonry-center-title';
32677 if(!this.html.length && this.bgimage.length){
32678 cls += ' masonry-bottom-title';
32683 cls += ' ' + this.cls;
32687 tag: (this.href.length) ? 'a' : 'div',
32692 cls: 'masonry-brick-mask'
32696 cls: 'masonry-brick-paragraph',
32702 if(this.href.length){
32703 cfg.href = this.href;
32706 var cn = cfg.cn[1].cn;
32708 if(this.title.length){
32711 cls: 'masonry-brick-title',
32716 if(this.html.length){
32719 cls: 'masonry-brick-text',
32724 if (!this.title.length && !this.html.length) {
32725 cfg.cn[1].cls += ' hide';
32728 if(this.bgimage.length){
32731 cls: 'masonry-brick-image-view',
32736 if(this.videourl.length){
32737 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32738 // youtube support only?
32741 cls: 'masonry-brick-image-view',
32744 allowfullscreen : true
32752 getSplitAutoCreate : function()
32754 var cls = 'masonry-brick masonry-brick-split';
32756 if(this.href.length){
32757 cls += ' masonry-brick-link';
32760 if(this.bgimage.length){
32761 cls += ' masonry-brick-image';
32765 cls += ' masonry-' + this.size + '-brick';
32768 switch (this.placetitle) {
32770 cls += ' masonry-center-title';
32773 cls += ' masonry-bottom-title';
32776 if(!this.bgimage.length){
32777 cls += ' masonry-center-title';
32780 if(this.bgimage.length){
32781 cls += ' masonry-bottom-title';
32787 cls += ' ' + this.cls;
32791 tag: (this.href.length) ? 'a' : 'div',
32796 cls: 'masonry-brick-split-head',
32800 cls: 'masonry-brick-paragraph',
32807 cls: 'masonry-brick-split-body',
32813 if(this.href.length){
32814 cfg.href = this.href;
32817 if(this.title.length){
32818 cfg.cn[0].cn[0].cn.push({
32820 cls: 'masonry-brick-title',
32825 if(this.html.length){
32826 cfg.cn[1].cn.push({
32828 cls: 'masonry-brick-text',
32833 if(this.bgimage.length){
32834 cfg.cn[0].cn.push({
32836 cls: 'masonry-brick-image-view',
32841 if(this.videourl.length){
32842 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32843 // youtube support only?
32844 cfg.cn[0].cn.cn.push({
32846 cls: 'masonry-brick-image-view',
32849 allowfullscreen : true
32856 initEvents: function()
32858 switch (this.size) {
32891 this.el.on('touchstart', this.onTouchStart, this);
32892 this.el.on('touchmove', this.onTouchMove, this);
32893 this.el.on('touchend', this.onTouchEnd, this);
32894 this.el.on('contextmenu', this.onContextMenu, this);
32896 this.el.on('mouseenter' ,this.enter, this);
32897 this.el.on('mouseleave', this.leave, this);
32898 this.el.on('click', this.onClick, this);
32901 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32902 this.parent().bricks.push(this);
32907 onClick: function(e, el)
32909 var time = this.endTimer - this.startTimer;
32910 // Roo.log(e.preventDefault());
32913 e.preventDefault();
32918 if(!this.preventDefault){
32922 e.preventDefault();
32924 if (this.activeClass != '') {
32925 this.selectBrick();
32928 this.fireEvent('click', this, e);
32931 enter: function(e, el)
32933 e.preventDefault();
32935 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32939 if(this.bgimage.length && this.html.length){
32940 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32944 leave: function(e, el)
32946 e.preventDefault();
32948 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32952 if(this.bgimage.length && this.html.length){
32953 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32957 onTouchStart: function(e, el)
32959 // e.preventDefault();
32961 this.touchmoved = false;
32963 if(!this.isFitContainer){
32967 if(!this.bgimage.length || !this.html.length){
32971 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32973 this.timer = new Date().getTime();
32977 onTouchMove: function(e, el)
32979 this.touchmoved = true;
32982 onContextMenu : function(e,el)
32984 e.preventDefault();
32985 e.stopPropagation();
32989 onTouchEnd: function(e, el)
32991 // e.preventDefault();
32993 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33000 if(!this.bgimage.length || !this.html.length){
33002 if(this.href.length){
33003 window.location.href = this.href;
33009 if(!this.isFitContainer){
33013 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33015 window.location.href = this.href;
33018 //selection on single brick only
33019 selectBrick : function() {
33021 if (!this.parentId) {
33025 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33026 var index = m.selectedBrick.indexOf(this.id);
33029 m.selectedBrick.splice(index,1);
33030 this.el.removeClass(this.activeClass);
33034 for(var i = 0; i < m.selectedBrick.length; i++) {
33035 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33036 b.el.removeClass(b.activeClass);
33039 m.selectedBrick = [];
33041 m.selectedBrick.push(this.id);
33042 this.el.addClass(this.activeClass);
33046 isSelected : function(){
33047 return this.el.hasClass(this.activeClass);
33052 Roo.apply(Roo.bootstrap.MasonryBrick, {
33055 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33057 * register a Masonry Brick
33058 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33061 register : function(brick)
33063 //this.groups[brick.id] = brick;
33064 this.groups.add(brick.id, brick);
33067 * fetch a masonry brick based on the masonry brick ID
33068 * @param {string} the masonry brick to add
33069 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33072 get: function(brick_id)
33074 // if (typeof(this.groups[brick_id]) == 'undefined') {
33077 // return this.groups[brick_id] ;
33079 if(this.groups.key(brick_id)) {
33080 return this.groups.key(brick_id);
33098 * @class Roo.bootstrap.Brick
33099 * @extends Roo.bootstrap.Component
33100 * Bootstrap Brick class
33103 * Create a new Brick
33104 * @param {Object} config The config object
33107 Roo.bootstrap.Brick = function(config){
33108 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33114 * When a Brick is click
33115 * @param {Roo.bootstrap.Brick} this
33116 * @param {Roo.EventObject} e
33122 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33125 * @cfg {String} title
33129 * @cfg {String} html
33133 * @cfg {String} bgimage
33137 * @cfg {String} cls
33141 * @cfg {String} href
33145 * @cfg {String} video
33149 * @cfg {Boolean} square
33153 getAutoCreate : function()
33155 var cls = 'roo-brick';
33157 if(this.href.length){
33158 cls += ' roo-brick-link';
33161 if(this.bgimage.length){
33162 cls += ' roo-brick-image';
33165 if(!this.html.length && !this.bgimage.length){
33166 cls += ' roo-brick-center-title';
33169 if(!this.html.length && this.bgimage.length){
33170 cls += ' roo-brick-bottom-title';
33174 cls += ' ' + this.cls;
33178 tag: (this.href.length) ? 'a' : 'div',
33183 cls: 'roo-brick-paragraph',
33189 if(this.href.length){
33190 cfg.href = this.href;
33193 var cn = cfg.cn[0].cn;
33195 if(this.title.length){
33198 cls: 'roo-brick-title',
33203 if(this.html.length){
33206 cls: 'roo-brick-text',
33213 if(this.bgimage.length){
33216 cls: 'roo-brick-image-view',
33224 initEvents: function()
33226 if(this.title.length || this.html.length){
33227 this.el.on('mouseenter' ,this.enter, this);
33228 this.el.on('mouseleave', this.leave, this);
33231 Roo.EventManager.onWindowResize(this.resize, this);
33233 if(this.bgimage.length){
33234 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33235 this.imageEl.on('load', this.onImageLoad, this);
33242 onImageLoad : function()
33247 resize : function()
33249 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33251 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33253 if(this.bgimage.length){
33254 var image = this.el.select('.roo-brick-image-view', true).first();
33256 image.setWidth(paragraph.getWidth());
33259 image.setHeight(paragraph.getWidth());
33262 this.el.setHeight(image.getHeight());
33263 paragraph.setHeight(image.getHeight());
33269 enter: function(e, el)
33271 e.preventDefault();
33273 if(this.bgimage.length){
33274 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33275 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33279 leave: function(e, el)
33281 e.preventDefault();
33283 if(this.bgimage.length){
33284 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33285 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33300 * @class Roo.bootstrap.NumberField
33301 * @extends Roo.bootstrap.Input
33302 * Bootstrap NumberField class
33308 * Create a new NumberField
33309 * @param {Object} config The config object
33312 Roo.bootstrap.NumberField = function(config){
33313 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33316 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33319 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33321 allowDecimals : true,
33323 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33325 decimalSeparator : ".",
33327 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33329 decimalPrecision : 2,
33331 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33333 allowNegative : true,
33336 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33340 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33342 minValue : Number.NEGATIVE_INFINITY,
33344 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33346 maxValue : Number.MAX_VALUE,
33348 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33350 minText : "The minimum value for this field is {0}",
33352 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33354 maxText : "The maximum value for this field is {0}",
33356 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33357 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33359 nanText : "{0} is not a valid number",
33361 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33363 thousandsDelimiter : false,
33365 * @cfg {String} valueAlign alignment of value
33367 valueAlign : "left",
33369 getAutoCreate : function()
33371 var hiddenInput = {
33375 cls: 'hidden-number-input'
33379 hiddenInput.name = this.name;
33384 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33386 this.name = hiddenInput.name;
33388 if(cfg.cn.length > 0) {
33389 cfg.cn.push(hiddenInput);
33396 initEvents : function()
33398 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33400 var allowed = "0123456789";
33402 if(this.allowDecimals){
33403 allowed += this.decimalSeparator;
33406 if(this.allowNegative){
33410 if(this.thousandsDelimiter) {
33414 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33416 var keyPress = function(e){
33418 var k = e.getKey();
33420 var c = e.getCharCode();
33423 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33424 allowed.indexOf(String.fromCharCode(c)) === -1
33430 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33434 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33439 this.el.on("keypress", keyPress, this);
33442 validateValue : function(value)
33445 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33449 var num = this.parseValue(value);
33452 this.markInvalid(String.format(this.nanText, value));
33456 if(num < this.minValue){
33457 this.markInvalid(String.format(this.minText, this.minValue));
33461 if(num > this.maxValue){
33462 this.markInvalid(String.format(this.maxText, this.maxValue));
33469 getValue : function()
33471 var v = this.hiddenEl().getValue();
33473 return this.fixPrecision(this.parseValue(v));
33476 parseValue : function(value)
33478 if(this.thousandsDelimiter) {
33480 r = new RegExp(",", "g");
33481 value = value.replace(r, "");
33484 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33485 return isNaN(value) ? '' : value;
33488 fixPrecision : function(value)
33490 if(this.thousandsDelimiter) {
33492 r = new RegExp(",", "g");
33493 value = value.replace(r, "");
33496 var nan = isNaN(value);
33498 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33499 return nan ? '' : value;
33501 return parseFloat(value).toFixed(this.decimalPrecision);
33504 setValue : function(v)
33506 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33512 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33514 this.inputEl().dom.value = (v == '') ? '' :
33515 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33517 if(!this.allowZero && v === '0') {
33518 this.hiddenEl().dom.value = '';
33519 this.inputEl().dom.value = '';
33526 decimalPrecisionFcn : function(v)
33528 return Math.floor(v);
33531 beforeBlur : function()
33533 var v = this.parseValue(this.getRawValue());
33535 if(v || v === 0 || v === ''){
33540 hiddenEl : function()
33542 return this.el.select('input.hidden-number-input',true).first();
33554 * @class Roo.bootstrap.DocumentSlider
33555 * @extends Roo.bootstrap.Component
33556 * Bootstrap DocumentSlider class
33559 * Create a new DocumentViewer
33560 * @param {Object} config The config object
33563 Roo.bootstrap.DocumentSlider = function(config){
33564 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33571 * Fire after initEvent
33572 * @param {Roo.bootstrap.DocumentSlider} this
33577 * Fire after update
33578 * @param {Roo.bootstrap.DocumentSlider} this
33584 * @param {Roo.bootstrap.DocumentSlider} this
33590 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33596 getAutoCreate : function()
33600 cls : 'roo-document-slider',
33604 cls : 'roo-document-slider-header',
33608 cls : 'roo-document-slider-header-title'
33614 cls : 'roo-document-slider-body',
33618 cls : 'roo-document-slider-prev',
33622 cls : 'fa fa-chevron-left'
33628 cls : 'roo-document-slider-thumb',
33632 cls : 'roo-document-slider-image'
33638 cls : 'roo-document-slider-next',
33642 cls : 'fa fa-chevron-right'
33654 initEvents : function()
33656 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33657 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33659 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33660 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33662 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33663 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33665 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33666 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33668 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33669 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33671 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33672 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33674 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33675 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33677 this.thumbEl.on('click', this.onClick, this);
33679 this.prevIndicator.on('click', this.prev, this);
33681 this.nextIndicator.on('click', this.next, this);
33685 initial : function()
33687 if(this.files.length){
33688 this.indicator = 1;
33692 this.fireEvent('initial', this);
33695 update : function()
33697 this.imageEl.attr('src', this.files[this.indicator - 1]);
33699 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33701 this.prevIndicator.show();
33703 if(this.indicator == 1){
33704 this.prevIndicator.hide();
33707 this.nextIndicator.show();
33709 if(this.indicator == this.files.length){
33710 this.nextIndicator.hide();
33713 this.thumbEl.scrollTo('top');
33715 this.fireEvent('update', this);
33718 onClick : function(e)
33720 e.preventDefault();
33722 this.fireEvent('click', this);
33727 e.preventDefault();
33729 this.indicator = Math.max(1, this.indicator - 1);
33736 e.preventDefault();
33738 this.indicator = Math.min(this.files.length, this.indicator + 1);
33752 * @class Roo.bootstrap.RadioSet
33753 * @extends Roo.bootstrap.Input
33754 * Bootstrap RadioSet class
33755 * @cfg {String} indicatorpos (left|right) default left
33756 * @cfg {Boolean} inline (true|false) inline the element (default true)
33757 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33759 * Create a new RadioSet
33760 * @param {Object} config The config object
33763 Roo.bootstrap.RadioSet = function(config){
33765 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33769 Roo.bootstrap.RadioSet.register(this);
33774 * Fires when the element is checked or unchecked.
33775 * @param {Roo.bootstrap.RadioSet} this This radio
33776 * @param {Roo.bootstrap.Radio} item The checked item
33781 * Fires when the element is click.
33782 * @param {Roo.bootstrap.RadioSet} this This radio set
33783 * @param {Roo.bootstrap.Radio} item The checked item
33784 * @param {Roo.EventObject} e The event object
33791 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33799 indicatorpos : 'left',
33801 getAutoCreate : function()
33805 cls : 'roo-radio-set-label',
33809 html : this.fieldLabel
33814 if(this.indicatorpos == 'left'){
33817 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33818 tooltip : 'This field is required'
33823 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33824 tooltip : 'This field is required'
33830 cls : 'roo-radio-set-items'
33833 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33835 if (align === 'left' && this.fieldLabel.length) {
33838 cls : "roo-radio-set-right",
33844 if(this.labelWidth > 12){
33845 label.style = "width: " + this.labelWidth + 'px';
33848 if(this.labelWidth < 13 && this.labelmd == 0){
33849 this.labelmd = this.labelWidth;
33852 if(this.labellg > 0){
33853 label.cls += ' col-lg-' + this.labellg;
33854 items.cls += ' col-lg-' + (12 - this.labellg);
33857 if(this.labelmd > 0){
33858 label.cls += ' col-md-' + this.labelmd;
33859 items.cls += ' col-md-' + (12 - this.labelmd);
33862 if(this.labelsm > 0){
33863 label.cls += ' col-sm-' + this.labelsm;
33864 items.cls += ' col-sm-' + (12 - this.labelsm);
33867 if(this.labelxs > 0){
33868 label.cls += ' col-xs-' + this.labelxs;
33869 items.cls += ' col-xs-' + (12 - this.labelxs);
33875 cls : 'roo-radio-set',
33879 cls : 'roo-radio-set-input',
33882 value : this.value ? this.value : ''
33889 if(this.weight.length){
33890 cfg.cls += ' roo-radio-' + this.weight;
33894 cfg.cls += ' roo-radio-set-inline';
33898 ['xs','sm','md','lg'].map(function(size){
33899 if (settings[size]) {
33900 cfg.cls += ' col-' + size + '-' + settings[size];
33908 initEvents : function()
33910 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33911 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33913 if(!this.fieldLabel.length){
33914 this.labelEl.hide();
33917 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33918 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33920 this.indicator = this.indicatorEl();
33922 if(this.indicator){
33923 this.indicator.addClass('invisible');
33926 this.originalValue = this.getValue();
33930 inputEl: function ()
33932 return this.el.select('.roo-radio-set-input', true).first();
33935 getChildContainer : function()
33937 return this.itemsEl;
33940 register : function(item)
33942 this.radioes.push(item);
33946 validate : function()
33948 if(this.getVisibilityEl().hasClass('hidden')){
33954 Roo.each(this.radioes, function(i){
33963 if(this.allowBlank) {
33967 if(this.disabled || valid){
33972 this.markInvalid();
33977 markValid : function()
33979 if(this.labelEl.isVisible(true)){
33980 this.indicatorEl().removeClass('visible');
33981 this.indicatorEl().addClass('invisible');
33984 this.el.removeClass([this.invalidClass, this.validClass]);
33985 this.el.addClass(this.validClass);
33987 this.fireEvent('valid', this);
33990 markInvalid : function(msg)
33992 if(this.allowBlank || this.disabled){
33996 if(this.labelEl.isVisible(true)){
33997 this.indicatorEl().removeClass('invisible');
33998 this.indicatorEl().addClass('visible');
34001 this.el.removeClass([this.invalidClass, this.validClass]);
34002 this.el.addClass(this.invalidClass);
34004 this.fireEvent('invalid', this, msg);
34008 setValue : function(v, suppressEvent)
34010 if(this.value === v){
34017 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34020 Roo.each(this.radioes, function(i){
34022 i.el.removeClass('checked');
34025 Roo.each(this.radioes, function(i){
34027 if(i.value === v || i.value.toString() === v.toString()){
34029 i.el.addClass('checked');
34031 if(suppressEvent !== true){
34032 this.fireEvent('check', this, i);
34043 clearInvalid : function(){
34045 if(!this.el || this.preventMark){
34049 this.el.removeClass([this.invalidClass]);
34051 this.fireEvent('valid', this);
34056 Roo.apply(Roo.bootstrap.RadioSet, {
34060 register : function(set)
34062 this.groups[set.name] = set;
34065 get: function(name)
34067 if (typeof(this.groups[name]) == 'undefined') {
34071 return this.groups[name] ;
34077 * Ext JS Library 1.1.1
34078 * Copyright(c) 2006-2007, Ext JS, LLC.
34080 * Originally Released Under LGPL - original licence link has changed is not relivant.
34083 * <script type="text/javascript">
34088 * @class Roo.bootstrap.SplitBar
34089 * @extends Roo.util.Observable
34090 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34094 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34095 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34096 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34097 split.minSize = 100;
34098 split.maxSize = 600;
34099 split.animate = true;
34100 split.on('moved', splitterMoved);
34103 * Create a new SplitBar
34104 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34105 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34106 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34107 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34108 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34109 position of the SplitBar).
34111 Roo.bootstrap.SplitBar = function(cfg){
34116 // dragElement : elm
34117 // resizingElement: el,
34119 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34120 // placement : Roo.bootstrap.SplitBar.LEFT ,
34121 // existingProxy ???
34124 this.el = Roo.get(cfg.dragElement, true);
34125 this.el.dom.unselectable = "on";
34127 this.resizingEl = Roo.get(cfg.resizingElement, true);
34131 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34132 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34135 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34138 * The minimum size of the resizing element. (Defaults to 0)
34144 * The maximum size of the resizing element. (Defaults to 2000)
34147 this.maxSize = 2000;
34150 * Whether to animate the transition to the new size
34153 this.animate = false;
34156 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34159 this.useShim = false;
34164 if(!cfg.existingProxy){
34166 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34168 this.proxy = Roo.get(cfg.existingProxy).dom;
34171 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34174 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34177 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34180 this.dragSpecs = {};
34183 * @private The adapter to use to positon and resize elements
34185 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34186 this.adapter.init(this);
34188 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34190 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34191 this.el.addClass("roo-splitbar-h");
34194 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34195 this.el.addClass("roo-splitbar-v");
34201 * Fires when the splitter is moved (alias for {@link #event-moved})
34202 * @param {Roo.bootstrap.SplitBar} this
34203 * @param {Number} newSize the new width or height
34208 * Fires when the splitter is moved
34209 * @param {Roo.bootstrap.SplitBar} this
34210 * @param {Number} newSize the new width or height
34214 * @event beforeresize
34215 * Fires before the splitter is dragged
34216 * @param {Roo.bootstrap.SplitBar} this
34218 "beforeresize" : true,
34220 "beforeapply" : true
34223 Roo.util.Observable.call(this);
34226 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34227 onStartProxyDrag : function(x, y){
34228 this.fireEvent("beforeresize", this);
34230 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34232 o.enableDisplayMode("block");
34233 // all splitbars share the same overlay
34234 Roo.bootstrap.SplitBar.prototype.overlay = o;
34236 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34237 this.overlay.show();
34238 Roo.get(this.proxy).setDisplayed("block");
34239 var size = this.adapter.getElementSize(this);
34240 this.activeMinSize = this.getMinimumSize();;
34241 this.activeMaxSize = this.getMaximumSize();;
34242 var c1 = size - this.activeMinSize;
34243 var c2 = Math.max(this.activeMaxSize - size, 0);
34244 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34245 this.dd.resetConstraints();
34246 this.dd.setXConstraint(
34247 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34248 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34250 this.dd.setYConstraint(0, 0);
34252 this.dd.resetConstraints();
34253 this.dd.setXConstraint(0, 0);
34254 this.dd.setYConstraint(
34255 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34256 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34259 this.dragSpecs.startSize = size;
34260 this.dragSpecs.startPoint = [x, y];
34261 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34265 * @private Called after the drag operation by the DDProxy
34267 onEndProxyDrag : function(e){
34268 Roo.get(this.proxy).setDisplayed(false);
34269 var endPoint = Roo.lib.Event.getXY(e);
34271 this.overlay.hide();
34274 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34275 newSize = this.dragSpecs.startSize +
34276 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34277 endPoint[0] - this.dragSpecs.startPoint[0] :
34278 this.dragSpecs.startPoint[0] - endPoint[0]
34281 newSize = this.dragSpecs.startSize +
34282 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34283 endPoint[1] - this.dragSpecs.startPoint[1] :
34284 this.dragSpecs.startPoint[1] - endPoint[1]
34287 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34288 if(newSize != this.dragSpecs.startSize){
34289 if(this.fireEvent('beforeapply', this, newSize) !== false){
34290 this.adapter.setElementSize(this, newSize);
34291 this.fireEvent("moved", this, newSize);
34292 this.fireEvent("resize", this, newSize);
34298 * Get the adapter this SplitBar uses
34299 * @return The adapter object
34301 getAdapter : function(){
34302 return this.adapter;
34306 * Set the adapter this SplitBar uses
34307 * @param {Object} adapter A SplitBar adapter object
34309 setAdapter : function(adapter){
34310 this.adapter = adapter;
34311 this.adapter.init(this);
34315 * Gets the minimum size for the resizing element
34316 * @return {Number} The minimum size
34318 getMinimumSize : function(){
34319 return this.minSize;
34323 * Sets the minimum size for the resizing element
34324 * @param {Number} minSize The minimum size
34326 setMinimumSize : function(minSize){
34327 this.minSize = minSize;
34331 * Gets the maximum size for the resizing element
34332 * @return {Number} The maximum size
34334 getMaximumSize : function(){
34335 return this.maxSize;
34339 * Sets the maximum size for the resizing element
34340 * @param {Number} maxSize The maximum size
34342 setMaximumSize : function(maxSize){
34343 this.maxSize = maxSize;
34347 * Sets the initialize size for the resizing element
34348 * @param {Number} size The initial size
34350 setCurrentSize : function(size){
34351 var oldAnimate = this.animate;
34352 this.animate = false;
34353 this.adapter.setElementSize(this, size);
34354 this.animate = oldAnimate;
34358 * Destroy this splitbar.
34359 * @param {Boolean} removeEl True to remove the element
34361 destroy : function(removeEl){
34363 this.shim.remove();
34366 this.proxy.parentNode.removeChild(this.proxy);
34374 * @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.
34376 Roo.bootstrap.SplitBar.createProxy = function(dir){
34377 var proxy = new Roo.Element(document.createElement("div"));
34378 proxy.unselectable();
34379 var cls = 'roo-splitbar-proxy';
34380 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34381 document.body.appendChild(proxy.dom);
34386 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34387 * Default Adapter. It assumes the splitter and resizing element are not positioned
34388 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34390 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34393 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34394 // do nothing for now
34395 init : function(s){
34399 * Called before drag operations to get the current size of the resizing element.
34400 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34402 getElementSize : function(s){
34403 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34404 return s.resizingEl.getWidth();
34406 return s.resizingEl.getHeight();
34411 * Called after drag operations to set the size of the resizing element.
34412 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34413 * @param {Number} newSize The new size to set
34414 * @param {Function} onComplete A function to be invoked when resizing is complete
34416 setElementSize : function(s, newSize, onComplete){
34417 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34419 s.resizingEl.setWidth(newSize);
34421 onComplete(s, newSize);
34424 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34429 s.resizingEl.setHeight(newSize);
34431 onComplete(s, newSize);
34434 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34441 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34442 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34443 * Adapter that moves the splitter element to align with the resized sizing element.
34444 * Used with an absolute positioned SplitBar.
34445 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34446 * document.body, make sure you assign an id to the body element.
34448 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34449 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34450 this.container = Roo.get(container);
34453 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34454 init : function(s){
34455 this.basic.init(s);
34458 getElementSize : function(s){
34459 return this.basic.getElementSize(s);
34462 setElementSize : function(s, newSize, onComplete){
34463 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34466 moveSplitter : function(s){
34467 var yes = Roo.bootstrap.SplitBar;
34468 switch(s.placement){
34470 s.el.setX(s.resizingEl.getRight());
34473 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34476 s.el.setY(s.resizingEl.getBottom());
34479 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34486 * Orientation constant - Create a vertical SplitBar
34490 Roo.bootstrap.SplitBar.VERTICAL = 1;
34493 * Orientation constant - Create a horizontal SplitBar
34497 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34500 * Placement constant - The resizing element is to the left of the splitter element
34504 Roo.bootstrap.SplitBar.LEFT = 1;
34507 * Placement constant - The resizing element is to the right of the splitter element
34511 Roo.bootstrap.SplitBar.RIGHT = 2;
34514 * Placement constant - The resizing element is positioned above the splitter element
34518 Roo.bootstrap.SplitBar.TOP = 3;
34521 * Placement constant - The resizing element is positioned under splitter element
34525 Roo.bootstrap.SplitBar.BOTTOM = 4;
34526 Roo.namespace("Roo.bootstrap.layout");/*
34528 * Ext JS Library 1.1.1
34529 * Copyright(c) 2006-2007, Ext JS, LLC.
34531 * Originally Released Under LGPL - original licence link has changed is not relivant.
34534 * <script type="text/javascript">
34538 * @class Roo.bootstrap.layout.Manager
34539 * @extends Roo.bootstrap.Component
34540 * Base class for layout managers.
34542 Roo.bootstrap.layout.Manager = function(config)
34544 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34550 /** false to disable window resize monitoring @type Boolean */
34551 this.monitorWindowResize = true;
34556 * Fires when a layout is performed.
34557 * @param {Roo.LayoutManager} this
34561 * @event regionresized
34562 * Fires when the user resizes a region.
34563 * @param {Roo.LayoutRegion} region The resized region
34564 * @param {Number} newSize The new size (width for east/west, height for north/south)
34566 "regionresized" : true,
34568 * @event regioncollapsed
34569 * Fires when a region is collapsed.
34570 * @param {Roo.LayoutRegion} region The collapsed region
34572 "regioncollapsed" : true,
34574 * @event regionexpanded
34575 * Fires when a region is expanded.
34576 * @param {Roo.LayoutRegion} region The expanded region
34578 "regionexpanded" : true
34580 this.updating = false;
34583 this.el = Roo.get(config.el);
34589 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34594 monitorWindowResize : true,
34600 onRender : function(ct, position)
34603 this.el = Roo.get(ct);
34606 //this.fireEvent('render',this);
34610 initEvents: function()
34614 // ie scrollbar fix
34615 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34616 document.body.scroll = "no";
34617 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34618 this.el.position('relative');
34620 this.id = this.el.id;
34621 this.el.addClass("roo-layout-container");
34622 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34623 if(this.el.dom != document.body ) {
34624 this.el.on('resize', this.layout,this);
34625 this.el.on('show', this.layout,this);
34631 * Returns true if this layout is currently being updated
34632 * @return {Boolean}
34634 isUpdating : function(){
34635 return this.updating;
34639 * Suspend the LayoutManager from doing auto-layouts while
34640 * making multiple add or remove calls
34642 beginUpdate : function(){
34643 this.updating = true;
34647 * Restore auto-layouts and optionally disable the manager from performing a layout
34648 * @param {Boolean} noLayout true to disable a layout update
34650 endUpdate : function(noLayout){
34651 this.updating = false;
34657 layout: function(){
34661 onRegionResized : function(region, newSize){
34662 this.fireEvent("regionresized", region, newSize);
34666 onRegionCollapsed : function(region){
34667 this.fireEvent("regioncollapsed", region);
34670 onRegionExpanded : function(region){
34671 this.fireEvent("regionexpanded", region);
34675 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34676 * performs box-model adjustments.
34677 * @return {Object} The size as an object {width: (the width), height: (the height)}
34679 getViewSize : function()
34682 if(this.el.dom != document.body){
34683 size = this.el.getSize();
34685 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34687 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34688 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34693 * Returns the Element this layout is bound to.
34694 * @return {Roo.Element}
34696 getEl : function(){
34701 * Returns the specified region.
34702 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34703 * @return {Roo.LayoutRegion}
34705 getRegion : function(target){
34706 return this.regions[target.toLowerCase()];
34709 onWindowResize : function(){
34710 if(this.monitorWindowResize){
34717 * Ext JS Library 1.1.1
34718 * Copyright(c) 2006-2007, Ext JS, LLC.
34720 * Originally Released Under LGPL - original licence link has changed is not relivant.
34723 * <script type="text/javascript">
34726 * @class Roo.bootstrap.layout.Border
34727 * @extends Roo.bootstrap.layout.Manager
34728 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34729 * please see: examples/bootstrap/nested.html<br><br>
34731 <b>The container the layout is rendered into can be either the body element or any other element.
34732 If it is not the body element, the container needs to either be an absolute positioned element,
34733 or you will need to add "position:relative" to the css of the container. You will also need to specify
34734 the container size if it is not the body element.</b>
34737 * Create a new Border
34738 * @param {Object} config Configuration options
34740 Roo.bootstrap.layout.Border = function(config){
34741 config = config || {};
34742 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34746 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34747 if(config[region]){
34748 config[region].region = region;
34749 this.addRegion(config[region]);
34755 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34757 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34759 * Creates and adds a new region if it doesn't already exist.
34760 * @param {String} target The target region key (north, south, east, west or center).
34761 * @param {Object} config The regions config object
34762 * @return {BorderLayoutRegion} The new region
34764 addRegion : function(config)
34766 if(!this.regions[config.region]){
34767 var r = this.factory(config);
34768 this.bindRegion(r);
34770 return this.regions[config.region];
34774 bindRegion : function(r){
34775 this.regions[r.config.region] = r;
34777 r.on("visibilitychange", this.layout, this);
34778 r.on("paneladded", this.layout, this);
34779 r.on("panelremoved", this.layout, this);
34780 r.on("invalidated", this.layout, this);
34781 r.on("resized", this.onRegionResized, this);
34782 r.on("collapsed", this.onRegionCollapsed, this);
34783 r.on("expanded", this.onRegionExpanded, this);
34787 * Performs a layout update.
34789 layout : function()
34791 if(this.updating) {
34795 // render all the rebions if they have not been done alreayd?
34796 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34797 if(this.regions[region] && !this.regions[region].bodyEl){
34798 this.regions[region].onRender(this.el)
34802 var size = this.getViewSize();
34803 var w = size.width;
34804 var h = size.height;
34809 //var x = 0, y = 0;
34811 var rs = this.regions;
34812 var north = rs["north"];
34813 var south = rs["south"];
34814 var west = rs["west"];
34815 var east = rs["east"];
34816 var center = rs["center"];
34817 //if(this.hideOnLayout){ // not supported anymore
34818 //c.el.setStyle("display", "none");
34820 if(north && north.isVisible()){
34821 var b = north.getBox();
34822 var m = north.getMargins();
34823 b.width = w - (m.left+m.right);
34826 centerY = b.height + b.y + m.bottom;
34827 centerH -= centerY;
34828 north.updateBox(this.safeBox(b));
34830 if(south && south.isVisible()){
34831 var b = south.getBox();
34832 var m = south.getMargins();
34833 b.width = w - (m.left+m.right);
34835 var totalHeight = (b.height + m.top + m.bottom);
34836 b.y = h - totalHeight + m.top;
34837 centerH -= totalHeight;
34838 south.updateBox(this.safeBox(b));
34840 if(west && west.isVisible()){
34841 var b = west.getBox();
34842 var m = west.getMargins();
34843 b.height = centerH - (m.top+m.bottom);
34845 b.y = centerY + m.top;
34846 var totalWidth = (b.width + m.left + m.right);
34847 centerX += totalWidth;
34848 centerW -= totalWidth;
34849 west.updateBox(this.safeBox(b));
34851 if(east && east.isVisible()){
34852 var b = east.getBox();
34853 var m = east.getMargins();
34854 b.height = centerH - (m.top+m.bottom);
34855 var totalWidth = (b.width + m.left + m.right);
34856 b.x = w - totalWidth + m.left;
34857 b.y = centerY + m.top;
34858 centerW -= totalWidth;
34859 east.updateBox(this.safeBox(b));
34862 var m = center.getMargins();
34864 x: centerX + m.left,
34865 y: centerY + m.top,
34866 width: centerW - (m.left+m.right),
34867 height: centerH - (m.top+m.bottom)
34869 //if(this.hideOnLayout){
34870 //center.el.setStyle("display", "block");
34872 center.updateBox(this.safeBox(centerBox));
34875 this.fireEvent("layout", this);
34879 safeBox : function(box){
34880 box.width = Math.max(0, box.width);
34881 box.height = Math.max(0, box.height);
34886 * Adds a ContentPanel (or subclass) to this layout.
34887 * @param {String} target The target region key (north, south, east, west or center).
34888 * @param {Roo.ContentPanel} panel The panel to add
34889 * @return {Roo.ContentPanel} The added panel
34891 add : function(target, panel){
34893 target = target.toLowerCase();
34894 return this.regions[target].add(panel);
34898 * Remove a ContentPanel (or subclass) to this layout.
34899 * @param {String} target The target region key (north, south, east, west or center).
34900 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34901 * @return {Roo.ContentPanel} The removed panel
34903 remove : function(target, panel){
34904 target = target.toLowerCase();
34905 return this.regions[target].remove(panel);
34909 * Searches all regions for a panel with the specified id
34910 * @param {String} panelId
34911 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34913 findPanel : function(panelId){
34914 var rs = this.regions;
34915 for(var target in rs){
34916 if(typeof rs[target] != "function"){
34917 var p = rs[target].getPanel(panelId);
34927 * Searches all regions for a panel with the specified id and activates (shows) it.
34928 * @param {String/ContentPanel} panelId The panels id or the panel itself
34929 * @return {Roo.ContentPanel} The shown panel or null
34931 showPanel : function(panelId) {
34932 var rs = this.regions;
34933 for(var target in rs){
34934 var r = rs[target];
34935 if(typeof r != "function"){
34936 if(r.hasPanel(panelId)){
34937 return r.showPanel(panelId);
34945 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34946 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34949 restoreState : function(provider){
34951 provider = Roo.state.Manager;
34953 var sm = new Roo.LayoutStateManager();
34954 sm.init(this, provider);
34960 * Adds a xtype elements to the layout.
34964 xtype : 'ContentPanel',
34971 xtype : 'NestedLayoutPanel',
34977 items : [ ... list of content panels or nested layout panels.. ]
34981 * @param {Object} cfg Xtype definition of item to add.
34983 addxtype : function(cfg)
34985 // basically accepts a pannel...
34986 // can accept a layout region..!?!?
34987 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34990 // theory? children can only be panels??
34992 //if (!cfg.xtype.match(/Panel$/)) {
34997 if (typeof(cfg.region) == 'undefined') {
34998 Roo.log("Failed to add Panel, region was not set");
35002 var region = cfg.region;
35008 xitems = cfg.items;
35015 case 'Content': // ContentPanel (el, cfg)
35016 case 'Scroll': // ContentPanel (el, cfg)
35018 cfg.autoCreate = true;
35019 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35021 // var el = this.el.createChild();
35022 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35025 this.add(region, ret);
35029 case 'TreePanel': // our new panel!
35030 cfg.el = this.el.createChild();
35031 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35032 this.add(region, ret);
35037 // create a new Layout (which is a Border Layout...
35039 var clayout = cfg.layout;
35040 clayout.el = this.el.createChild();
35041 clayout.items = clayout.items || [];
35045 // replace this exitems with the clayout ones..
35046 xitems = clayout.items;
35048 // force background off if it's in center...
35049 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35050 cfg.background = false;
35052 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35055 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35056 //console.log('adding nested layout panel ' + cfg.toSource());
35057 this.add(region, ret);
35058 nb = {}; /// find first...
35063 // needs grid and region
35065 //var el = this.getRegion(region).el.createChild();
35067 *var el = this.el.createChild();
35068 // create the grid first...
35069 cfg.grid.container = el;
35070 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35073 if (region == 'center' && this.active ) {
35074 cfg.background = false;
35077 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35079 this.add(region, ret);
35081 if (cfg.background) {
35082 // render grid on panel activation (if panel background)
35083 ret.on('activate', function(gp) {
35084 if (!gp.grid.rendered) {
35085 // gp.grid.render(el);
35089 // cfg.grid.render(el);
35095 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35096 // it was the old xcomponent building that caused this before.
35097 // espeically if border is the top element in the tree.
35107 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35109 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35110 this.add(region, ret);
35114 throw "Can not add '" + cfg.xtype + "' to Border";
35120 this.beginUpdate();
35124 Roo.each(xitems, function(i) {
35125 region = nb && i.region ? i.region : false;
35127 var add = ret.addxtype(i);
35130 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35131 if (!i.background) {
35132 abn[region] = nb[region] ;
35139 // make the last non-background panel active..
35140 //if (nb) { Roo.log(abn); }
35143 for(var r in abn) {
35144 region = this.getRegion(r);
35146 // tried using nb[r], but it does not work..
35148 region.showPanel(abn[r]);
35159 factory : function(cfg)
35162 var validRegions = Roo.bootstrap.layout.Border.regions;
35164 var target = cfg.region;
35167 var r = Roo.bootstrap.layout;
35171 return new r.North(cfg);
35173 return new r.South(cfg);
35175 return new r.East(cfg);
35177 return new r.West(cfg);
35179 return new r.Center(cfg);
35181 throw 'Layout region "'+target+'" not supported.';
35188 * Ext JS Library 1.1.1
35189 * Copyright(c) 2006-2007, Ext JS, LLC.
35191 * Originally Released Under LGPL - original licence link has changed is not relivant.
35194 * <script type="text/javascript">
35198 * @class Roo.bootstrap.layout.Basic
35199 * @extends Roo.util.Observable
35200 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35201 * and does not have a titlebar, tabs or any other features. All it does is size and position
35202 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35203 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35204 * @cfg {string} region the region that it inhabits..
35205 * @cfg {bool} skipConfig skip config?
35209 Roo.bootstrap.layout.Basic = function(config){
35211 this.mgr = config.mgr;
35213 this.position = config.region;
35215 var skipConfig = config.skipConfig;
35219 * @scope Roo.BasicLayoutRegion
35223 * @event beforeremove
35224 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35225 * @param {Roo.LayoutRegion} this
35226 * @param {Roo.ContentPanel} panel The panel
35227 * @param {Object} e The cancel event object
35229 "beforeremove" : true,
35231 * @event invalidated
35232 * Fires when the layout for this region is changed.
35233 * @param {Roo.LayoutRegion} this
35235 "invalidated" : true,
35237 * @event visibilitychange
35238 * Fires when this region is shown or hidden
35239 * @param {Roo.LayoutRegion} this
35240 * @param {Boolean} visibility true or false
35242 "visibilitychange" : true,
35244 * @event paneladded
35245 * Fires when a panel is added.
35246 * @param {Roo.LayoutRegion} this
35247 * @param {Roo.ContentPanel} panel The panel
35249 "paneladded" : true,
35251 * @event panelremoved
35252 * Fires when a panel is removed.
35253 * @param {Roo.LayoutRegion} this
35254 * @param {Roo.ContentPanel} panel The panel
35256 "panelremoved" : true,
35258 * @event beforecollapse
35259 * Fires when this region before collapse.
35260 * @param {Roo.LayoutRegion} this
35262 "beforecollapse" : true,
35265 * Fires when this region is collapsed.
35266 * @param {Roo.LayoutRegion} this
35268 "collapsed" : true,
35271 * Fires when this region is expanded.
35272 * @param {Roo.LayoutRegion} this
35277 * Fires when this region is slid into view.
35278 * @param {Roo.LayoutRegion} this
35280 "slideshow" : true,
35283 * Fires when this region slides out of view.
35284 * @param {Roo.LayoutRegion} this
35286 "slidehide" : true,
35288 * @event panelactivated
35289 * Fires when a panel is activated.
35290 * @param {Roo.LayoutRegion} this
35291 * @param {Roo.ContentPanel} panel The activated panel
35293 "panelactivated" : true,
35296 * Fires when the user resizes this region.
35297 * @param {Roo.LayoutRegion} this
35298 * @param {Number} newSize The new size (width for east/west, height for north/south)
35302 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35303 this.panels = new Roo.util.MixedCollection();
35304 this.panels.getKey = this.getPanelId.createDelegate(this);
35306 this.activePanel = null;
35307 // ensure listeners are added...
35309 if (config.listeners || config.events) {
35310 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35311 listeners : config.listeners || {},
35312 events : config.events || {}
35316 if(skipConfig !== true){
35317 this.applyConfig(config);
35321 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35323 getPanelId : function(p){
35327 applyConfig : function(config){
35328 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35329 this.config = config;
35334 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35335 * the width, for horizontal (north, south) the height.
35336 * @param {Number} newSize The new width or height
35338 resizeTo : function(newSize){
35339 var el = this.el ? this.el :
35340 (this.activePanel ? this.activePanel.getEl() : null);
35342 switch(this.position){
35345 el.setWidth(newSize);
35346 this.fireEvent("resized", this, newSize);
35350 el.setHeight(newSize);
35351 this.fireEvent("resized", this, newSize);
35357 getBox : function(){
35358 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35361 getMargins : function(){
35362 return this.margins;
35365 updateBox : function(box){
35367 var el = this.activePanel.getEl();
35368 el.dom.style.left = box.x + "px";
35369 el.dom.style.top = box.y + "px";
35370 this.activePanel.setSize(box.width, box.height);
35374 * Returns the container element for this region.
35375 * @return {Roo.Element}
35377 getEl : function(){
35378 return this.activePanel;
35382 * Returns true if this region is currently visible.
35383 * @return {Boolean}
35385 isVisible : function(){
35386 return this.activePanel ? true : false;
35389 setActivePanel : function(panel){
35390 panel = this.getPanel(panel);
35391 if(this.activePanel && this.activePanel != panel){
35392 this.activePanel.setActiveState(false);
35393 this.activePanel.getEl().setLeftTop(-10000,-10000);
35395 this.activePanel = panel;
35396 panel.setActiveState(true);
35398 panel.setSize(this.box.width, this.box.height);
35400 this.fireEvent("panelactivated", this, panel);
35401 this.fireEvent("invalidated");
35405 * Show the specified panel.
35406 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35407 * @return {Roo.ContentPanel} The shown panel or null
35409 showPanel : function(panel){
35410 panel = this.getPanel(panel);
35412 this.setActivePanel(panel);
35418 * Get the active panel for this region.
35419 * @return {Roo.ContentPanel} The active panel or null
35421 getActivePanel : function(){
35422 return this.activePanel;
35426 * Add the passed ContentPanel(s)
35427 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35428 * @return {Roo.ContentPanel} The panel added (if only one was added)
35430 add : function(panel){
35431 if(arguments.length > 1){
35432 for(var i = 0, len = arguments.length; i < len; i++) {
35433 this.add(arguments[i]);
35437 if(this.hasPanel(panel)){
35438 this.showPanel(panel);
35441 var el = panel.getEl();
35442 if(el.dom.parentNode != this.mgr.el.dom){
35443 this.mgr.el.dom.appendChild(el.dom);
35445 if(panel.setRegion){
35446 panel.setRegion(this);
35448 this.panels.add(panel);
35449 el.setStyle("position", "absolute");
35450 if(!panel.background){
35451 this.setActivePanel(panel);
35452 if(this.config.initialSize && this.panels.getCount()==1){
35453 this.resizeTo(this.config.initialSize);
35456 this.fireEvent("paneladded", this, panel);
35461 * Returns true if the panel is in this region.
35462 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35463 * @return {Boolean}
35465 hasPanel : function(panel){
35466 if(typeof panel == "object"){ // must be panel obj
35467 panel = panel.getId();
35469 return this.getPanel(panel) ? true : false;
35473 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35474 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35475 * @param {Boolean} preservePanel Overrides the config preservePanel option
35476 * @return {Roo.ContentPanel} The panel that was removed
35478 remove : function(panel, preservePanel){
35479 panel = this.getPanel(panel);
35484 this.fireEvent("beforeremove", this, panel, e);
35485 if(e.cancel === true){
35488 var panelId = panel.getId();
35489 this.panels.removeKey(panelId);
35494 * Returns the panel specified or null if it's not in this region.
35495 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35496 * @return {Roo.ContentPanel}
35498 getPanel : function(id){
35499 if(typeof id == "object"){ // must be panel obj
35502 return this.panels.get(id);
35506 * Returns this regions position (north/south/east/west/center).
35509 getPosition: function(){
35510 return this.position;
35514 * Ext JS Library 1.1.1
35515 * Copyright(c) 2006-2007, Ext JS, LLC.
35517 * Originally Released Under LGPL - original licence link has changed is not relivant.
35520 * <script type="text/javascript">
35524 * @class Roo.bootstrap.layout.Region
35525 * @extends Roo.bootstrap.layout.Basic
35526 * This class represents a region in a layout manager.
35528 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35529 * @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})
35530 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35531 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35532 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35533 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35534 * @cfg {String} title The title for the region (overrides panel titles)
35535 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35536 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35537 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35538 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35539 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35540 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35541 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35542 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35543 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35544 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35546 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35547 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35548 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35549 * @cfg {Number} width For East/West panels
35550 * @cfg {Number} height For North/South panels
35551 * @cfg {Boolean} split To show the splitter
35552 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35554 * @cfg {string} cls Extra CSS classes to add to region
35556 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35557 * @cfg {string} region the region that it inhabits..
35560 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35561 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35563 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35564 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35565 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35567 Roo.bootstrap.layout.Region = function(config)
35569 this.applyConfig(config);
35571 var mgr = config.mgr;
35572 var pos = config.region;
35573 config.skipConfig = true;
35574 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35577 this.onRender(mgr.el);
35580 this.visible = true;
35581 this.collapsed = false;
35582 this.unrendered_panels = [];
35585 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35587 position: '', // set by wrapper (eg. north/south etc..)
35588 unrendered_panels : null, // unrendered panels.
35589 createBody : function(){
35590 /** This region's body element
35591 * @type Roo.Element */
35592 this.bodyEl = this.el.createChild({
35594 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35598 onRender: function(ctr, pos)
35600 var dh = Roo.DomHelper;
35601 /** This region's container element
35602 * @type Roo.Element */
35603 this.el = dh.append(ctr.dom, {
35605 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35607 /** This region's title element
35608 * @type Roo.Element */
35610 this.titleEl = dh.append(this.el.dom,
35613 unselectable: "on",
35614 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35616 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35617 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35620 this.titleEl.enableDisplayMode();
35621 /** This region's title text element
35622 * @type HTMLElement */
35623 this.titleTextEl = this.titleEl.dom.firstChild;
35624 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35626 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35627 this.closeBtn.enableDisplayMode();
35628 this.closeBtn.on("click", this.closeClicked, this);
35629 this.closeBtn.hide();
35631 this.createBody(this.config);
35632 if(this.config.hideWhenEmpty){
35634 this.on("paneladded", this.validateVisibility, this);
35635 this.on("panelremoved", this.validateVisibility, this);
35637 if(this.autoScroll){
35638 this.bodyEl.setStyle("overflow", "auto");
35640 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35642 //if(c.titlebar !== false){
35643 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35644 this.titleEl.hide();
35646 this.titleEl.show();
35647 if(this.config.title){
35648 this.titleTextEl.innerHTML = this.config.title;
35652 if(this.config.collapsed){
35653 this.collapse(true);
35655 if(this.config.hidden){
35659 if (this.unrendered_panels && this.unrendered_panels.length) {
35660 for (var i =0;i< this.unrendered_panels.length; i++) {
35661 this.add(this.unrendered_panels[i]);
35663 this.unrendered_panels = null;
35669 applyConfig : function(c)
35672 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35673 var dh = Roo.DomHelper;
35674 if(c.titlebar !== false){
35675 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35676 this.collapseBtn.on("click", this.collapse, this);
35677 this.collapseBtn.enableDisplayMode();
35679 if(c.showPin === true || this.showPin){
35680 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35681 this.stickBtn.enableDisplayMode();
35682 this.stickBtn.on("click", this.expand, this);
35683 this.stickBtn.hide();
35688 /** This region's collapsed element
35689 * @type Roo.Element */
35692 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35693 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35696 if(c.floatable !== false){
35697 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35698 this.collapsedEl.on("click", this.collapseClick, this);
35701 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35702 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35703 id: "message", unselectable: "on", style:{"float":"left"}});
35704 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35706 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35707 this.expandBtn.on("click", this.expand, this);
35711 if(this.collapseBtn){
35712 this.collapseBtn.setVisible(c.collapsible == true);
35715 this.cmargins = c.cmargins || this.cmargins ||
35716 (this.position == "west" || this.position == "east" ?
35717 {top: 0, left: 2, right:2, bottom: 0} :
35718 {top: 2, left: 0, right:0, bottom: 2});
35720 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35723 this.bottomTabs = c.tabPosition != "top";
35725 this.autoScroll = c.autoScroll || false;
35730 this.duration = c.duration || .30;
35731 this.slideDuration = c.slideDuration || .45;
35736 * Returns true if this region is currently visible.
35737 * @return {Boolean}
35739 isVisible : function(){
35740 return this.visible;
35744 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35745 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35747 //setCollapsedTitle : function(title){
35748 // title = title || " ";
35749 // if(this.collapsedTitleTextEl){
35750 // this.collapsedTitleTextEl.innerHTML = title;
35754 getBox : function(){
35756 // if(!this.collapsed){
35757 b = this.el.getBox(false, true);
35759 // b = this.collapsedEl.getBox(false, true);
35764 getMargins : function(){
35765 return this.margins;
35766 //return this.collapsed ? this.cmargins : this.margins;
35769 highlight : function(){
35770 this.el.addClass("x-layout-panel-dragover");
35773 unhighlight : function(){
35774 this.el.removeClass("x-layout-panel-dragover");
35777 updateBox : function(box)
35779 if (!this.bodyEl) {
35780 return; // not rendered yet..
35784 if(!this.collapsed){
35785 this.el.dom.style.left = box.x + "px";
35786 this.el.dom.style.top = box.y + "px";
35787 this.updateBody(box.width, box.height);
35789 this.collapsedEl.dom.style.left = box.x + "px";
35790 this.collapsedEl.dom.style.top = box.y + "px";
35791 this.collapsedEl.setSize(box.width, box.height);
35794 this.tabs.autoSizeTabs();
35798 updateBody : function(w, h)
35801 this.el.setWidth(w);
35802 w -= this.el.getBorderWidth("rl");
35803 if(this.config.adjustments){
35804 w += this.config.adjustments[0];
35807 if(h !== null && h > 0){
35808 this.el.setHeight(h);
35809 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35810 h -= this.el.getBorderWidth("tb");
35811 if(this.config.adjustments){
35812 h += this.config.adjustments[1];
35814 this.bodyEl.setHeight(h);
35816 h = this.tabs.syncHeight(h);
35819 if(this.panelSize){
35820 w = w !== null ? w : this.panelSize.width;
35821 h = h !== null ? h : this.panelSize.height;
35823 if(this.activePanel){
35824 var el = this.activePanel.getEl();
35825 w = w !== null ? w : el.getWidth();
35826 h = h !== null ? h : el.getHeight();
35827 this.panelSize = {width: w, height: h};
35828 this.activePanel.setSize(w, h);
35830 if(Roo.isIE && this.tabs){
35831 this.tabs.el.repaint();
35836 * Returns the container element for this region.
35837 * @return {Roo.Element}
35839 getEl : function(){
35844 * Hides this region.
35847 //if(!this.collapsed){
35848 this.el.dom.style.left = "-2000px";
35851 // this.collapsedEl.dom.style.left = "-2000px";
35852 // this.collapsedEl.hide();
35854 this.visible = false;
35855 this.fireEvent("visibilitychange", this, false);
35859 * Shows this region if it was previously hidden.
35862 //if(!this.collapsed){
35865 // this.collapsedEl.show();
35867 this.visible = true;
35868 this.fireEvent("visibilitychange", this, true);
35871 closeClicked : function(){
35872 if(this.activePanel){
35873 this.remove(this.activePanel);
35877 collapseClick : function(e){
35879 e.stopPropagation();
35882 e.stopPropagation();
35888 * Collapses this region.
35889 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35892 collapse : function(skipAnim, skipCheck = false){
35893 if(this.collapsed) {
35897 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35899 this.collapsed = true;
35901 this.split.el.hide();
35903 if(this.config.animate && skipAnim !== true){
35904 this.fireEvent("invalidated", this);
35905 this.animateCollapse();
35907 this.el.setLocation(-20000,-20000);
35909 this.collapsedEl.show();
35910 this.fireEvent("collapsed", this);
35911 this.fireEvent("invalidated", this);
35917 animateCollapse : function(){
35922 * Expands this region if it was previously collapsed.
35923 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35924 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35927 expand : function(e, skipAnim){
35929 e.stopPropagation();
35931 if(!this.collapsed || this.el.hasActiveFx()) {
35935 this.afterSlideIn();
35938 this.collapsed = false;
35939 if(this.config.animate && skipAnim !== true){
35940 this.animateExpand();
35944 this.split.el.show();
35946 this.collapsedEl.setLocation(-2000,-2000);
35947 this.collapsedEl.hide();
35948 this.fireEvent("invalidated", this);
35949 this.fireEvent("expanded", this);
35953 animateExpand : function(){
35957 initTabs : function()
35959 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35961 var ts = new Roo.bootstrap.panel.Tabs({
35962 el: this.bodyEl.dom,
35963 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35964 disableTooltips: this.config.disableTabTips,
35965 toolbar : this.config.toolbar
35968 if(this.config.hideTabs){
35969 ts.stripWrap.setDisplayed(false);
35972 ts.resizeTabs = this.config.resizeTabs === true;
35973 ts.minTabWidth = this.config.minTabWidth || 40;
35974 ts.maxTabWidth = this.config.maxTabWidth || 250;
35975 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35976 ts.monitorResize = false;
35977 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35978 ts.bodyEl.addClass('roo-layout-tabs-body');
35979 this.panels.each(this.initPanelAsTab, this);
35982 initPanelAsTab : function(panel){
35983 var ti = this.tabs.addTab(
35987 this.config.closeOnTab && panel.isClosable(),
35990 if(panel.tabTip !== undefined){
35991 ti.setTooltip(panel.tabTip);
35993 ti.on("activate", function(){
35994 this.setActivePanel(panel);
35997 if(this.config.closeOnTab){
35998 ti.on("beforeclose", function(t, e){
36000 this.remove(panel);
36004 panel.tabItem = ti;
36009 updatePanelTitle : function(panel, title)
36011 if(this.activePanel == panel){
36012 this.updateTitle(title);
36015 var ti = this.tabs.getTab(panel.getEl().id);
36017 if(panel.tabTip !== undefined){
36018 ti.setTooltip(panel.tabTip);
36023 updateTitle : function(title){
36024 if(this.titleTextEl && !this.config.title){
36025 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36029 setActivePanel : function(panel)
36031 panel = this.getPanel(panel);
36032 if(this.activePanel && this.activePanel != panel){
36033 if(this.activePanel.setActiveState(false) === false){
36037 this.activePanel = panel;
36038 panel.setActiveState(true);
36039 if(this.panelSize){
36040 panel.setSize(this.panelSize.width, this.panelSize.height);
36043 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36045 this.updateTitle(panel.getTitle());
36047 this.fireEvent("invalidated", this);
36049 this.fireEvent("panelactivated", this, panel);
36053 * Shows the specified panel.
36054 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36055 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36057 showPanel : function(panel)
36059 panel = this.getPanel(panel);
36062 var tab = this.tabs.getTab(panel.getEl().id);
36063 if(tab.isHidden()){
36064 this.tabs.unhideTab(tab.id);
36068 this.setActivePanel(panel);
36075 * Get the active panel for this region.
36076 * @return {Roo.ContentPanel} The active panel or null
36078 getActivePanel : function(){
36079 return this.activePanel;
36082 validateVisibility : function(){
36083 if(this.panels.getCount() < 1){
36084 this.updateTitle(" ");
36085 this.closeBtn.hide();
36088 if(!this.isVisible()){
36095 * Adds the passed ContentPanel(s) to this region.
36096 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36097 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36099 add : function(panel)
36101 if(arguments.length > 1){
36102 for(var i = 0, len = arguments.length; i < len; i++) {
36103 this.add(arguments[i]);
36108 // if we have not been rendered yet, then we can not really do much of this..
36109 if (!this.bodyEl) {
36110 this.unrendered_panels.push(panel);
36117 if(this.hasPanel(panel)){
36118 this.showPanel(panel);
36121 panel.setRegion(this);
36122 this.panels.add(panel);
36123 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36124 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36125 // and hide them... ???
36126 this.bodyEl.dom.appendChild(panel.getEl().dom);
36127 if(panel.background !== true){
36128 this.setActivePanel(panel);
36130 this.fireEvent("paneladded", this, panel);
36137 this.initPanelAsTab(panel);
36141 if(panel.background !== true){
36142 this.tabs.activate(panel.getEl().id);
36144 this.fireEvent("paneladded", this, panel);
36149 * Hides the tab for the specified panel.
36150 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36152 hidePanel : function(panel){
36153 if(this.tabs && (panel = this.getPanel(panel))){
36154 this.tabs.hideTab(panel.getEl().id);
36159 * Unhides the tab for a previously hidden panel.
36160 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36162 unhidePanel : function(panel){
36163 if(this.tabs && (panel = this.getPanel(panel))){
36164 this.tabs.unhideTab(panel.getEl().id);
36168 clearPanels : function(){
36169 while(this.panels.getCount() > 0){
36170 this.remove(this.panels.first());
36175 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36176 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36177 * @param {Boolean} preservePanel Overrides the config preservePanel option
36178 * @return {Roo.ContentPanel} The panel that was removed
36180 remove : function(panel, preservePanel)
36182 panel = this.getPanel(panel);
36187 this.fireEvent("beforeremove", this, panel, e);
36188 if(e.cancel === true){
36191 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36192 var panelId = panel.getId();
36193 this.panels.removeKey(panelId);
36195 document.body.appendChild(panel.getEl().dom);
36198 this.tabs.removeTab(panel.getEl().id);
36199 }else if (!preservePanel){
36200 this.bodyEl.dom.removeChild(panel.getEl().dom);
36202 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36203 var p = this.panels.first();
36204 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36205 tempEl.appendChild(p.getEl().dom);
36206 this.bodyEl.update("");
36207 this.bodyEl.dom.appendChild(p.getEl().dom);
36209 this.updateTitle(p.getTitle());
36211 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36212 this.setActivePanel(p);
36214 panel.setRegion(null);
36215 if(this.activePanel == panel){
36216 this.activePanel = null;
36218 if(this.config.autoDestroy !== false && preservePanel !== true){
36219 try{panel.destroy();}catch(e){}
36221 this.fireEvent("panelremoved", this, panel);
36226 * Returns the TabPanel component used by this region
36227 * @return {Roo.TabPanel}
36229 getTabs : function(){
36233 createTool : function(parentEl, className){
36234 var btn = Roo.DomHelper.append(parentEl, {
36236 cls: "x-layout-tools-button",
36239 cls: "roo-layout-tools-button-inner " + className,
36243 btn.addClassOnOver("roo-layout-tools-button-over");
36248 * Ext JS Library 1.1.1
36249 * Copyright(c) 2006-2007, Ext JS, LLC.
36251 * Originally Released Under LGPL - original licence link has changed is not relivant.
36254 * <script type="text/javascript">
36260 * @class Roo.SplitLayoutRegion
36261 * @extends Roo.LayoutRegion
36262 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36264 Roo.bootstrap.layout.Split = function(config){
36265 this.cursor = config.cursor;
36266 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36269 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36271 splitTip : "Drag to resize.",
36272 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36273 useSplitTips : false,
36275 applyConfig : function(config){
36276 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36279 onRender : function(ctr,pos) {
36281 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36282 if(!this.config.split){
36287 var splitEl = Roo.DomHelper.append(ctr.dom, {
36289 id: this.el.id + "-split",
36290 cls: "roo-layout-split roo-layout-split-"+this.position,
36293 /** The SplitBar for this region
36294 * @type Roo.SplitBar */
36295 // does not exist yet...
36296 Roo.log([this.position, this.orientation]);
36298 this.split = new Roo.bootstrap.SplitBar({
36299 dragElement : splitEl,
36300 resizingElement: this.el,
36301 orientation : this.orientation
36304 this.split.on("moved", this.onSplitMove, this);
36305 this.split.useShim = this.config.useShim === true;
36306 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36307 if(this.useSplitTips){
36308 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36310 //if(config.collapsible){
36311 // this.split.el.on("dblclick", this.collapse, this);
36314 if(typeof this.config.minSize != "undefined"){
36315 this.split.minSize = this.config.minSize;
36317 if(typeof this.config.maxSize != "undefined"){
36318 this.split.maxSize = this.config.maxSize;
36320 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36321 this.hideSplitter();
36326 getHMaxSize : function(){
36327 var cmax = this.config.maxSize || 10000;
36328 var center = this.mgr.getRegion("center");
36329 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36332 getVMaxSize : function(){
36333 var cmax = this.config.maxSize || 10000;
36334 var center = this.mgr.getRegion("center");
36335 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36338 onSplitMove : function(split, newSize){
36339 this.fireEvent("resized", this, newSize);
36343 * Returns the {@link Roo.SplitBar} for this region.
36344 * @return {Roo.SplitBar}
36346 getSplitBar : function(){
36351 this.hideSplitter();
36352 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36355 hideSplitter : function(){
36357 this.split.el.setLocation(-2000,-2000);
36358 this.split.el.hide();
36364 this.split.el.show();
36366 Roo.bootstrap.layout.Split.superclass.show.call(this);
36369 beforeSlide: function(){
36370 if(Roo.isGecko){// firefox overflow auto bug workaround
36371 this.bodyEl.clip();
36373 this.tabs.bodyEl.clip();
36375 if(this.activePanel){
36376 this.activePanel.getEl().clip();
36378 if(this.activePanel.beforeSlide){
36379 this.activePanel.beforeSlide();
36385 afterSlide : function(){
36386 if(Roo.isGecko){// firefox overflow auto bug workaround
36387 this.bodyEl.unclip();
36389 this.tabs.bodyEl.unclip();
36391 if(this.activePanel){
36392 this.activePanel.getEl().unclip();
36393 if(this.activePanel.afterSlide){
36394 this.activePanel.afterSlide();
36400 initAutoHide : function(){
36401 if(this.autoHide !== false){
36402 if(!this.autoHideHd){
36403 var st = new Roo.util.DelayedTask(this.slideIn, this);
36404 this.autoHideHd = {
36405 "mouseout": function(e){
36406 if(!e.within(this.el, true)){
36410 "mouseover" : function(e){
36416 this.el.on(this.autoHideHd);
36420 clearAutoHide : function(){
36421 if(this.autoHide !== false){
36422 this.el.un("mouseout", this.autoHideHd.mouseout);
36423 this.el.un("mouseover", this.autoHideHd.mouseover);
36427 clearMonitor : function(){
36428 Roo.get(document).un("click", this.slideInIf, this);
36431 // these names are backwards but not changed for compat
36432 slideOut : function(){
36433 if(this.isSlid || this.el.hasActiveFx()){
36436 this.isSlid = true;
36437 if(this.collapseBtn){
36438 this.collapseBtn.hide();
36440 this.closeBtnState = this.closeBtn.getStyle('display');
36441 this.closeBtn.hide();
36443 this.stickBtn.show();
36446 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36447 this.beforeSlide();
36448 this.el.setStyle("z-index", 10001);
36449 this.el.slideIn(this.getSlideAnchor(), {
36450 callback: function(){
36452 this.initAutoHide();
36453 Roo.get(document).on("click", this.slideInIf, this);
36454 this.fireEvent("slideshow", this);
36461 afterSlideIn : function(){
36462 this.clearAutoHide();
36463 this.isSlid = false;
36464 this.clearMonitor();
36465 this.el.setStyle("z-index", "");
36466 if(this.collapseBtn){
36467 this.collapseBtn.show();
36469 this.closeBtn.setStyle('display', this.closeBtnState);
36471 this.stickBtn.hide();
36473 this.fireEvent("slidehide", this);
36476 slideIn : function(cb){
36477 if(!this.isSlid || this.el.hasActiveFx()){
36481 this.isSlid = false;
36482 this.beforeSlide();
36483 this.el.slideOut(this.getSlideAnchor(), {
36484 callback: function(){
36485 this.el.setLeftTop(-10000, -10000);
36487 this.afterSlideIn();
36495 slideInIf : function(e){
36496 if(!e.within(this.el)){
36501 animateCollapse : function(){
36502 this.beforeSlide();
36503 this.el.setStyle("z-index", 20000);
36504 var anchor = this.getSlideAnchor();
36505 this.el.slideOut(anchor, {
36506 callback : function(){
36507 this.el.setStyle("z-index", "");
36508 this.collapsedEl.slideIn(anchor, {duration:.3});
36510 this.el.setLocation(-10000,-10000);
36512 this.fireEvent("collapsed", this);
36519 animateExpand : function(){
36520 this.beforeSlide();
36521 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36522 this.el.setStyle("z-index", 20000);
36523 this.collapsedEl.hide({
36526 this.el.slideIn(this.getSlideAnchor(), {
36527 callback : function(){
36528 this.el.setStyle("z-index", "");
36531 this.split.el.show();
36533 this.fireEvent("invalidated", this);
36534 this.fireEvent("expanded", this);
36562 getAnchor : function(){
36563 return this.anchors[this.position];
36566 getCollapseAnchor : function(){
36567 return this.canchors[this.position];
36570 getSlideAnchor : function(){
36571 return this.sanchors[this.position];
36574 getAlignAdj : function(){
36575 var cm = this.cmargins;
36576 switch(this.position){
36592 getExpandAdj : function(){
36593 var c = this.collapsedEl, cm = this.cmargins;
36594 switch(this.position){
36596 return [-(cm.right+c.getWidth()+cm.left), 0];
36599 return [cm.right+c.getWidth()+cm.left, 0];
36602 return [0, -(cm.top+cm.bottom+c.getHeight())];
36605 return [0, cm.top+cm.bottom+c.getHeight()];
36611 * Ext JS Library 1.1.1
36612 * Copyright(c) 2006-2007, Ext JS, LLC.
36614 * Originally Released Under LGPL - original licence link has changed is not relivant.
36617 * <script type="text/javascript">
36620 * These classes are private internal classes
36622 Roo.bootstrap.layout.Center = function(config){
36623 config.region = "center";
36624 Roo.bootstrap.layout.Region.call(this, config);
36625 this.visible = true;
36626 this.minWidth = config.minWidth || 20;
36627 this.minHeight = config.minHeight || 20;
36630 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36632 // center panel can't be hidden
36636 // center panel can't be hidden
36639 getMinWidth: function(){
36640 return this.minWidth;
36643 getMinHeight: function(){
36644 return this.minHeight;
36657 Roo.bootstrap.layout.North = function(config)
36659 config.region = 'north';
36660 config.cursor = 'n-resize';
36662 Roo.bootstrap.layout.Split.call(this, config);
36666 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36667 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36668 this.split.el.addClass("roo-layout-split-v");
36670 var size = config.initialSize || config.height;
36671 if(typeof size != "undefined"){
36672 this.el.setHeight(size);
36675 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36677 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36681 getBox : function(){
36682 if(this.collapsed){
36683 return this.collapsedEl.getBox();
36685 var box = this.el.getBox();
36687 box.height += this.split.el.getHeight();
36692 updateBox : function(box){
36693 if(this.split && !this.collapsed){
36694 box.height -= this.split.el.getHeight();
36695 this.split.el.setLeft(box.x);
36696 this.split.el.setTop(box.y+box.height);
36697 this.split.el.setWidth(box.width);
36699 if(this.collapsed){
36700 this.updateBody(box.width, null);
36702 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36710 Roo.bootstrap.layout.South = function(config){
36711 config.region = 'south';
36712 config.cursor = 's-resize';
36713 Roo.bootstrap.layout.Split.call(this, config);
36715 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36716 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36717 this.split.el.addClass("roo-layout-split-v");
36719 var size = config.initialSize || config.height;
36720 if(typeof size != "undefined"){
36721 this.el.setHeight(size);
36725 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36726 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36727 getBox : function(){
36728 if(this.collapsed){
36729 return this.collapsedEl.getBox();
36731 var box = this.el.getBox();
36733 var sh = this.split.el.getHeight();
36740 updateBox : function(box){
36741 if(this.split && !this.collapsed){
36742 var sh = this.split.el.getHeight();
36745 this.split.el.setLeft(box.x);
36746 this.split.el.setTop(box.y-sh);
36747 this.split.el.setWidth(box.width);
36749 if(this.collapsed){
36750 this.updateBody(box.width, null);
36752 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36756 Roo.bootstrap.layout.East = function(config){
36757 config.region = "east";
36758 config.cursor = "e-resize";
36759 Roo.bootstrap.layout.Split.call(this, config);
36761 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36762 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36763 this.split.el.addClass("roo-layout-split-h");
36765 var size = config.initialSize || config.width;
36766 if(typeof size != "undefined"){
36767 this.el.setWidth(size);
36770 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36771 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36772 getBox : function(){
36773 if(this.collapsed){
36774 return this.collapsedEl.getBox();
36776 var box = this.el.getBox();
36778 var sw = this.split.el.getWidth();
36785 updateBox : function(box){
36786 if(this.split && !this.collapsed){
36787 var sw = this.split.el.getWidth();
36789 this.split.el.setLeft(box.x);
36790 this.split.el.setTop(box.y);
36791 this.split.el.setHeight(box.height);
36794 if(this.collapsed){
36795 this.updateBody(null, box.height);
36797 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36801 Roo.bootstrap.layout.West = function(config){
36802 config.region = "west";
36803 config.cursor = "w-resize";
36805 Roo.bootstrap.layout.Split.call(this, config);
36807 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36808 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36809 this.split.el.addClass("roo-layout-split-h");
36813 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36814 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36816 onRender: function(ctr, pos)
36818 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36819 var size = this.config.initialSize || this.config.width;
36820 if(typeof size != "undefined"){
36821 this.el.setWidth(size);
36825 getBox : function(){
36826 if(this.collapsed){
36827 return this.collapsedEl.getBox();
36829 var box = this.el.getBox();
36831 box.width += this.split.el.getWidth();
36836 updateBox : function(box){
36837 if(this.split && !this.collapsed){
36838 var sw = this.split.el.getWidth();
36840 this.split.el.setLeft(box.x+box.width);
36841 this.split.el.setTop(box.y);
36842 this.split.el.setHeight(box.height);
36844 if(this.collapsed){
36845 this.updateBody(null, box.height);
36847 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36850 Roo.namespace("Roo.bootstrap.panel");/*
36852 * Ext JS Library 1.1.1
36853 * Copyright(c) 2006-2007, Ext JS, LLC.
36855 * Originally Released Under LGPL - original licence link has changed is not relivant.
36858 * <script type="text/javascript">
36861 * @class Roo.ContentPanel
36862 * @extends Roo.util.Observable
36863 * A basic ContentPanel element.
36864 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36865 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36866 * @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
36867 * @cfg {Boolean} closable True if the panel can be closed/removed
36868 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36869 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36870 * @cfg {Toolbar} toolbar A toolbar for this panel
36871 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36872 * @cfg {String} title The title for this panel
36873 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36874 * @cfg {String} url Calls {@link #setUrl} with this value
36875 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36876 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36877 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36878 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36879 * @cfg {Boolean} badges render the badges
36882 * Create a new ContentPanel.
36883 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36884 * @param {String/Object} config A string to set only the title or a config object
36885 * @param {String} content (optional) Set the HTML content for this panel
36886 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36888 Roo.bootstrap.panel.Content = function( config){
36890 this.tpl = config.tpl || false;
36892 var el = config.el;
36893 var content = config.content;
36895 if(config.autoCreate){ // xtype is available if this is called from factory
36898 this.el = Roo.get(el);
36899 if(!this.el && config && config.autoCreate){
36900 if(typeof config.autoCreate == "object"){
36901 if(!config.autoCreate.id){
36902 config.autoCreate.id = config.id||el;
36904 this.el = Roo.DomHelper.append(document.body,
36905 config.autoCreate, true);
36907 var elcfg = { tag: "div",
36908 cls: "roo-layout-inactive-content",
36912 elcfg.html = config.html;
36916 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36919 this.closable = false;
36920 this.loaded = false;
36921 this.active = false;
36924 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36926 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36928 this.wrapEl = this.el; //this.el.wrap();
36930 if (config.toolbar.items) {
36931 ti = config.toolbar.items ;
36932 delete config.toolbar.items ;
36936 this.toolbar.render(this.wrapEl, 'before');
36937 for(var i =0;i < ti.length;i++) {
36938 // Roo.log(['add child', items[i]]);
36939 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36941 this.toolbar.items = nitems;
36942 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36943 delete config.toolbar;
36947 // xtype created footer. - not sure if will work as we normally have to render first..
36948 if (this.footer && !this.footer.el && this.footer.xtype) {
36949 if (!this.wrapEl) {
36950 this.wrapEl = this.el.wrap();
36953 this.footer.container = this.wrapEl.createChild();
36955 this.footer = Roo.factory(this.footer, Roo);
36960 if(typeof config == "string"){
36961 this.title = config;
36963 Roo.apply(this, config);
36967 this.resizeEl = Roo.get(this.resizeEl, true);
36969 this.resizeEl = this.el;
36971 // handle view.xtype
36979 * Fires when this panel is activated.
36980 * @param {Roo.ContentPanel} this
36984 * @event deactivate
36985 * Fires when this panel is activated.
36986 * @param {Roo.ContentPanel} this
36988 "deactivate" : true,
36992 * Fires when this panel is resized if fitToFrame is true.
36993 * @param {Roo.ContentPanel} this
36994 * @param {Number} width The width after any component adjustments
36995 * @param {Number} height The height after any component adjustments
37001 * Fires when this tab is created
37002 * @param {Roo.ContentPanel} this
37013 if(this.autoScroll){
37014 this.resizeEl.setStyle("overflow", "auto");
37016 // fix randome scrolling
37017 //this.el.on('scroll', function() {
37018 // Roo.log('fix random scolling');
37019 // this.scrollTo('top',0);
37022 content = content || this.content;
37024 this.setContent(content);
37026 if(config && config.url){
37027 this.setUrl(this.url, this.params, this.loadOnce);
37032 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37034 if (this.view && typeof(this.view.xtype) != 'undefined') {
37035 this.view.el = this.el.appendChild(document.createElement("div"));
37036 this.view = Roo.factory(this.view);
37037 this.view.render && this.view.render(false, '');
37041 this.fireEvent('render', this);
37044 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37048 setRegion : function(region){
37049 this.region = region;
37050 this.setActiveClass(region && !this.background);
37054 setActiveClass: function(state)
37057 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37058 this.el.setStyle('position','relative');
37060 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37061 this.el.setStyle('position', 'absolute');
37066 * Returns the toolbar for this Panel if one was configured.
37067 * @return {Roo.Toolbar}
37069 getToolbar : function(){
37070 return this.toolbar;
37073 setActiveState : function(active)
37075 this.active = active;
37076 this.setActiveClass(active);
37078 if(this.fireEvent("deactivate", this) === false){
37083 this.fireEvent("activate", this);
37087 * Updates this panel's element
37088 * @param {String} content The new content
37089 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37091 setContent : function(content, loadScripts){
37092 this.el.update(content, loadScripts);
37095 ignoreResize : function(w, h){
37096 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37099 this.lastSize = {width: w, height: h};
37104 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37105 * @return {Roo.UpdateManager} The UpdateManager
37107 getUpdateManager : function(){
37108 return this.el.getUpdateManager();
37111 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37112 * @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:
37115 url: "your-url.php",
37116 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37117 callback: yourFunction,
37118 scope: yourObject, //(optional scope)
37121 text: "Loading...",
37126 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37127 * 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.
37128 * @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}
37129 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37130 * @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.
37131 * @return {Roo.ContentPanel} this
37134 var um = this.el.getUpdateManager();
37135 um.update.apply(um, arguments);
37141 * 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.
37142 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37143 * @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)
37144 * @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)
37145 * @return {Roo.UpdateManager} The UpdateManager
37147 setUrl : function(url, params, loadOnce){
37148 if(this.refreshDelegate){
37149 this.removeListener("activate", this.refreshDelegate);
37151 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37152 this.on("activate", this.refreshDelegate);
37153 return this.el.getUpdateManager();
37156 _handleRefresh : function(url, params, loadOnce){
37157 if(!loadOnce || !this.loaded){
37158 var updater = this.el.getUpdateManager();
37159 updater.update(url, params, this._setLoaded.createDelegate(this));
37163 _setLoaded : function(){
37164 this.loaded = true;
37168 * Returns this panel's id
37171 getId : function(){
37176 * Returns this panel's element - used by regiosn to add.
37177 * @return {Roo.Element}
37179 getEl : function(){
37180 return this.wrapEl || this.el;
37185 adjustForComponents : function(width, height)
37187 //Roo.log('adjustForComponents ');
37188 if(this.resizeEl != this.el){
37189 width -= this.el.getFrameWidth('lr');
37190 height -= this.el.getFrameWidth('tb');
37193 var te = this.toolbar.getEl();
37194 te.setWidth(width);
37195 height -= te.getHeight();
37198 var te = this.footer.getEl();
37199 te.setWidth(width);
37200 height -= te.getHeight();
37204 if(this.adjustments){
37205 width += this.adjustments[0];
37206 height += this.adjustments[1];
37208 return {"width": width, "height": height};
37211 setSize : function(width, height){
37212 if(this.fitToFrame && !this.ignoreResize(width, height)){
37213 if(this.fitContainer && this.resizeEl != this.el){
37214 this.el.setSize(width, height);
37216 var size = this.adjustForComponents(width, height);
37217 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37218 this.fireEvent('resize', this, size.width, size.height);
37223 * Returns this panel's title
37226 getTitle : function(){
37228 if (typeof(this.title) != 'object') {
37233 for (var k in this.title) {
37234 if (!this.title.hasOwnProperty(k)) {
37238 if (k.indexOf('-') >= 0) {
37239 var s = k.split('-');
37240 for (var i = 0; i<s.length; i++) {
37241 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37244 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37251 * Set this panel's title
37252 * @param {String} title
37254 setTitle : function(title){
37255 this.title = title;
37257 this.region.updatePanelTitle(this, title);
37262 * Returns true is this panel was configured to be closable
37263 * @return {Boolean}
37265 isClosable : function(){
37266 return this.closable;
37269 beforeSlide : function(){
37271 this.resizeEl.clip();
37274 afterSlide : function(){
37276 this.resizeEl.unclip();
37280 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37281 * Will fail silently if the {@link #setUrl} method has not been called.
37282 * This does not activate the panel, just updates its content.
37284 refresh : function(){
37285 if(this.refreshDelegate){
37286 this.loaded = false;
37287 this.refreshDelegate();
37292 * Destroys this panel
37294 destroy : function(){
37295 this.el.removeAllListeners();
37296 var tempEl = document.createElement("span");
37297 tempEl.appendChild(this.el.dom);
37298 tempEl.innerHTML = "";
37304 * form - if the content panel contains a form - this is a reference to it.
37305 * @type {Roo.form.Form}
37309 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37310 * This contains a reference to it.
37316 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37326 * @param {Object} cfg Xtype definition of item to add.
37330 getChildContainer: function () {
37331 return this.getEl();
37336 var ret = new Roo.factory(cfg);
37341 if (cfg.xtype.match(/^Form$/)) {
37344 //if (this.footer) {
37345 // el = this.footer.container.insertSibling(false, 'before');
37347 el = this.el.createChild();
37350 this.form = new Roo.form.Form(cfg);
37353 if ( this.form.allItems.length) {
37354 this.form.render(el.dom);
37358 // should only have one of theses..
37359 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37360 // views.. should not be just added - used named prop 'view''
37362 cfg.el = this.el.appendChild(document.createElement("div"));
37365 var ret = new Roo.factory(cfg);
37367 ret.render && ret.render(false, ''); // render blank..
37377 * @class Roo.bootstrap.panel.Grid
37378 * @extends Roo.bootstrap.panel.Content
37380 * Create a new GridPanel.
37381 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37382 * @param {Object} config A the config object
37388 Roo.bootstrap.panel.Grid = function(config)
37392 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37393 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37395 config.el = this.wrapper;
37396 //this.el = this.wrapper;
37398 if (config.container) {
37399 // ctor'ed from a Border/panel.grid
37402 this.wrapper.setStyle("overflow", "hidden");
37403 this.wrapper.addClass('roo-grid-container');
37408 if(config.toolbar){
37409 var tool_el = this.wrapper.createChild();
37410 this.toolbar = Roo.factory(config.toolbar);
37412 if (config.toolbar.items) {
37413 ti = config.toolbar.items ;
37414 delete config.toolbar.items ;
37418 this.toolbar.render(tool_el);
37419 for(var i =0;i < ti.length;i++) {
37420 // Roo.log(['add child', items[i]]);
37421 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37423 this.toolbar.items = nitems;
37425 delete config.toolbar;
37428 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37429 config.grid.scrollBody = true;;
37430 config.grid.monitorWindowResize = false; // turn off autosizing
37431 config.grid.autoHeight = false;
37432 config.grid.autoWidth = false;
37434 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37436 if (config.background) {
37437 // render grid on panel activation (if panel background)
37438 this.on('activate', function(gp) {
37439 if (!gp.grid.rendered) {
37440 gp.grid.render(this.wrapper);
37441 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37446 this.grid.render(this.wrapper);
37447 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37450 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37451 // ??? needed ??? config.el = this.wrapper;
37456 // xtype created footer. - not sure if will work as we normally have to render first..
37457 if (this.footer && !this.footer.el && this.footer.xtype) {
37459 var ctr = this.grid.getView().getFooterPanel(true);
37460 this.footer.dataSource = this.grid.dataSource;
37461 this.footer = Roo.factory(this.footer, Roo);
37462 this.footer.render(ctr);
37472 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37473 getId : function(){
37474 return this.grid.id;
37478 * Returns the grid for this panel
37479 * @return {Roo.bootstrap.Table}
37481 getGrid : function(){
37485 setSize : function(width, height){
37486 if(!this.ignoreResize(width, height)){
37487 var grid = this.grid;
37488 var size = this.adjustForComponents(width, height);
37489 var gridel = grid.getGridEl();
37490 gridel.setSize(size.width, size.height);
37492 var thd = grid.getGridEl().select('thead',true).first();
37493 var tbd = grid.getGridEl().select('tbody', true).first();
37495 tbd.setSize(width, height - thd.getHeight());
37504 beforeSlide : function(){
37505 this.grid.getView().scroller.clip();
37508 afterSlide : function(){
37509 this.grid.getView().scroller.unclip();
37512 destroy : function(){
37513 this.grid.destroy();
37515 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37520 * @class Roo.bootstrap.panel.Nest
37521 * @extends Roo.bootstrap.panel.Content
37523 * Create a new Panel, that can contain a layout.Border.
37526 * @param {Roo.BorderLayout} layout The layout for this panel
37527 * @param {String/Object} config A string to set only the title or a config object
37529 Roo.bootstrap.panel.Nest = function(config)
37531 // construct with only one argument..
37532 /* FIXME - implement nicer consturctors
37533 if (layout.layout) {
37535 layout = config.layout;
37536 delete config.layout;
37538 if (layout.xtype && !layout.getEl) {
37539 // then layout needs constructing..
37540 layout = Roo.factory(layout, Roo);
37544 config.el = config.layout.getEl();
37546 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37548 config.layout.monitorWindowResize = false; // turn off autosizing
37549 this.layout = config.layout;
37550 this.layout.getEl().addClass("roo-layout-nested-layout");
37557 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37559 setSize : function(width, height){
37560 if(!this.ignoreResize(width, height)){
37561 var size = this.adjustForComponents(width, height);
37562 var el = this.layout.getEl();
37563 if (size.height < 1) {
37564 el.setWidth(size.width);
37566 el.setSize(size.width, size.height);
37568 var touch = el.dom.offsetWidth;
37569 this.layout.layout();
37570 // ie requires a double layout on the first pass
37571 if(Roo.isIE && !this.initialized){
37572 this.initialized = true;
37573 this.layout.layout();
37578 // activate all subpanels if not currently active..
37580 setActiveState : function(active){
37581 this.active = active;
37582 this.setActiveClass(active);
37585 this.fireEvent("deactivate", this);
37589 this.fireEvent("activate", this);
37590 // not sure if this should happen before or after..
37591 if (!this.layout) {
37592 return; // should not happen..
37595 for (var r in this.layout.regions) {
37596 reg = this.layout.getRegion(r);
37597 if (reg.getActivePanel()) {
37598 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37599 reg.setActivePanel(reg.getActivePanel());
37602 if (!reg.panels.length) {
37605 reg.showPanel(reg.getPanel(0));
37614 * Returns the nested BorderLayout for this panel
37615 * @return {Roo.BorderLayout}
37617 getLayout : function(){
37618 return this.layout;
37622 * Adds a xtype elements to the layout of the nested panel
37626 xtype : 'ContentPanel',
37633 xtype : 'NestedLayoutPanel',
37639 items : [ ... list of content panels or nested layout panels.. ]
37643 * @param {Object} cfg Xtype definition of item to add.
37645 addxtype : function(cfg) {
37646 return this.layout.addxtype(cfg);
37651 * Ext JS Library 1.1.1
37652 * Copyright(c) 2006-2007, Ext JS, LLC.
37654 * Originally Released Under LGPL - original licence link has changed is not relivant.
37657 * <script type="text/javascript">
37660 * @class Roo.TabPanel
37661 * @extends Roo.util.Observable
37662 * A lightweight tab container.
37666 // basic tabs 1, built from existing content
37667 var tabs = new Roo.TabPanel("tabs1");
37668 tabs.addTab("script", "View Script");
37669 tabs.addTab("markup", "View Markup");
37670 tabs.activate("script");
37672 // more advanced tabs, built from javascript
37673 var jtabs = new Roo.TabPanel("jtabs");
37674 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37676 // set up the UpdateManager
37677 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37678 var updater = tab2.getUpdateManager();
37679 updater.setDefaultUrl("ajax1.htm");
37680 tab2.on('activate', updater.refresh, updater, true);
37682 // Use setUrl for Ajax loading
37683 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37684 tab3.setUrl("ajax2.htm", null, true);
37687 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37690 jtabs.activate("jtabs-1");
37693 * Create a new TabPanel.
37694 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37695 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37697 Roo.bootstrap.panel.Tabs = function(config){
37699 * The container element for this TabPanel.
37700 * @type Roo.Element
37702 this.el = Roo.get(config.el);
37705 if(typeof config == "boolean"){
37706 this.tabPosition = config ? "bottom" : "top";
37708 Roo.apply(this, config);
37712 if(this.tabPosition == "bottom"){
37713 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37714 this.el.addClass("roo-tabs-bottom");
37716 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37717 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37718 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37720 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37722 if(this.tabPosition != "bottom"){
37723 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37724 * @type Roo.Element
37726 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37727 this.el.addClass("roo-tabs-top");
37731 this.bodyEl.setStyle("position", "relative");
37733 this.active = null;
37734 this.activateDelegate = this.activate.createDelegate(this);
37739 * Fires when the active tab changes
37740 * @param {Roo.TabPanel} this
37741 * @param {Roo.TabPanelItem} activePanel The new active tab
37745 * @event beforetabchange
37746 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37747 * @param {Roo.TabPanel} this
37748 * @param {Object} e Set cancel to true on this object to cancel the tab change
37749 * @param {Roo.TabPanelItem} tab The tab being changed to
37751 "beforetabchange" : true
37754 Roo.EventManager.onWindowResize(this.onResize, this);
37755 this.cpad = this.el.getPadding("lr");
37756 this.hiddenCount = 0;
37759 // toolbar on the tabbar support...
37760 if (this.toolbar) {
37761 alert("no toolbar support yet");
37762 this.toolbar = false;
37764 var tcfg = this.toolbar;
37765 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37766 this.toolbar = new Roo.Toolbar(tcfg);
37767 if (Roo.isSafari) {
37768 var tbl = tcfg.container.child('table', true);
37769 tbl.setAttribute('width', '100%');
37777 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37780 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37782 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37784 tabPosition : "top",
37786 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37788 currentTabWidth : 0,
37790 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37794 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37798 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37800 preferredTabWidth : 175,
37802 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37804 resizeTabs : false,
37806 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37808 monitorResize : true,
37810 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37815 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37816 * @param {String} id The id of the div to use <b>or create</b>
37817 * @param {String} text The text for the tab
37818 * @param {String} content (optional) Content to put in the TabPanelItem body
37819 * @param {Boolean} closable (optional) True to create a close icon on the tab
37820 * @return {Roo.TabPanelItem} The created TabPanelItem
37822 addTab : function(id, text, content, closable, tpl)
37824 var item = new Roo.bootstrap.panel.TabItem({
37828 closable : closable,
37831 this.addTabItem(item);
37833 item.setContent(content);
37839 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37840 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37841 * @return {Roo.TabPanelItem}
37843 getTab : function(id){
37844 return this.items[id];
37848 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37849 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37851 hideTab : function(id){
37852 var t = this.items[id];
37855 this.hiddenCount++;
37856 this.autoSizeTabs();
37861 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37862 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37864 unhideTab : function(id){
37865 var t = this.items[id];
37867 t.setHidden(false);
37868 this.hiddenCount--;
37869 this.autoSizeTabs();
37874 * Adds an existing {@link Roo.TabPanelItem}.
37875 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37877 addTabItem : function(item){
37878 this.items[item.id] = item;
37879 this.items.push(item);
37880 // if(this.resizeTabs){
37881 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37882 // this.autoSizeTabs();
37884 // item.autoSize();
37889 * Removes a {@link Roo.TabPanelItem}.
37890 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37892 removeTab : function(id){
37893 var items = this.items;
37894 var tab = items[id];
37895 if(!tab) { return; }
37896 var index = items.indexOf(tab);
37897 if(this.active == tab && items.length > 1){
37898 var newTab = this.getNextAvailable(index);
37903 this.stripEl.dom.removeChild(tab.pnode.dom);
37904 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37905 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37907 items.splice(index, 1);
37908 delete this.items[tab.id];
37909 tab.fireEvent("close", tab);
37910 tab.purgeListeners();
37911 this.autoSizeTabs();
37914 getNextAvailable : function(start){
37915 var items = this.items;
37917 // look for a next tab that will slide over to
37918 // replace the one being removed
37919 while(index < items.length){
37920 var item = items[++index];
37921 if(item && !item.isHidden()){
37925 // if one isn't found select the previous tab (on the left)
37928 var item = items[--index];
37929 if(item && !item.isHidden()){
37937 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37938 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37940 disableTab : function(id){
37941 var tab = this.items[id];
37942 if(tab && this.active != tab){
37948 * Enables a {@link Roo.TabPanelItem} that is disabled.
37949 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37951 enableTab : function(id){
37952 var tab = this.items[id];
37957 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37958 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37959 * @return {Roo.TabPanelItem} The TabPanelItem.
37961 activate : function(id){
37962 var tab = this.items[id];
37966 if(tab == this.active || tab.disabled){
37970 this.fireEvent("beforetabchange", this, e, tab);
37971 if(e.cancel !== true && !tab.disabled){
37973 this.active.hide();
37975 this.active = this.items[id];
37976 this.active.show();
37977 this.fireEvent("tabchange", this, this.active);
37983 * Gets the active {@link Roo.TabPanelItem}.
37984 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37986 getActiveTab : function(){
37987 return this.active;
37991 * Updates the tab body element to fit the height of the container element
37992 * for overflow scrolling
37993 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37995 syncHeight : function(targetHeight){
37996 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37997 var bm = this.bodyEl.getMargins();
37998 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37999 this.bodyEl.setHeight(newHeight);
38003 onResize : function(){
38004 if(this.monitorResize){
38005 this.autoSizeTabs();
38010 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38012 beginUpdate : function(){
38013 this.updating = true;
38017 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38019 endUpdate : function(){
38020 this.updating = false;
38021 this.autoSizeTabs();
38025 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38027 autoSizeTabs : function(){
38028 var count = this.items.length;
38029 var vcount = count - this.hiddenCount;
38030 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38033 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38034 var availWidth = Math.floor(w / vcount);
38035 var b = this.stripBody;
38036 if(b.getWidth() > w){
38037 var tabs = this.items;
38038 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38039 if(availWidth < this.minTabWidth){
38040 /*if(!this.sleft){ // incomplete scrolling code
38041 this.createScrollButtons();
38044 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38047 if(this.currentTabWidth < this.preferredTabWidth){
38048 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38054 * Returns the number of tabs in this TabPanel.
38057 getCount : function(){
38058 return this.items.length;
38062 * Resizes all the tabs to the passed width
38063 * @param {Number} The new width
38065 setTabWidth : function(width){
38066 this.currentTabWidth = width;
38067 for(var i = 0, len = this.items.length; i < len; i++) {
38068 if(!this.items[i].isHidden()) {
38069 this.items[i].setWidth(width);
38075 * Destroys this TabPanel
38076 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38078 destroy : function(removeEl){
38079 Roo.EventManager.removeResizeListener(this.onResize, this);
38080 for(var i = 0, len = this.items.length; i < len; i++){
38081 this.items[i].purgeListeners();
38083 if(removeEl === true){
38084 this.el.update("");
38089 createStrip : function(container)
38091 var strip = document.createElement("nav");
38092 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38093 container.appendChild(strip);
38097 createStripList : function(strip)
38099 // div wrapper for retard IE
38100 // returns the "tr" element.
38101 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38102 //'<div class="x-tabs-strip-wrap">'+
38103 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38104 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38105 return strip.firstChild; //.firstChild.firstChild.firstChild;
38107 createBody : function(container)
38109 var body = document.createElement("div");
38110 Roo.id(body, "tab-body");
38111 //Roo.fly(body).addClass("x-tabs-body");
38112 Roo.fly(body).addClass("tab-content");
38113 container.appendChild(body);
38116 createItemBody :function(bodyEl, id){
38117 var body = Roo.getDom(id);
38119 body = document.createElement("div");
38122 //Roo.fly(body).addClass("x-tabs-item-body");
38123 Roo.fly(body).addClass("tab-pane");
38124 bodyEl.insertBefore(body, bodyEl.firstChild);
38128 createStripElements : function(stripEl, text, closable, tpl)
38130 var td = document.createElement("li"); // was td..
38133 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38136 stripEl.appendChild(td);
38138 td.className = "x-tabs-closable";
38139 if(!this.closeTpl){
38140 this.closeTpl = new Roo.Template(
38141 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38142 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38143 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38146 var el = this.closeTpl.overwrite(td, {"text": text});
38147 var close = el.getElementsByTagName("div")[0];
38148 var inner = el.getElementsByTagName("em")[0];
38149 return {"el": el, "close": close, "inner": inner};
38152 // not sure what this is..
38153 // if(!this.tabTpl){
38154 //this.tabTpl = new Roo.Template(
38155 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38156 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38158 // this.tabTpl = new Roo.Template(
38159 // '<a href="#">' +
38160 // '<span unselectable="on"' +
38161 // (this.disableTooltips ? '' : ' title="{text}"') +
38162 // ' >{text}</span></a>'
38168 var template = tpl || this.tabTpl || false;
38172 template = new Roo.Template(
38174 '<span unselectable="on"' +
38175 (this.disableTooltips ? '' : ' title="{text}"') +
38176 ' >{text}</span></a>'
38180 switch (typeof(template)) {
38184 template = new Roo.Template(template);
38190 var el = template.overwrite(td, {"text": text});
38192 var inner = el.getElementsByTagName("span")[0];
38194 return {"el": el, "inner": inner};
38202 * @class Roo.TabPanelItem
38203 * @extends Roo.util.Observable
38204 * Represents an individual item (tab plus body) in a TabPanel.
38205 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38206 * @param {String} id The id of this TabPanelItem
38207 * @param {String} text The text for the tab of this TabPanelItem
38208 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38210 Roo.bootstrap.panel.TabItem = function(config){
38212 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38213 * @type Roo.TabPanel
38215 this.tabPanel = config.panel;
38217 * The id for this TabPanelItem
38220 this.id = config.id;
38222 this.disabled = false;
38224 this.text = config.text;
38226 this.loaded = false;
38227 this.closable = config.closable;
38230 * The body element for this TabPanelItem.
38231 * @type Roo.Element
38233 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38234 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38235 this.bodyEl.setStyle("display", "block");
38236 this.bodyEl.setStyle("zoom", "1");
38237 //this.hideAction();
38239 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38241 this.el = Roo.get(els.el);
38242 this.inner = Roo.get(els.inner, true);
38243 this.textEl = Roo.get(this.el.dom.firstChild, true);
38244 this.pnode = Roo.get(els.el.parentNode, true);
38245 // this.el.on("mousedown", this.onTabMouseDown, this);
38246 this.el.on("click", this.onTabClick, this);
38248 if(config.closable){
38249 var c = Roo.get(els.close, true);
38250 c.dom.title = this.closeText;
38251 c.addClassOnOver("close-over");
38252 c.on("click", this.closeClick, this);
38258 * Fires when this tab becomes the active tab.
38259 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38260 * @param {Roo.TabPanelItem} this
38264 * @event beforeclose
38265 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38266 * @param {Roo.TabPanelItem} this
38267 * @param {Object} e Set cancel to true on this object to cancel the close.
38269 "beforeclose": true,
38272 * Fires when this tab is closed.
38273 * @param {Roo.TabPanelItem} this
38277 * @event deactivate
38278 * Fires when this tab is no longer the active tab.
38279 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38280 * @param {Roo.TabPanelItem} this
38282 "deactivate" : true
38284 this.hidden = false;
38286 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38289 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38291 purgeListeners : function(){
38292 Roo.util.Observable.prototype.purgeListeners.call(this);
38293 this.el.removeAllListeners();
38296 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38299 this.pnode.addClass("active");
38302 this.tabPanel.stripWrap.repaint();
38304 this.fireEvent("activate", this.tabPanel, this);
38308 * Returns true if this tab is the active tab.
38309 * @return {Boolean}
38311 isActive : function(){
38312 return this.tabPanel.getActiveTab() == this;
38316 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38319 this.pnode.removeClass("active");
38321 this.fireEvent("deactivate", this.tabPanel, this);
38324 hideAction : function(){
38325 this.bodyEl.hide();
38326 this.bodyEl.setStyle("position", "absolute");
38327 this.bodyEl.setLeft("-20000px");
38328 this.bodyEl.setTop("-20000px");
38331 showAction : function(){
38332 this.bodyEl.setStyle("position", "relative");
38333 this.bodyEl.setTop("");
38334 this.bodyEl.setLeft("");
38335 this.bodyEl.show();
38339 * Set the tooltip for the tab.
38340 * @param {String} tooltip The tab's tooltip
38342 setTooltip : function(text){
38343 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38344 this.textEl.dom.qtip = text;
38345 this.textEl.dom.removeAttribute('title');
38347 this.textEl.dom.title = text;
38351 onTabClick : function(e){
38352 e.preventDefault();
38353 this.tabPanel.activate(this.id);
38356 onTabMouseDown : function(e){
38357 e.preventDefault();
38358 this.tabPanel.activate(this.id);
38361 getWidth : function(){
38362 return this.inner.getWidth();
38365 setWidth : function(width){
38366 var iwidth = width - this.pnode.getPadding("lr");
38367 this.inner.setWidth(iwidth);
38368 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38369 this.pnode.setWidth(width);
38373 * Show or hide the tab
38374 * @param {Boolean} hidden True to hide or false to show.
38376 setHidden : function(hidden){
38377 this.hidden = hidden;
38378 this.pnode.setStyle("display", hidden ? "none" : "");
38382 * Returns true if this tab is "hidden"
38383 * @return {Boolean}
38385 isHidden : function(){
38386 return this.hidden;
38390 * Returns the text for this tab
38393 getText : function(){
38397 autoSize : function(){
38398 //this.el.beginMeasure();
38399 this.textEl.setWidth(1);
38401 * #2804 [new] Tabs in Roojs
38402 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38404 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38405 //this.el.endMeasure();
38409 * Sets the text for the tab (Note: this also sets the tooltip text)
38410 * @param {String} text The tab's text and tooltip
38412 setText : function(text){
38414 this.textEl.update(text);
38415 this.setTooltip(text);
38416 //if(!this.tabPanel.resizeTabs){
38417 // this.autoSize();
38421 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38423 activate : function(){
38424 this.tabPanel.activate(this.id);
38428 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38430 disable : function(){
38431 if(this.tabPanel.active != this){
38432 this.disabled = true;
38433 this.pnode.addClass("disabled");
38438 * Enables this TabPanelItem if it was previously disabled.
38440 enable : function(){
38441 this.disabled = false;
38442 this.pnode.removeClass("disabled");
38446 * Sets the content for this TabPanelItem.
38447 * @param {String} content The content
38448 * @param {Boolean} loadScripts true to look for and load scripts
38450 setContent : function(content, loadScripts){
38451 this.bodyEl.update(content, loadScripts);
38455 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38456 * @return {Roo.UpdateManager} The UpdateManager
38458 getUpdateManager : function(){
38459 return this.bodyEl.getUpdateManager();
38463 * Set a URL to be used to load the content for this TabPanelItem.
38464 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38465 * @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)
38466 * @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)
38467 * @return {Roo.UpdateManager} The UpdateManager
38469 setUrl : function(url, params, loadOnce){
38470 if(this.refreshDelegate){
38471 this.un('activate', this.refreshDelegate);
38473 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38474 this.on("activate", this.refreshDelegate);
38475 return this.bodyEl.getUpdateManager();
38479 _handleRefresh : function(url, params, loadOnce){
38480 if(!loadOnce || !this.loaded){
38481 var updater = this.bodyEl.getUpdateManager();
38482 updater.update(url, params, this._setLoaded.createDelegate(this));
38487 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38488 * Will fail silently if the setUrl method has not been called.
38489 * This does not activate the panel, just updates its content.
38491 refresh : function(){
38492 if(this.refreshDelegate){
38493 this.loaded = false;
38494 this.refreshDelegate();
38499 _setLoaded : function(){
38500 this.loaded = true;
38504 closeClick : function(e){
38507 this.fireEvent("beforeclose", this, o);
38508 if(o.cancel !== true){
38509 this.tabPanel.removeTab(this.id);
38513 * The text displayed in the tooltip for the close icon.
38516 closeText : "Close this tab"
38519 * This script refer to:
38520 * Title: International Telephone Input
38521 * Author: Jack O'Connor
38522 * Code version: v12.1.12
38523 * Availability: https://github.com/jackocnr/intl-tel-input.git
38526 Roo.bootstrap.PhoneInputData = function() {
38529 "Afghanistan (افغانستان)",
38534 "Albania (Shqipëri)",
38539 "Algeria (الجزائر)",
38564 "Antigua and Barbuda",
38574 "Armenia (Հայաստան)",
38590 "Austria (Österreich)",
38595 "Azerbaijan (Azərbaycan)",
38605 "Bahrain (البحرين)",
38610 "Bangladesh (বাংলাদেশ)",
38620 "Belarus (Беларусь)",
38625 "Belgium (België)",
38655 "Bosnia and Herzegovina (Босна и Херцеговина)",
38670 "British Indian Ocean Territory",
38675 "British Virgin Islands",
38685 "Bulgaria (България)",
38695 "Burundi (Uburundi)",
38700 "Cambodia (កម្ពុជា)",
38705 "Cameroon (Cameroun)",
38714 ["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"]
38717 "Cape Verde (Kabu Verdi)",
38722 "Caribbean Netherlands",
38733 "Central African Republic (République centrafricaine)",
38753 "Christmas Island",
38759 "Cocos (Keeling) Islands",
38770 "Comoros (جزر القمر)",
38775 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38780 "Congo (Republic) (Congo-Brazzaville)",
38800 "Croatia (Hrvatska)",
38821 "Czech Republic (Česká republika)",
38826 "Denmark (Danmark)",
38841 "Dominican Republic (República Dominicana)",
38845 ["809", "829", "849"]
38863 "Equatorial Guinea (Guinea Ecuatorial)",
38883 "Falkland Islands (Islas Malvinas)",
38888 "Faroe Islands (Føroyar)",
38909 "French Guiana (Guyane française)",
38914 "French Polynesia (Polynésie française)",
38929 "Georgia (საქართველო)",
38934 "Germany (Deutschland)",
38954 "Greenland (Kalaallit Nunaat)",
38991 "Guinea-Bissau (Guiné Bissau)",
39016 "Hungary (Magyarország)",
39021 "Iceland (Ísland)",
39041 "Iraq (العراق)",
39057 "Israel (ישראל)",
39084 "Jordan (الأردن)",
39089 "Kazakhstan (Казахстан)",
39110 "Kuwait (الكويت)",
39115 "Kyrgyzstan (Кыргызстан)",
39125 "Latvia (Latvija)",
39130 "Lebanon (لبنان)",
39145 "Libya (ليبيا)",
39155 "Lithuania (Lietuva)",
39170 "Macedonia (FYROM) (Македонија)",
39175 "Madagascar (Madagasikara)",
39205 "Marshall Islands",
39215 "Mauritania (موريتانيا)",
39220 "Mauritius (Moris)",
39241 "Moldova (Republica Moldova)",
39251 "Mongolia (Монгол)",
39256 "Montenegro (Crna Gora)",
39266 "Morocco (المغرب)",
39272 "Mozambique (Moçambique)",
39277 "Myanmar (Burma) (မြန်မာ)",
39282 "Namibia (Namibië)",
39297 "Netherlands (Nederland)",
39302 "New Caledonia (Nouvelle-Calédonie)",
39337 "North Korea (조선 민주주의 인민 공화국)",
39342 "Northern Mariana Islands",
39358 "Pakistan (پاکستان)",
39368 "Palestine (فلسطين)",
39378 "Papua New Guinea",
39420 "Réunion (La Réunion)",
39426 "Romania (România)",
39442 "Saint Barthélemy",
39453 "Saint Kitts and Nevis",
39463 "Saint Martin (Saint-Martin (partie française))",
39469 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39474 "Saint Vincent and the Grenadines",
39489 "São Tomé and Príncipe (São Tomé e Príncipe)",
39494 "Saudi Arabia (المملكة العربية السعودية)",
39499 "Senegal (Sénégal)",
39529 "Slovakia (Slovensko)",
39534 "Slovenia (Slovenija)",
39544 "Somalia (Soomaaliya)",
39554 "South Korea (대한민국)",
39559 "South Sudan (جنوب السودان)",
39569 "Sri Lanka (ශ්රී ලංකාව)",
39574 "Sudan (السودان)",
39584 "Svalbard and Jan Mayen",
39595 "Sweden (Sverige)",
39600 "Switzerland (Schweiz)",
39605 "Syria (سوريا)",
39650 "Trinidad and Tobago",
39655 "Tunisia (تونس)",
39660 "Turkey (Türkiye)",
39670 "Turks and Caicos Islands",
39680 "U.S. Virgin Islands",
39690 "Ukraine (Україна)",
39695 "United Arab Emirates (الإمارات العربية المتحدة)",
39717 "Uzbekistan (Oʻzbekiston)",
39727 "Vatican City (Città del Vaticano)",
39738 "Vietnam (Việt Nam)",
39743 "Wallis and Futuna (Wallis-et-Futuna)",
39748 "Western Sahara (الصحراء الغربية)",
39754 "Yemen (اليمن)",
39778 * This script refer to:
39779 * Title: International Telephone Input
39780 * Author: Jack O'Connor
39781 * Code version: v12.1.12
39782 * Availability: https://github.com/jackocnr/intl-tel-input.git
39786 * @class Roo.bootstrap.PhoneInput
39787 * @extends Roo.bootstrap.TriggerField
39788 * An input with International dial-code selection
39790 * @cfg {String} defaultDialCode default '+852'
39791 * @cfg {Array} preferedCountries default []
39794 * Create a new PhoneInput.
39795 * @param {Object} config Configuration options
39798 Roo.bootstrap.PhoneInput = function(config) {
39799 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39802 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39804 listWidth: undefined,
39806 selectedClass: 'active',
39808 invalidClass : "has-warning",
39810 validClass: 'has-success',
39812 allowed: '0123456789',
39815 * @cfg {String} defaultDialCode The default dial code when initializing the input
39817 defaultDialCode: '+852',
39820 * @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
39822 preferedCountries: false,
39824 getAutoCreate : function()
39826 var data = Roo.bootstrap.PhoneInputData();
39827 var align = this.labelAlign || this.parentLabelAlign();
39830 this.allCountries = [];
39831 this.dialCodeMapping = [];
39833 for (var i = 0; i < data.length; i++) {
39835 this.allCountries[i] = {
39839 priority: c[3] || 0,
39840 areaCodes: c[4] || null
39842 this.dialCodeMapping[c[2]] = {
39845 priority: c[3] || 0,
39846 areaCodes: c[4] || null
39858 cls : 'form-control tel-input',
39859 autocomplete: 'new-password'
39862 var hiddenInput = {
39865 cls: 'hidden-tel-input'
39869 hiddenInput.name = this.name;
39872 if (this.disabled) {
39873 input.disabled = true;
39876 var flag_container = {
39893 cls: this.hasFeedback ? 'has-feedback' : '',
39899 cls: 'dial-code-holder',
39906 cls: 'roo-select2-container input-group',
39913 if (this.fieldLabel.length) {
39916 tooltip: 'This field is required'
39922 cls: 'control-label',
39928 html: this.fieldLabel
39931 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39937 if(this.indicatorpos == 'right') {
39938 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39945 if(align == 'left') {
39953 if(this.labelWidth > 12){
39954 label.style = "width: " + this.labelWidth + 'px';
39956 if(this.labelWidth < 13 && this.labelmd == 0){
39957 this.labelmd = this.labelWidth;
39959 if(this.labellg > 0){
39960 label.cls += ' col-lg-' + this.labellg;
39961 input.cls += ' col-lg-' + (12 - this.labellg);
39963 if(this.labelmd > 0){
39964 label.cls += ' col-md-' + this.labelmd;
39965 container.cls += ' col-md-' + (12 - this.labelmd);
39967 if(this.labelsm > 0){
39968 label.cls += ' col-sm-' + this.labelsm;
39969 container.cls += ' col-sm-' + (12 - this.labelsm);
39971 if(this.labelxs > 0){
39972 label.cls += ' col-xs-' + this.labelxs;
39973 container.cls += ' col-xs-' + (12 - this.labelxs);
39983 var settings = this;
39985 ['xs','sm','md','lg'].map(function(size){
39986 if (settings[size]) {
39987 cfg.cls += ' col-' + size + '-' + settings[size];
39991 this.store = new Roo.data.Store({
39992 proxy : new Roo.data.MemoryProxy({}),
39993 reader : new Roo.data.JsonReader({
40004 'name' : 'dialCode',
40008 'name' : 'priority',
40012 'name' : 'areaCodes',
40019 if(!this.preferedCountries) {
40020 this.preferedCountries = [
40027 var p = this.preferedCountries.reverse();
40030 for (var i = 0; i < p.length; i++) {
40031 for (var j = 0; j < this.allCountries.length; j++) {
40032 if(this.allCountries[j].iso2 == p[i]) {
40033 var t = this.allCountries[j];
40034 this.allCountries.splice(j,1);
40035 this.allCountries.unshift(t);
40041 this.store.proxy.data = {
40043 data: this.allCountries
40049 initEvents : function()
40052 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40054 this.indicator = this.indicatorEl();
40055 this.flag = this.flagEl();
40056 this.dialCodeHolder = this.dialCodeHolderEl();
40058 this.trigger = this.el.select('div.flag-box',true).first();
40059 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40064 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40065 _this.list.setWidth(lw);
40068 this.list.on('mouseover', this.onViewOver, this);
40069 this.list.on('mousemove', this.onViewMove, this);
40070 this.inputEl().on("keyup", this.onKeyUp, this);
40072 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40074 this.view = new Roo.View(this.list, this.tpl, {
40075 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40078 this.view.on('click', this.onViewClick, this);
40079 this.setValue(this.defaultDialCode);
40082 onTriggerClick : function(e)
40084 Roo.log('trigger click');
40089 if(this.isExpanded()){
40091 this.hasFocus = false;
40093 this.store.load({});
40094 this.hasFocus = true;
40099 isExpanded : function()
40101 return this.list.isVisible();
40104 collapse : function()
40106 if(!this.isExpanded()){
40110 Roo.get(document).un('mousedown', this.collapseIf, this);
40111 Roo.get(document).un('mousewheel', this.collapseIf, this);
40112 this.fireEvent('collapse', this);
40116 expand : function()
40120 if(this.isExpanded() || !this.hasFocus){
40124 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40125 this.list.setWidth(lw);
40128 this.restrictHeight();
40130 Roo.get(document).on('mousedown', this.collapseIf, this);
40131 Roo.get(document).on('mousewheel', this.collapseIf, this);
40133 this.fireEvent('expand', this);
40136 restrictHeight : function()
40138 this.list.alignTo(this.inputEl(), this.listAlign);
40139 this.list.alignTo(this.inputEl(), this.listAlign);
40142 onViewOver : function(e, t)
40144 if(this.inKeyMode){
40147 var item = this.view.findItemFromChild(t);
40150 var index = this.view.indexOf(item);
40151 this.select(index, false);
40156 onViewClick : function(view, doFocus, el, e)
40158 var index = this.view.getSelectedIndexes()[0];
40160 var r = this.store.getAt(index);
40163 this.onSelect(r, index);
40165 if(doFocus !== false && !this.blockFocus){
40166 this.inputEl().focus();
40170 onViewMove : function(e, t)
40172 this.inKeyMode = false;
40175 select : function(index, scrollIntoView)
40177 this.selectedIndex = index;
40178 this.view.select(index);
40179 if(scrollIntoView !== false){
40180 var el = this.view.getNode(index);
40182 this.list.scrollChildIntoView(el, false);
40187 createList : function()
40189 this.list = Roo.get(document.body).createChild({
40191 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40192 style: 'display:none'
40195 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40198 collapseIf : function(e)
40200 var in_combo = e.within(this.el);
40201 var in_list = e.within(this.list);
40202 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40204 if (in_combo || in_list || is_list) {
40210 onSelect : function(record, index)
40212 if(this.fireEvent('beforeselect', this, record, index) !== false){
40214 this.setFlagClass(record.data.iso2);
40215 this.setDialCode(record.data.dialCode);
40216 this.hasFocus = false;
40218 this.fireEvent('select', this, record, index);
40222 flagEl : function()
40224 var flag = this.el.select('div.flag',true).first();
40231 dialCodeHolderEl : function()
40233 var d = this.el.select('input.dial-code-holder',true).first();
40240 setDialCode : function(v)
40242 this.dialCodeHolder.dom.value = '+'+v;
40245 setFlagClass : function(n)
40247 this.flag.dom.className = 'flag '+n;
40250 getValue : function()
40252 var v = this.inputEl().getValue();
40253 if(this.dialCodeHolder) {
40254 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40259 setValue : function(v)
40261 var d = this.getDialCode(v);
40263 //invalid dial code
40264 if(v.length == 0 || !d || d.length == 0) {
40266 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40267 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40273 this.setFlagClass(this.dialCodeMapping[d].iso2);
40274 this.setDialCode(d);
40275 this.inputEl().dom.value = v.replace('+'+d,'');
40276 this.hiddenEl().dom.value = this.getValue();
40281 getDialCode : function(v)
40285 if (v.length == 0) {
40286 return this.dialCodeHolder.dom.value;
40290 if (v.charAt(0) != "+") {
40293 var numericChars = "";
40294 for (var i = 1; i < v.length; i++) {
40295 var c = v.charAt(i);
40298 if (this.dialCodeMapping[numericChars]) {
40299 dialCode = v.substr(1, i);
40301 if (numericChars.length == 4) {
40311 this.setValue(this.defaultDialCode);
40315 hiddenEl : function()
40317 return this.el.select('input.hidden-tel-input',true).first();
40320 onKeyUp : function(e){
40322 var k = e.getKey();
40323 var c = e.getCharCode();
40326 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40327 this.allowed.indexOf(String.fromCharCode(c)) === -1
40332 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40335 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40339 this.setValue(this.getValue());
40344 * @class Roo.bootstrap.MoneyField
40345 * @extends Roo.bootstrap.ComboBox
40346 * Bootstrap MoneyField class
40349 * Create a new MoneyField.
40350 * @param {Object} config Configuration options
40353 Roo.bootstrap.MoneyField = function(config) {
40355 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40359 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40362 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40364 allowDecimals : true,
40366 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40368 decimalSeparator : ".",
40370 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40372 decimalPrecision : 0,
40374 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40376 allowNegative : true,
40378 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40382 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40384 minValue : Number.NEGATIVE_INFINITY,
40386 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40388 maxValue : Number.MAX_VALUE,
40390 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40392 minText : "The minimum value for this field is {0}",
40394 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40396 maxText : "The maximum value for this field is {0}",
40398 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40399 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40401 nanText : "{0} is not a valid number",
40403 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40407 * @cfg {String} defaults currency of the MoneyField
40408 * value should be in lkey
40410 defaultCurrency : false,
40412 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40414 thousandsDelimiter : false,
40424 getAutoCreate : function()
40426 var align = this.labelAlign || this.parentLabelAlign();
40438 cls : 'form-control roo-money-amount-input',
40439 autocomplete: 'new-password'
40442 var hiddenInput = {
40446 cls: 'hidden-number-input'
40450 hiddenInput.name = this.name;
40453 if (this.disabled) {
40454 input.disabled = true;
40457 var clg = 12 - this.inputlg;
40458 var cmd = 12 - this.inputmd;
40459 var csm = 12 - this.inputsm;
40460 var cxs = 12 - this.inputxs;
40464 cls : 'row roo-money-field',
40468 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40472 cls: 'roo-select2-container input-group',
40476 cls : 'form-control roo-money-currency-input',
40477 autocomplete: 'new-password',
40479 name : this.currencyName
40483 cls : 'input-group-addon',
40497 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40501 cls: this.hasFeedback ? 'has-feedback' : '',
40512 if (this.fieldLabel.length) {
40515 tooltip: 'This field is required'
40521 cls: 'control-label',
40527 html: this.fieldLabel
40530 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40536 if(this.indicatorpos == 'right') {
40537 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40544 if(align == 'left') {
40552 if(this.labelWidth > 12){
40553 label.style = "width: " + this.labelWidth + 'px';
40555 if(this.labelWidth < 13 && this.labelmd == 0){
40556 this.labelmd = this.labelWidth;
40558 if(this.labellg > 0){
40559 label.cls += ' col-lg-' + this.labellg;
40560 input.cls += ' col-lg-' + (12 - this.labellg);
40562 if(this.labelmd > 0){
40563 label.cls += ' col-md-' + this.labelmd;
40564 container.cls += ' col-md-' + (12 - this.labelmd);
40566 if(this.labelsm > 0){
40567 label.cls += ' col-sm-' + this.labelsm;
40568 container.cls += ' col-sm-' + (12 - this.labelsm);
40570 if(this.labelxs > 0){
40571 label.cls += ' col-xs-' + this.labelxs;
40572 container.cls += ' col-xs-' + (12 - this.labelxs);
40583 var settings = this;
40585 ['xs','sm','md','lg'].map(function(size){
40586 if (settings[size]) {
40587 cfg.cls += ' col-' + size + '-' + settings[size];
40594 initEvents : function()
40596 this.indicator = this.indicatorEl();
40598 this.initCurrencyEvent();
40600 this.initNumberEvent();
40603 initCurrencyEvent : function()
40606 throw "can not find store for combo";
40609 this.store = Roo.factory(this.store, Roo.data);
40610 this.store.parent = this;
40614 this.triggerEl = this.el.select('.input-group-addon', true).first();
40616 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40621 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40622 _this.list.setWidth(lw);
40625 this.list.on('mouseover', this.onViewOver, this);
40626 this.list.on('mousemove', this.onViewMove, this);
40627 this.list.on('scroll', this.onViewScroll, this);
40630 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40633 this.view = new Roo.View(this.list, this.tpl, {
40634 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40637 this.view.on('click', this.onViewClick, this);
40639 this.store.on('beforeload', this.onBeforeLoad, this);
40640 this.store.on('load', this.onLoad, this);
40641 this.store.on('loadexception', this.onLoadException, this);
40643 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40644 "up" : function(e){
40645 this.inKeyMode = true;
40649 "down" : function(e){
40650 if(!this.isExpanded()){
40651 this.onTriggerClick();
40653 this.inKeyMode = true;
40658 "enter" : function(e){
40661 if(this.fireEvent("specialkey", this, e)){
40662 this.onViewClick(false);
40668 "esc" : function(e){
40672 "tab" : function(e){
40675 if(this.fireEvent("specialkey", this, e)){
40676 this.onViewClick(false);
40684 doRelay : function(foo, bar, hname){
40685 if(hname == 'down' || this.scope.isExpanded()){
40686 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40694 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40698 initNumberEvent : function(e)
40700 this.inputEl().on("keydown" , this.fireKey, this);
40701 this.inputEl().on("focus", this.onFocus, this);
40702 this.inputEl().on("blur", this.onBlur, this);
40704 this.inputEl().relayEvent('keyup', this);
40706 if(this.indicator){
40707 this.indicator.addClass('invisible');
40710 this.originalValue = this.getValue();
40712 if(this.validationEvent == 'keyup'){
40713 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40714 this.inputEl().on('keyup', this.filterValidation, this);
40716 else if(this.validationEvent !== false){
40717 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40720 if(this.selectOnFocus){
40721 this.on("focus", this.preFocus, this);
40724 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40725 this.inputEl().on("keypress", this.filterKeys, this);
40727 this.inputEl().relayEvent('keypress', this);
40730 var allowed = "0123456789";
40732 if(this.allowDecimals){
40733 allowed += this.decimalSeparator;
40736 if(this.allowNegative){
40740 if(this.thousandsDelimiter) {
40744 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40746 var keyPress = function(e){
40748 var k = e.getKey();
40750 var c = e.getCharCode();
40753 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40754 allowed.indexOf(String.fromCharCode(c)) === -1
40760 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40764 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40769 this.inputEl().on("keypress", keyPress, this);
40773 onTriggerClick : function(e)
40780 this.loadNext = false;
40782 if(this.isExpanded()){
40787 this.hasFocus = true;
40789 if(this.triggerAction == 'all') {
40790 this.doQuery(this.allQuery, true);
40794 this.doQuery(this.getRawValue());
40797 getCurrency : function()
40799 var v = this.currencyEl().getValue();
40804 restrictHeight : function()
40806 this.list.alignTo(this.currencyEl(), this.listAlign);
40807 this.list.alignTo(this.currencyEl(), this.listAlign);
40810 onViewClick : function(view, doFocus, el, e)
40812 var index = this.view.getSelectedIndexes()[0];
40814 var r = this.store.getAt(index);
40817 this.onSelect(r, index);
40821 onSelect : function(record, index){
40823 if(this.fireEvent('beforeselect', this, record, index) !== false){
40825 this.setFromCurrencyData(index > -1 ? record.data : false);
40829 this.fireEvent('select', this, record, index);
40833 setFromCurrencyData : function(o)
40837 this.lastCurrency = o;
40839 if (this.currencyField) {
40840 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40842 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40845 this.lastSelectionText = currency;
40847 //setting default currency
40848 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40849 this.setCurrency(this.defaultCurrency);
40853 this.setCurrency(currency);
40856 setFromData : function(o)
40860 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40862 this.setFromCurrencyData(c);
40867 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40869 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40872 this.setValue(value);
40876 setCurrency : function(v)
40878 this.currencyValue = v;
40881 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40886 setValue : function(v)
40888 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40894 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40896 this.inputEl().dom.value = (v == '') ? '' :
40897 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40899 if(!this.allowZero && v === '0') {
40900 this.hiddenEl().dom.value = '';
40901 this.inputEl().dom.value = '';
40908 getRawValue : function()
40910 var v = this.inputEl().getValue();
40915 getValue : function()
40917 return this.fixPrecision(this.parseValue(this.getRawValue()));
40920 parseValue : function(value)
40922 if(this.thousandsDelimiter) {
40924 r = new RegExp(",", "g");
40925 value = value.replace(r, "");
40928 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40929 return isNaN(value) ? '' : value;
40933 fixPrecision : function(value)
40935 if(this.thousandsDelimiter) {
40937 r = new RegExp(",", "g");
40938 value = value.replace(r, "");
40941 var nan = isNaN(value);
40943 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40944 return nan ? '' : value;
40946 return parseFloat(value).toFixed(this.decimalPrecision);
40949 decimalPrecisionFcn : function(v)
40951 return Math.floor(v);
40954 validateValue : function(value)
40956 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40960 var num = this.parseValue(value);
40963 this.markInvalid(String.format(this.nanText, value));
40967 if(num < this.minValue){
40968 this.markInvalid(String.format(this.minText, this.minValue));
40972 if(num > this.maxValue){
40973 this.markInvalid(String.format(this.maxText, this.maxValue));
40980 validate : function()
40982 if(this.disabled || this.allowBlank){
40987 var currency = this.getCurrency();
40989 if(this.validateValue(this.getRawValue()) && currency.length){
40994 this.markInvalid();
40998 getName: function()
41003 beforeBlur : function()
41009 var v = this.parseValue(this.getRawValue());
41016 onBlur : function()
41020 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41021 //this.el.removeClass(this.focusClass);
41024 this.hasFocus = false;
41026 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41030 var v = this.getValue();
41032 if(String(v) !== String(this.startValue)){
41033 this.fireEvent('change', this, v, this.startValue);
41036 this.fireEvent("blur", this);
41039 inputEl : function()
41041 return this.el.select('.roo-money-amount-input', true).first();
41044 currencyEl : function()
41046 return this.el.select('.roo-money-currency-input', true).first();
41049 hiddenEl : function()
41051 return this.el.select('input.hidden-number-input',true).first();