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',
2651 onRender : function(ct, position)
2653 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2656 var cfg = Roo.apply({}, this.getAutoCreate());
2659 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2661 //if (!cfg.name.length) {
2665 cfg.cls += ' ' + this.cls;
2668 cfg.style = this.style;
2670 this.el = Roo.get(document.body).createChild(cfg, position);
2672 //var type = this.el.dom.type;
2675 if(this.tabIndex !== undefined){
2676 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2679 this.dialogEl = this.el.select('.modal-dialog',true).first();
2680 this.bodyEl = this.el.select('.modal-body',true).first();
2681 this.closeEl = this.el.select('.modal-header .close', true).first();
2682 this.headerEl = this.el.select('.modal-header',true).first();
2683 this.titleEl = this.el.select('.modal-title',true).first();
2684 this.footerEl = this.el.select('.modal-footer',true).first();
2686 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2688 //this.el.addClass("x-dlg-modal");
2690 if (this.buttons.length) {
2691 Roo.each(this.buttons, function(bb) {
2692 var b = Roo.apply({}, bb);
2693 b.xns = b.xns || Roo.bootstrap;
2694 b.xtype = b.xtype || 'Button';
2695 if (typeof(b.listeners) == 'undefined') {
2696 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2699 var btn = Roo.factory(b);
2701 btn.render(this.el.select('.modal-footer div').first());
2705 // render the children.
2708 if(typeof(this.items) != 'undefined'){
2709 var items = this.items;
2712 for(var i =0;i < items.length;i++) {
2713 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2717 this.items = nitems;
2719 // where are these used - they used to be body/close/footer
2723 //this.el.addClass([this.fieldClass, this.cls]);
2727 getAutoCreate : function()
2731 html : this.html || ''
2736 cls : 'modal-title',
2740 if(this.specificTitle){
2746 if (this.allow_close) {
2758 if(this.size.length){
2759 size = 'modal-' + this.size;
2766 cls: "modal-dialog " + size,
2769 cls : "modal-content",
2772 cls : 'modal-header',
2777 cls : 'modal-footer',
2781 cls: 'btn-' + this.buttonPosition
2798 modal.cls += ' fade';
2804 getChildContainer : function() {
2809 getButtonContainer : function() {
2810 return this.el.select('.modal-footer div',true).first();
2813 initEvents : function()
2815 if (this.allow_close) {
2816 this.closeEl.on('click', this.hide, this);
2818 Roo.EventManager.onWindowResize(this.resize, this, true);
2825 this.maskEl.setSize(
2826 Roo.lib.Dom.getViewWidth(true),
2827 Roo.lib.Dom.getViewHeight(true)
2830 if (this.fitwindow) {
2832 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2838 if(this.max_width !== 0) {
2840 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2845 this.height <= Roo.lib.Dom.getViewportHeight(true) - 60 ?
2846 this.height : Roo.lib.Dom.getViewportHeight(true) - 60
2851 if(!this.fit_content) {
2852 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2856 this.setSize(w, Math.min(
2858 this.headerEl.getHeight() +
2859 this.footerEl.getHeight() +
2860 this.getChildHeight(this.bodyEl.dom.childNodes),
2861 Roo.lib.Dom.getViewportHeight(true) - 60)
2867 setSize : function(w,h)
2878 if (!this.rendered) {
2882 //this.el.setStyle('display', 'block');
2883 this.el.removeClass('hideing');
2884 this.el.addClass('show');
2886 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2889 this.el.addClass('in');
2892 this.el.addClass('in');
2895 // not sure how we can show data in here..
2897 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2900 Roo.get(document.body).addClass("x-body-masked");
2902 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2903 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2904 this.maskEl.addClass('show');
2908 this.fireEvent('show', this);
2910 // set zindex here - otherwise it appears to be ignored...
2911 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2914 this.items.forEach( function(e) {
2915 e.layout ? e.layout() : false;
2923 if(this.fireEvent("beforehide", this) !== false){
2924 this.maskEl.removeClass('show');
2925 Roo.get(document.body).removeClass("x-body-masked");
2926 this.el.removeClass('in');
2927 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2929 if(this.animate){ // why
2930 this.el.addClass('hideing');
2932 if (!this.el.hasClass('hideing')) {
2933 return; // it's been shown again...
2935 this.el.removeClass('show');
2936 this.el.removeClass('hideing');
2940 this.el.removeClass('show');
2942 this.fireEvent('hide', this);
2945 isVisible : function()
2948 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2952 addButton : function(str, cb)
2956 var b = Roo.apply({}, { html : str } );
2957 b.xns = b.xns || Roo.bootstrap;
2958 b.xtype = b.xtype || 'Button';
2959 if (typeof(b.listeners) == 'undefined') {
2960 b.listeners = { click : cb.createDelegate(this) };
2963 var btn = Roo.factory(b);
2965 btn.render(this.el.select('.modal-footer div').first());
2971 setDefaultButton : function(btn)
2973 //this.el.select('.modal-footer').()
2977 resizeTo: function(w,h)
2981 this.dialogEl.setWidth(w);
2982 if (this.diff === false) {
2983 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2986 this.bodyEl.setHeight(h - this.diff);
2988 this.fireEvent('resize', this);
2991 setContentSize : function(w, h)
2995 onButtonClick: function(btn,e)
2998 this.fireEvent('btnclick', btn.name, e);
3001 * Set the title of the Dialog
3002 * @param {String} str new Title
3004 setTitle: function(str) {
3005 this.titleEl.dom.innerHTML = str;
3008 * Set the body of the Dialog
3009 * @param {String} str new Title
3011 setBody: function(str) {
3012 this.bodyEl.dom.innerHTML = str;
3015 * Set the body of the Dialog using the template
3016 * @param {Obj} data - apply this data to the template and replace the body contents.
3018 applyBody: function(obj)
3021 Roo.log("Error - using apply Body without a template");
3024 this.tmpl.overwrite(this.bodyEl, obj);
3027 getChildHeight : function(child_nodes)
3031 child_nodes.length == 0
3036 var child_height = 0;
3038 for(var i = 0; i < child_nodes.length; i++) {
3040 // for modal with tabs...
3041 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3043 var layout_childs = child_nodes[i].childNodes;
3045 for(var j = 0; j < layout_childs.length; j++) {
3047 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3049 var layout_body_childs = layout_childs[j].childNodes;
3051 for(var k = 0; k < layout_body_childs.length; k++) {
3053 if(layout_body_childs[k].classList.contains('navbar')) {
3054 child_height += layout_body_childs[k].offsetHeight;
3055 Roo.log('nav height: '+ layout_body_childs[k].offsetHeight);
3059 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3061 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3063 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3065 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3066 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3067 Roo.log('active panel height: '+this.getChildHeight(layout_body_tab_childs[m].childNodes));
3081 child_height += child_nodes[i].offsetHeight;
3084 return child_height;
3090 Roo.apply(Roo.bootstrap.Modal, {
3092 * Button config that displays a single OK button
3101 * Button config that displays Yes and No buttons
3117 * Button config that displays OK and Cancel buttons
3132 * Button config that displays Yes, No and Cancel buttons
3156 * messagebox - can be used as a replace
3160 * @class Roo.MessageBox
3161 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3165 Roo.Msg.alert('Status', 'Changes saved successfully.');
3167 // Prompt for user data:
3168 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3170 // process text value...
3174 // Show a dialog using config options:
3176 title:'Save Changes?',
3177 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3178 buttons: Roo.Msg.YESNOCANCEL,
3185 Roo.bootstrap.MessageBox = function(){
3186 var dlg, opt, mask, waitTimer;
3187 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3188 var buttons, activeTextEl, bwidth;
3192 var handleButton = function(button){
3194 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3198 var handleHide = function(){
3200 dlg.el.removeClass(opt.cls);
3203 // Roo.TaskMgr.stop(waitTimer);
3204 // waitTimer = null;
3209 var updateButtons = function(b){
3212 buttons["ok"].hide();
3213 buttons["cancel"].hide();
3214 buttons["yes"].hide();
3215 buttons["no"].hide();
3216 //dlg.footer.dom.style.display = 'none';
3219 dlg.footerEl.dom.style.display = '';
3220 for(var k in buttons){
3221 if(typeof buttons[k] != "function"){
3224 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3225 width += buttons[k].el.getWidth()+15;
3235 var handleEsc = function(d, k, e){
3236 if(opt && opt.closable !== false){
3246 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3247 * @return {Roo.BasicDialog} The BasicDialog element
3249 getDialog : function(){
3251 dlg = new Roo.bootstrap.Modal( {
3254 //constraintoviewport:false,
3256 //collapsible : false,
3261 //buttonAlign:"center",
3262 closeClick : function(){
3263 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3266 handleButton("cancel");
3271 dlg.on("hide", handleHide);
3273 //dlg.addKeyListener(27, handleEsc);
3275 this.buttons = buttons;
3276 var bt = this.buttonText;
3277 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3278 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3279 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3280 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3282 bodyEl = dlg.bodyEl.createChild({
3284 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3285 '<textarea class="roo-mb-textarea"></textarea>' +
3286 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3288 msgEl = bodyEl.dom.firstChild;
3289 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3290 textboxEl.enableDisplayMode();
3291 textboxEl.addKeyListener([10,13], function(){
3292 if(dlg.isVisible() && opt && opt.buttons){
3295 }else if(opt.buttons.yes){
3296 handleButton("yes");
3300 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3301 textareaEl.enableDisplayMode();
3302 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3303 progressEl.enableDisplayMode();
3305 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3306 var pf = progressEl.dom.firstChild;
3308 pp = Roo.get(pf.firstChild);
3309 pp.setHeight(pf.offsetHeight);
3317 * Updates the message box body text
3318 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3319 * the XHTML-compliant non-breaking space character '&#160;')
3320 * @return {Roo.MessageBox} This message box
3322 updateText : function(text)
3324 if(!dlg.isVisible() && !opt.width){
3325 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3326 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3328 msgEl.innerHTML = text || ' ';
3330 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3331 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3333 Math.min(opt.width || cw , this.maxWidth),
3334 Math.max(opt.minWidth || this.minWidth, bwidth)
3337 activeTextEl.setWidth(w);
3339 if(dlg.isVisible()){
3340 dlg.fixedcenter = false;
3342 // to big, make it scroll. = But as usual stupid IE does not support
3345 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3346 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3347 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3349 bodyEl.dom.style.height = '';
3350 bodyEl.dom.style.overflowY = '';
3353 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3355 bodyEl.dom.style.overflowX = '';
3358 dlg.setContentSize(w, bodyEl.getHeight());
3359 if(dlg.isVisible()){
3360 dlg.fixedcenter = true;
3366 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3367 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3368 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3369 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3370 * @return {Roo.MessageBox} This message box
3372 updateProgress : function(value, text){
3374 this.updateText(text);
3377 if (pp) { // weird bug on my firefox - for some reason this is not defined
3378 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3379 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3385 * Returns true if the message box is currently displayed
3386 * @return {Boolean} True if the message box is visible, else false
3388 isVisible : function(){
3389 return dlg && dlg.isVisible();
3393 * Hides the message box if it is displayed
3396 if(this.isVisible()){
3402 * Displays a new message box, or reinitializes an existing message box, based on the config options
3403 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3404 * The following config object properties are supported:
3406 Property Type Description
3407 ---------- --------------- ------------------------------------------------------------------------------------
3408 animEl String/Element An id or Element from which the message box should animate as it opens and
3409 closes (defaults to undefined)
3410 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3411 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3412 closable Boolean False to hide the top-right close button (defaults to true). Note that
3413 progress and wait dialogs will ignore this property and always hide the
3414 close button as they can only be closed programmatically.
3415 cls String A custom CSS class to apply to the message box element
3416 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3417 displayed (defaults to 75)
3418 fn Function A callback function to execute after closing the dialog. The arguments to the
3419 function will be btn (the name of the button that was clicked, if applicable,
3420 e.g. "ok"), and text (the value of the active text field, if applicable).
3421 Progress and wait dialogs will ignore this option since they do not respond to
3422 user actions and can only be closed programmatically, so any required function
3423 should be called by the same code after it closes the dialog.
3424 icon String A CSS class that provides a background image to be used as an icon for
3425 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3426 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3427 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3428 modal Boolean False to allow user interaction with the page while the message box is
3429 displayed (defaults to true)
3430 msg String A string that will replace the existing message box body text (defaults
3431 to the XHTML-compliant non-breaking space character ' ')
3432 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3433 progress Boolean True to display a progress bar (defaults to false)
3434 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3435 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3436 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3437 title String The title text
3438 value String The string value to set into the active textbox element if displayed
3439 wait Boolean True to display a progress bar (defaults to false)
3440 width Number The width of the dialog in pixels
3447 msg: 'Please enter your address:',
3449 buttons: Roo.MessageBox.OKCANCEL,
3452 animEl: 'addAddressBtn'
3455 * @param {Object} config Configuration options
3456 * @return {Roo.MessageBox} This message box
3458 show : function(options)
3461 // this causes nightmares if you show one dialog after another
3462 // especially on callbacks..
3464 if(this.isVisible()){
3467 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3468 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3469 Roo.log("New Dialog Message:" + options.msg )
3470 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3471 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3474 var d = this.getDialog();
3476 d.setTitle(opt.title || " ");
3477 d.closeEl.setDisplayed(opt.closable !== false);
3478 activeTextEl = textboxEl;
3479 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3484 textareaEl.setHeight(typeof opt.multiline == "number" ?
3485 opt.multiline : this.defaultTextHeight);
3486 activeTextEl = textareaEl;
3495 progressEl.setDisplayed(opt.progress === true);
3496 this.updateProgress(0);
3497 activeTextEl.dom.value = opt.value || "";
3499 dlg.setDefaultButton(activeTextEl);
3501 var bs = opt.buttons;
3505 }else if(bs && bs.yes){
3506 db = buttons["yes"];
3508 dlg.setDefaultButton(db);
3510 bwidth = updateButtons(opt.buttons);
3511 this.updateText(opt.msg);
3513 d.el.addClass(opt.cls);
3515 d.proxyDrag = opt.proxyDrag === true;
3516 d.modal = opt.modal !== false;
3517 d.mask = opt.modal !== false ? mask : false;
3519 // force it to the end of the z-index stack so it gets a cursor in FF
3520 document.body.appendChild(dlg.el.dom);
3521 d.animateTarget = null;
3522 d.show(options.animEl);
3528 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3529 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3530 * and closing the message box when the process is complete.
3531 * @param {String} title The title bar text
3532 * @param {String} msg The message box body text
3533 * @return {Roo.MessageBox} This message box
3535 progress : function(title, msg){
3542 minWidth: this.minProgressWidth,
3549 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3550 * If a callback function is passed it will be called after the user clicks the button, and the
3551 * id of the button that was clicked will be passed as the only parameter to the callback
3552 * (could also be the top-right close button).
3553 * @param {String} title The title bar text
3554 * @param {String} msg The message box body text
3555 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3556 * @param {Object} scope (optional) The scope of the callback function
3557 * @return {Roo.MessageBox} This message box
3559 alert : function(title, msg, fn, scope)
3574 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3575 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3576 * You are responsible for closing the message box when the process is complete.
3577 * @param {String} msg The message box body text
3578 * @param {String} title (optional) The title bar text
3579 * @return {Roo.MessageBox} This message box
3581 wait : function(msg, title){
3592 waitTimer = Roo.TaskMgr.start({
3594 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3602 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3603 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3604 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3605 * @param {String} title The title bar text
3606 * @param {String} msg The message box body text
3607 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3608 * @param {Object} scope (optional) The scope of the callback function
3609 * @return {Roo.MessageBox} This message box
3611 confirm : function(title, msg, fn, scope){
3615 buttons: this.YESNO,
3624 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3625 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3626 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3627 * (could also be the top-right close button) and the text that was entered will be passed as the two
3628 * parameters to the callback.
3629 * @param {String} title The title bar text
3630 * @param {String} msg The message box body text
3631 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3632 * @param {Object} scope (optional) The scope of the callback function
3633 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3634 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3635 * @return {Roo.MessageBox} This message box
3637 prompt : function(title, msg, fn, scope, multiline){
3641 buttons: this.OKCANCEL,
3646 multiline: multiline,
3653 * Button config that displays a single OK button
3658 * Button config that displays Yes and No buttons
3661 YESNO : {yes:true, no:true},
3663 * Button config that displays OK and Cancel buttons
3666 OKCANCEL : {ok:true, cancel:true},
3668 * Button config that displays Yes, No and Cancel buttons
3671 YESNOCANCEL : {yes:true, no:true, cancel:true},
3674 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3677 defaultTextHeight : 75,
3679 * The maximum width in pixels of the message box (defaults to 600)
3684 * The minimum width in pixels of the message box (defaults to 100)
3689 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3690 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3693 minProgressWidth : 250,
3695 * An object containing the default button text strings that can be overriden for localized language support.
3696 * Supported properties are: ok, cancel, yes and no.
3697 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3710 * Shorthand for {@link Roo.MessageBox}
3712 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3713 Roo.Msg = Roo.Msg || Roo.MessageBox;
3722 * @class Roo.bootstrap.Navbar
3723 * @extends Roo.bootstrap.Component
3724 * Bootstrap Navbar class
3727 * Create a new Navbar
3728 * @param {Object} config The config object
3732 Roo.bootstrap.Navbar = function(config){
3733 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3737 * @event beforetoggle
3738 * Fire before toggle the menu
3739 * @param {Roo.EventObject} e
3741 "beforetoggle" : true
3745 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3754 getAutoCreate : function(){
3757 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3761 initEvents :function ()
3763 //Roo.log(this.el.select('.navbar-toggle',true));
3764 this.el.select('.navbar-toggle',true).on('click', function() {
3765 if(this.fireEvent('beforetoggle', this) !== false){
3766 this.el.select('.navbar-collapse',true).toggleClass('in');
3776 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3778 var size = this.el.getSize();
3779 this.maskEl.setSize(size.width, size.height);
3780 this.maskEl.enableDisplayMode("block");
3789 getChildContainer : function()
3791 if (this.el.select('.collapse').getCount()) {
3792 return this.el.select('.collapse',true).first();
3825 * @class Roo.bootstrap.NavSimplebar
3826 * @extends Roo.bootstrap.Navbar
3827 * Bootstrap Sidebar class
3829 * @cfg {Boolean} inverse is inverted color
3831 * @cfg {String} type (nav | pills | tabs)
3832 * @cfg {Boolean} arrangement stacked | justified
3833 * @cfg {String} align (left | right) alignment
3835 * @cfg {Boolean} main (true|false) main nav bar? default false
3836 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3838 * @cfg {String} tag (header|footer|nav|div) default is nav
3844 * Create a new Sidebar
3845 * @param {Object} config The config object
3849 Roo.bootstrap.NavSimplebar = function(config){
3850 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3853 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3869 getAutoCreate : function(){
3873 tag : this.tag || 'div',
3886 this.type = this.type || 'nav';
3887 if (['tabs','pills'].indexOf(this.type)!==-1) {
3888 cfg.cn[0].cls += ' nav-' + this.type
3892 if (this.type!=='nav') {
3893 Roo.log('nav type must be nav/tabs/pills')
3895 cfg.cn[0].cls += ' navbar-nav'
3901 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3902 cfg.cn[0].cls += ' nav-' + this.arrangement;
3906 if (this.align === 'right') {
3907 cfg.cn[0].cls += ' navbar-right';
3911 cfg.cls += ' navbar-inverse';
3938 * @class Roo.bootstrap.NavHeaderbar
3939 * @extends Roo.bootstrap.NavSimplebar
3940 * Bootstrap Sidebar class
3942 * @cfg {String} brand what is brand
3943 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944 * @cfg {String} brand_href href of the brand
3945 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3946 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3951 * Create a new Sidebar
3952 * @param {Object} config The config object
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3968 desktopCenter : false,
3971 getAutoCreate : function(){
3974 tag: this.nav || 'nav',
3981 if (this.desktopCenter) {
3982 cn.push({cls : 'container', cn : []});
3989 cls: 'navbar-header',
3994 cls: 'navbar-toggle',
3995 'data-toggle': 'collapse',
4000 html: 'Toggle navigation'
4022 cls: 'collapse navbar-collapse',
4026 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4028 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029 cfg.cls += ' navbar-' + this.position;
4031 // tag can override this..
4033 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4036 if (this.brand !== '') {
4039 href: this.brand_href ? this.brand_href : '#',
4040 cls: 'navbar-brand',
4048 cfg.cls += ' main-nav';
4056 getHeaderChildContainer : function()
4058 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059 return this.el.select('.navbar-header',true).first();
4062 return this.getChildContainer();
4066 initEvents : function()
4068 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4070 if (this.autohide) {
4075 Roo.get(document).on('scroll',function(e) {
4076 var ns = Roo.get(document).getScroll().top;
4077 var os = prevScroll;
4081 ft.removeClass('slideDown');
4082 ft.addClass('slideUp');
4085 ft.removeClass('slideUp');
4086 ft.addClass('slideDown');
4107 * @class Roo.bootstrap.NavSidebar
4108 * @extends Roo.bootstrap.Navbar
4109 * Bootstrap Sidebar class
4112 * Create a new Sidebar
4113 * @param {Object} config The config object
4117 Roo.bootstrap.NavSidebar = function(config){
4118 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4123 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4125 getAutoCreate : function(){
4130 cls: 'sidebar sidebar-nav'
4152 * @class Roo.bootstrap.NavGroup
4153 * @extends Roo.bootstrap.Component
4154 * Bootstrap NavGroup class
4155 * @cfg {String} align (left|right)
4156 * @cfg {Boolean} inverse
4157 * @cfg {String} type (nav|pills|tab) default nav
4158 * @cfg {String} navId - reference Id for navbar.
4162 * Create a new nav group
4163 * @param {Object} config The config object
4166 Roo.bootstrap.NavGroup = function(config){
4167 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4170 Roo.bootstrap.NavGroup.register(this);
4174 * Fires when the active item changes
4175 * @param {Roo.bootstrap.NavGroup} this
4176 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4195 getAutoCreate : function()
4197 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4204 if (['tabs','pills'].indexOf(this.type)!==-1) {
4205 cfg.cls += ' nav-' + this.type
4207 if (this.type!=='nav') {
4208 Roo.log('nav type must be nav/tabs/pills')
4210 cfg.cls += ' navbar-nav'
4213 if (this.parent() && this.parent().sidebar) {
4216 cls: 'dashboard-menu sidebar-menu'
4222 if (this.form === true) {
4228 if (this.align === 'right') {
4229 cfg.cls += ' navbar-right';
4231 cfg.cls += ' navbar-left';
4235 if (this.align === 'right') {
4236 cfg.cls += ' navbar-right';
4240 cfg.cls += ' navbar-inverse';
4248 * sets the active Navigation item
4249 * @param {Roo.bootstrap.NavItem} the new current navitem
4251 setActiveItem : function(item)
4254 Roo.each(this.navItems, function(v){
4259 v.setActive(false, true);
4266 item.setActive(true, true);
4267 this.fireEvent('changed', this, item, prev);
4272 * gets the active Navigation item
4273 * @return {Roo.bootstrap.NavItem} the current navitem
4275 getActive : function()
4279 Roo.each(this.navItems, function(v){
4290 indexOfNav : function()
4294 Roo.each(this.navItems, function(v,i){
4305 * adds a Navigation item
4306 * @param {Roo.bootstrap.NavItem} the navitem to add
4308 addItem : function(cfg)
4310 var cn = new Roo.bootstrap.NavItem(cfg);
4312 cn.parentId = this.id;
4313 cn.onRender(this.el, null);
4317 * register a Navigation item
4318 * @param {Roo.bootstrap.NavItem} the navitem to add
4320 register : function(item)
4322 this.navItems.push( item);
4323 item.navId = this.navId;
4328 * clear all the Navigation item
4331 clearAll : function()
4334 this.el.dom.innerHTML = '';
4337 getNavItem: function(tabId)
4340 Roo.each(this.navItems, function(e) {
4341 if (e.tabId == tabId) {
4351 setActiveNext : function()
4353 var i = this.indexOfNav(this.getActive());
4354 if (i > this.navItems.length) {
4357 this.setActiveItem(this.navItems[i+1]);
4359 setActivePrev : function()
4361 var i = this.indexOfNav(this.getActive());
4365 this.setActiveItem(this.navItems[i-1]);
4367 clearWasActive : function(except) {
4368 Roo.each(this.navItems, function(e) {
4369 if (e.tabId != except.tabId && e.was_active) {
4370 e.was_active = false;
4377 getWasActive : function ()
4380 Roo.each(this.navItems, function(e) {
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4399 * register a Navigation Group
4400 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4402 register : function(navgrp)
4404 this.groups[navgrp.navId] = navgrp;
4408 * fetch a Navigation Group based on the navigation ID
4409 * @param {string} the navgroup to add
4410 * @returns {Roo.bootstrap.NavGroup} the navgroup
4412 get: function(navId) {
4413 if (typeof(this.groups[navId]) == 'undefined') {
4415 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4417 return this.groups[navId] ;
4432 * @class Roo.bootstrap.NavItem
4433 * @extends Roo.bootstrap.Component
4434 * Bootstrap Navbar.NavItem class
4435 * @cfg {String} href link to
4436 * @cfg {String} html content of button
4437 * @cfg {String} badge text inside badge
4438 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439 * @cfg {String} glyphicon name of glyphicon
4440 * @cfg {String} icon name of font awesome icon
4441 * @cfg {Boolean} active Is item active
4442 * @cfg {Boolean} disabled Is item disabled
4444 * @cfg {Boolean} preventDefault (true | false) default false
4445 * @cfg {String} tabId the tab that this item activates.
4446 * @cfg {String} tagtype (a|span) render as a href or span?
4447 * @cfg {Boolean} animateRef (true|false) link to element default false
4450 * Create a new Navbar Item
4451 * @param {Object} config The config object
4453 Roo.bootstrap.NavItem = function(config){
4454 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4459 * The raw click event for the entire grid.
4460 * @param {Roo.EventObject} e
4465 * Fires when the active item active state changes
4466 * @param {Roo.bootstrap.NavItem} this
4467 * @param {boolean} state the new state
4473 * Fires when scroll to element
4474 * @param {Roo.bootstrap.NavItem} this
4475 * @param {Object} options
4476 * @param {Roo.EventObject} e
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4492 preventDefault : false,
4499 getAutoCreate : function(){
4508 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4510 if (this.disabled) {
4511 cfg.cls += ' disabled';
4514 if (this.href || this.html || this.glyphicon || this.icon) {
4518 href : this.href || "#",
4519 html: this.html || ''
4524 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4527 if(this.glyphicon) {
4528 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4533 cfg.cn[0].html += " <span class='caret'></span>";
4537 if (this.badge !== '') {
4539 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4547 initEvents: function()
4549 if (typeof (this.menu) != 'undefined') {
4550 this.menu.parentType = this.xtype;
4551 this.menu.triggerEl = this.el;
4552 this.menu = this.addxtype(Roo.apply({}, this.menu));
4555 this.el.select('a',true).on('click', this.onClick, this);
4557 if(this.tagtype == 'span'){
4558 this.el.select('span',true).on('click', this.onClick, this);
4561 // at this point parent should be available..
4562 this.parent().register(this);
4565 onClick : function(e)
4567 if (e.getTarget('.dropdown-menu-item')) {
4568 // did you click on a menu itemm.... - then don't trigger onclick..
4573 this.preventDefault ||
4576 Roo.log("NavItem - prevent Default?");
4580 if (this.disabled) {
4584 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4585 if (tg && tg.transition) {
4586 Roo.log("waiting for the transitionend");
4592 //Roo.log("fire event clicked");
4593 if(this.fireEvent('click', this, e) === false){
4597 if(this.tagtype == 'span'){
4601 //Roo.log(this.href);
4602 var ael = this.el.select('a',true).first();
4605 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4606 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4607 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4608 return; // ignore... - it's a 'hash' to another page.
4610 Roo.log("NavItem - prevent Default?");
4612 this.scrollToElement(e);
4616 var p = this.parent();
4618 if (['tabs','pills'].indexOf(p.type)!==-1) {
4619 if (typeof(p.setActiveItem) !== 'undefined') {
4620 p.setActiveItem(this);
4624 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4625 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4626 // remove the collapsed menu expand...
4627 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4631 isActive: function () {
4634 setActive : function(state, fire, is_was_active)
4636 if (this.active && !state && this.navId) {
4637 this.was_active = true;
4638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4640 nv.clearWasActive(this);
4644 this.active = state;
4647 this.el.removeClass('active');
4648 } else if (!this.el.hasClass('active')) {
4649 this.el.addClass('active');
4652 this.fireEvent('changed', this, state);
4655 // show a panel if it's registered and related..
4657 if (!this.navId || !this.tabId || !state || is_was_active) {
4661 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4665 var pan = tg.getPanelByName(this.tabId);
4669 // if we can not flip to new panel - go back to old nav highlight..
4670 if (false == tg.showPanel(pan)) {
4671 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4673 var onav = nv.getWasActive();
4675 onav.setActive(true, false, true);
4684 // this should not be here...
4685 setDisabled : function(state)
4687 this.disabled = state;
4689 this.el.removeClass('disabled');
4690 } else if (!this.el.hasClass('disabled')) {
4691 this.el.addClass('disabled');
4697 * Fetch the element to display the tooltip on.
4698 * @return {Roo.Element} defaults to this.el
4700 tooltipEl : function()
4702 return this.el.select('' + this.tagtype + '', true).first();
4705 scrollToElement : function(e)
4707 var c = document.body;
4710 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4712 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4713 c = document.documentElement;
4716 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4722 var o = target.calcOffsetsTo(c);
4729 this.fireEvent('scrollto', this, options, e);
4731 Roo.get(c).scrollTo('top', options.value, true);
4744 * <span> icon </span>
4745 * <span> text </span>
4746 * <span>badge </span>
4750 * @class Roo.bootstrap.NavSidebarItem
4751 * @extends Roo.bootstrap.NavItem
4752 * Bootstrap Navbar.NavSidebarItem class
4753 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4754 * {Boolean} open is the menu open
4755 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4756 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4757 * {String} buttonSize (sm|md|lg)the extra classes for the button
4758 * {Boolean} showArrow show arrow next to the text (default true)
4760 * Create a new Navbar Button
4761 * @param {Object} config The config object
4763 Roo.bootstrap.NavSidebarItem = function(config){
4764 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4769 * The raw click event for the entire grid.
4770 * @param {Roo.EventObject} e
4775 * Fires when the active item active state changes
4776 * @param {Roo.bootstrap.NavSidebarItem} this
4777 * @param {boolean} state the new state
4785 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4787 badgeWeight : 'default',
4793 buttonWeight : 'default',
4799 getAutoCreate : function(){
4804 href : this.href || '#',
4810 if(this.buttonView){
4813 href : this.href || '#',
4814 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4827 cfg.cls += ' active';
4830 if (this.disabled) {
4831 cfg.cls += ' disabled';
4834 cfg.cls += ' open x-open';
4837 if (this.glyphicon || this.icon) {
4838 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4839 a.cn.push({ tag : 'i', cls : c }) ;
4842 if(!this.buttonView){
4845 html : this.html || ''
4852 if (this.badge !== '') {
4853 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4859 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4862 a.cls += ' dropdown-toggle treeview' ;
4868 initEvents : function()
4870 if (typeof (this.menu) != 'undefined') {
4871 this.menu.parentType = this.xtype;
4872 this.menu.triggerEl = this.el;
4873 this.menu = this.addxtype(Roo.apply({}, this.menu));
4876 this.el.on('click', this.onClick, this);
4878 if(this.badge !== ''){
4879 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4884 onClick : function(e)
4891 if(this.preventDefault){
4895 this.fireEvent('click', this);
4898 disable : function()
4900 this.setDisabled(true);
4905 this.setDisabled(false);
4908 setDisabled : function(state)
4910 if(this.disabled == state){
4914 this.disabled = state;
4917 this.el.addClass('disabled');
4921 this.el.removeClass('disabled');
4926 setActive : function(state)
4928 if(this.active == state){
4932 this.active = state;
4935 this.el.addClass('active');
4939 this.el.removeClass('active');
4944 isActive: function ()
4949 setBadge : function(str)
4955 this.badgeEl.dom.innerHTML = str;
4972 * @class Roo.bootstrap.Row
4973 * @extends Roo.bootstrap.Component
4974 * Bootstrap Row class (contains columns...)
4978 * @param {Object} config The config object
4981 Roo.bootstrap.Row = function(config){
4982 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4985 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4987 getAutoCreate : function(){
5006 * @class Roo.bootstrap.Element
5007 * @extends Roo.bootstrap.Component
5008 * Bootstrap Element class
5009 * @cfg {String} html contents of the element
5010 * @cfg {String} tag tag of the element
5011 * @cfg {String} cls class of the element
5012 * @cfg {Boolean} preventDefault (true|false) default false
5013 * @cfg {Boolean} clickable (true|false) default false
5016 * Create a new Element
5017 * @param {Object} config The config object
5020 Roo.bootstrap.Element = function(config){
5021 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5027 * When a element is chick
5028 * @param {Roo.bootstrap.Element} this
5029 * @param {Roo.EventObject} e
5035 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5040 preventDefault: false,
5043 getAutoCreate : function(){
5047 // cls: this.cls, double assign in parent class Component.js :: onRender
5054 initEvents: function()
5056 Roo.bootstrap.Element.superclass.initEvents.call(this);
5059 this.el.on('click', this.onClick, this);
5064 onClick : function(e)
5066 if(this.preventDefault){
5070 this.fireEvent('click', this, e);
5073 getValue : function()
5075 return this.el.dom.innerHTML;
5078 setValue : function(value)
5080 this.el.dom.innerHTML = value;
5095 * @class Roo.bootstrap.Pagination
5096 * @extends Roo.bootstrap.Component
5097 * Bootstrap Pagination class
5098 * @cfg {String} size xs | sm | md | lg
5099 * @cfg {Boolean} inverse false | true
5102 * Create a new Pagination
5103 * @param {Object} config The config object
5106 Roo.bootstrap.Pagination = function(config){
5107 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5110 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5116 getAutoCreate : function(){
5122 cfg.cls += ' inverse';
5128 cfg.cls += " " + this.cls;
5146 * @class Roo.bootstrap.PaginationItem
5147 * @extends Roo.bootstrap.Component
5148 * Bootstrap PaginationItem class
5149 * @cfg {String} html text
5150 * @cfg {String} href the link
5151 * @cfg {Boolean} preventDefault (true | false) default true
5152 * @cfg {Boolean} active (true | false) default false
5153 * @cfg {Boolean} disabled default false
5157 * Create a new PaginationItem
5158 * @param {Object} config The config object
5162 Roo.bootstrap.PaginationItem = function(config){
5163 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5168 * The raw click event for the entire grid.
5169 * @param {Roo.EventObject} e
5175 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5179 preventDefault: true,
5184 getAutoCreate : function(){
5190 href : this.href ? this.href : '#',
5191 html : this.html ? this.html : ''
5201 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5205 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5211 initEvents: function() {
5213 this.el.on('click', this.onClick, this);
5216 onClick : function(e)
5218 Roo.log('PaginationItem on click ');
5219 if(this.preventDefault){
5227 this.fireEvent('click', this, e);
5243 * @class Roo.bootstrap.Slider
5244 * @extends Roo.bootstrap.Component
5245 * Bootstrap Slider class
5248 * Create a new Slider
5249 * @param {Object} config The config object
5252 Roo.bootstrap.Slider = function(config){
5253 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5256 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5258 getAutoCreate : function(){
5262 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5266 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5278 * Ext JS Library 1.1.1
5279 * Copyright(c) 2006-2007, Ext JS, LLC.
5281 * Originally Released Under LGPL - original licence link has changed is not relivant.
5284 * <script type="text/javascript">
5289 * @class Roo.grid.ColumnModel
5290 * @extends Roo.util.Observable
5291 * This is the default implementation of a ColumnModel used by the Grid. It defines
5292 * the columns in the grid.
5295 var colModel = new Roo.grid.ColumnModel([
5296 {header: "Ticker", width: 60, sortable: true, locked: true},
5297 {header: "Company Name", width: 150, sortable: true},
5298 {header: "Market Cap.", width: 100, sortable: true},
5299 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5300 {header: "Employees", width: 100, sortable: true, resizable: false}
5305 * The config options listed for this class are options which may appear in each
5306 * individual column definition.
5307 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5309 * @param {Object} config An Array of column config objects. See this class's
5310 * config objects for details.
5312 Roo.grid.ColumnModel = function(config){
5314 * The config passed into the constructor
5316 this.config = config;
5319 // if no id, create one
5320 // if the column does not have a dataIndex mapping,
5321 // map it to the order it is in the config
5322 for(var i = 0, len = config.length; i < len; i++){
5324 if(typeof c.dataIndex == "undefined"){
5327 if(typeof c.renderer == "string"){
5328 c.renderer = Roo.util.Format[c.renderer];
5330 if(typeof c.id == "undefined"){
5333 if(c.editor && c.editor.xtype){
5334 c.editor = Roo.factory(c.editor, Roo.grid);
5336 if(c.editor && c.editor.isFormField){
5337 c.editor = new Roo.grid.GridEditor(c.editor);
5339 this.lookup[c.id] = c;
5343 * The width of columns which have no width specified (defaults to 100)
5346 this.defaultWidth = 100;
5349 * Default sortable of columns which have no sortable specified (defaults to false)
5352 this.defaultSortable = false;
5356 * @event widthchange
5357 * Fires when the width of a column changes.
5358 * @param {ColumnModel} this
5359 * @param {Number} columnIndex The column index
5360 * @param {Number} newWidth The new width
5362 "widthchange": true,
5364 * @event headerchange
5365 * Fires when the text of a header changes.
5366 * @param {ColumnModel} this
5367 * @param {Number} columnIndex The column index
5368 * @param {Number} newText The new header text
5370 "headerchange": true,
5372 * @event hiddenchange
5373 * Fires when a column is hidden or "unhidden".
5374 * @param {ColumnModel} this
5375 * @param {Number} columnIndex The column index
5376 * @param {Boolean} hidden true if hidden, false otherwise
5378 "hiddenchange": true,
5380 * @event columnmoved
5381 * Fires when a column is moved.
5382 * @param {ColumnModel} this
5383 * @param {Number} oldIndex
5384 * @param {Number} newIndex
5386 "columnmoved" : true,
5388 * @event columlockchange
5389 * Fires when a column's locked state is changed
5390 * @param {ColumnModel} this
5391 * @param {Number} colIndex
5392 * @param {Boolean} locked true if locked
5394 "columnlockchange" : true
5396 Roo.grid.ColumnModel.superclass.constructor.call(this);
5398 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5400 * @cfg {String} header The header text to display in the Grid view.
5403 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5404 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5405 * specified, the column's index is used as an index into the Record's data Array.
5408 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5409 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5412 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5413 * Defaults to the value of the {@link #defaultSortable} property.
5414 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5417 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5420 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5423 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5426 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5429 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5430 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5431 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5432 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5435 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5438 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5441 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5444 * @cfg {String} cursor (Optional)
5447 * @cfg {String} tooltip (Optional)
5450 * @cfg {Number} xs (Optional)
5453 * @cfg {Number} sm (Optional)
5456 * @cfg {Number} md (Optional)
5459 * @cfg {Number} lg (Optional)
5462 * Returns the id of the column at the specified index.
5463 * @param {Number} index The column index
5464 * @return {String} the id
5466 getColumnId : function(index){
5467 return this.config[index].id;
5471 * Returns the column for a specified id.
5472 * @param {String} id The column id
5473 * @return {Object} the column
5475 getColumnById : function(id){
5476 return this.lookup[id];
5481 * Returns the column for a specified dataIndex.
5482 * @param {String} dataIndex The column dataIndex
5483 * @return {Object|Boolean} the column or false if not found
5485 getColumnByDataIndex: function(dataIndex){
5486 var index = this.findColumnIndex(dataIndex);
5487 return index > -1 ? this.config[index] : false;
5491 * Returns the index for a specified column id.
5492 * @param {String} id The column id
5493 * @return {Number} the index, or -1 if not found
5495 getIndexById : function(id){
5496 for(var i = 0, len = this.config.length; i < len; i++){
5497 if(this.config[i].id == id){
5505 * Returns the index for a specified column dataIndex.
5506 * @param {String} dataIndex The column dataIndex
5507 * @return {Number} the index, or -1 if not found
5510 findColumnIndex : function(dataIndex){
5511 for(var i = 0, len = this.config.length; i < len; i++){
5512 if(this.config[i].dataIndex == dataIndex){
5520 moveColumn : function(oldIndex, newIndex){
5521 var c = this.config[oldIndex];
5522 this.config.splice(oldIndex, 1);
5523 this.config.splice(newIndex, 0, c);
5524 this.dataMap = null;
5525 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5528 isLocked : function(colIndex){
5529 return this.config[colIndex].locked === true;
5532 setLocked : function(colIndex, value, suppressEvent){
5533 if(this.isLocked(colIndex) == value){
5536 this.config[colIndex].locked = value;
5538 this.fireEvent("columnlockchange", this, colIndex, value);
5542 getTotalLockedWidth : function(){
5544 for(var i = 0; i < this.config.length; i++){
5545 if(this.isLocked(i) && !this.isHidden(i)){
5546 this.totalWidth += this.getColumnWidth(i);
5552 getLockedCount : function(){
5553 for(var i = 0, len = this.config.length; i < len; i++){
5554 if(!this.isLocked(i)){
5559 return this.config.length;
5563 * Returns the number of columns.
5566 getColumnCount : function(visibleOnly){
5567 if(visibleOnly === true){
5569 for(var i = 0, len = this.config.length; i < len; i++){
5570 if(!this.isHidden(i)){
5576 return this.config.length;
5580 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5581 * @param {Function} fn
5582 * @param {Object} scope (optional)
5583 * @return {Array} result
5585 getColumnsBy : function(fn, scope){
5587 for(var i = 0, len = this.config.length; i < len; i++){
5588 var c = this.config[i];
5589 if(fn.call(scope||this, c, i) === true){
5597 * Returns true if the specified column is sortable.
5598 * @param {Number} col The column index
5601 isSortable : function(col){
5602 if(typeof this.config[col].sortable == "undefined"){
5603 return this.defaultSortable;
5605 return this.config[col].sortable;
5609 * Returns the rendering (formatting) function defined for the column.
5610 * @param {Number} col The column index.
5611 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5613 getRenderer : function(col){
5614 if(!this.config[col].renderer){
5615 return Roo.grid.ColumnModel.defaultRenderer;
5617 return this.config[col].renderer;
5621 * Sets the rendering (formatting) function for a column.
5622 * @param {Number} col The column index
5623 * @param {Function} fn The function to use to process the cell's raw data
5624 * to return HTML markup for the grid view. The render function is called with
5625 * the following parameters:<ul>
5626 * <li>Data value.</li>
5627 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5628 * <li>css A CSS style string to apply to the table cell.</li>
5629 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5630 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5631 * <li>Row index</li>
5632 * <li>Column index</li>
5633 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5635 setRenderer : function(col, fn){
5636 this.config[col].renderer = fn;
5640 * Returns the width for the specified column.
5641 * @param {Number} col The column index
5644 getColumnWidth : function(col){
5645 return this.config[col].width * 1 || this.defaultWidth;
5649 * Sets the width for a column.
5650 * @param {Number} col The column index
5651 * @param {Number} width The new width
5653 setColumnWidth : function(col, width, suppressEvent){
5654 this.config[col].width = width;
5655 this.totalWidth = null;
5657 this.fireEvent("widthchange", this, col, width);
5662 * Returns the total width of all columns.
5663 * @param {Boolean} includeHidden True to include hidden column widths
5666 getTotalWidth : function(includeHidden){
5667 if(!this.totalWidth){
5668 this.totalWidth = 0;
5669 for(var i = 0, len = this.config.length; i < len; i++){
5670 if(includeHidden || !this.isHidden(i)){
5671 this.totalWidth += this.getColumnWidth(i);
5675 return this.totalWidth;
5679 * Returns the header for the specified column.
5680 * @param {Number} col The column index
5683 getColumnHeader : function(col){
5684 return this.config[col].header;
5688 * Sets the header for a column.
5689 * @param {Number} col The column index
5690 * @param {String} header The new header
5692 setColumnHeader : function(col, header){
5693 this.config[col].header = header;
5694 this.fireEvent("headerchange", this, col, header);
5698 * Returns the tooltip for the specified column.
5699 * @param {Number} col The column index
5702 getColumnTooltip : function(col){
5703 return this.config[col].tooltip;
5706 * Sets the tooltip for a column.
5707 * @param {Number} col The column index
5708 * @param {String} tooltip The new tooltip
5710 setColumnTooltip : function(col, tooltip){
5711 this.config[col].tooltip = tooltip;
5715 * Returns the dataIndex for the specified column.
5716 * @param {Number} col The column index
5719 getDataIndex : function(col){
5720 return this.config[col].dataIndex;
5724 * Sets the dataIndex for a column.
5725 * @param {Number} col The column index
5726 * @param {Number} dataIndex The new dataIndex
5728 setDataIndex : function(col, dataIndex){
5729 this.config[col].dataIndex = dataIndex;
5735 * Returns true if the cell is editable.
5736 * @param {Number} colIndex The column index
5737 * @param {Number} rowIndex The row index - this is nto actually used..?
5740 isCellEditable : function(colIndex, rowIndex){
5741 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5745 * Returns the editor defined for the cell/column.
5746 * return false or null to disable editing.
5747 * @param {Number} colIndex The column index
5748 * @param {Number} rowIndex The row index
5751 getCellEditor : function(colIndex, rowIndex){
5752 return this.config[colIndex].editor;
5756 * Sets if a column is editable.
5757 * @param {Number} col The column index
5758 * @param {Boolean} editable True if the column is editable
5760 setEditable : function(col, editable){
5761 this.config[col].editable = editable;
5766 * Returns true if the column is hidden.
5767 * @param {Number} colIndex The column index
5770 isHidden : function(colIndex){
5771 return this.config[colIndex].hidden;
5776 * Returns true if the column width cannot be changed
5778 isFixed : function(colIndex){
5779 return this.config[colIndex].fixed;
5783 * Returns true if the column can be resized
5786 isResizable : function(colIndex){
5787 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5790 * Sets if a column is hidden.
5791 * @param {Number} colIndex The column index
5792 * @param {Boolean} hidden True if the column is hidden
5794 setHidden : function(colIndex, hidden){
5795 this.config[colIndex].hidden = hidden;
5796 this.totalWidth = null;
5797 this.fireEvent("hiddenchange", this, colIndex, hidden);
5801 * Sets the editor for a column.
5802 * @param {Number} col The column index
5803 * @param {Object} editor The editor object
5805 setEditor : function(col, editor){
5806 this.config[col].editor = editor;
5810 Roo.grid.ColumnModel.defaultRenderer = function(value)
5812 if(typeof value == "object") {
5815 if(typeof value == "string" && value.length < 1){
5819 return String.format("{0}", value);
5822 // Alias for backwards compatibility
5823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5826 * Ext JS Library 1.1.1
5827 * Copyright(c) 2006-2007, Ext JS, LLC.
5829 * Originally Released Under LGPL - original licence link has changed is not relivant.
5832 * <script type="text/javascript">
5836 * @class Roo.LoadMask
5837 * A simple utility class for generically masking elements while loading data. If the element being masked has
5838 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5839 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5840 * element's UpdateManager load indicator and will be destroyed after the initial load.
5842 * Create a new LoadMask
5843 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5844 * @param {Object} config The config object
5846 Roo.LoadMask = function(el, config){
5847 this.el = Roo.get(el);
5848 Roo.apply(this, config);
5850 this.store.on('beforeload', this.onBeforeLoad, this);
5851 this.store.on('load', this.onLoad, this);
5852 this.store.on('loadexception', this.onLoadException, this);
5853 this.removeMask = false;
5855 var um = this.el.getUpdateManager();
5856 um.showLoadIndicator = false; // disable the default indicator
5857 um.on('beforeupdate', this.onBeforeLoad, this);
5858 um.on('update', this.onLoad, this);
5859 um.on('failure', this.onLoad, this);
5860 this.removeMask = true;
5864 Roo.LoadMask.prototype = {
5866 * @cfg {Boolean} removeMask
5867 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5868 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5872 * The text to display in a centered loading message box (defaults to 'Loading...')
5876 * @cfg {String} msgCls
5877 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5879 msgCls : 'x-mask-loading',
5882 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5888 * Disables the mask to prevent it from being displayed
5890 disable : function(){
5891 this.disabled = true;
5895 * Enables the mask so that it can be displayed
5897 enable : function(){
5898 this.disabled = false;
5901 onLoadException : function()
5905 if (typeof(arguments[3]) != 'undefined') {
5906 Roo.MessageBox.alert("Error loading",arguments[3]);
5910 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5911 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5918 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5927 onBeforeLoad : function(){
5929 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5934 destroy : function(){
5936 this.store.un('beforeload', this.onBeforeLoad, this);
5937 this.store.un('load', this.onLoad, this);
5938 this.store.un('loadexception', this.onLoadException, this);
5940 var um = this.el.getUpdateManager();
5941 um.un('beforeupdate', this.onBeforeLoad, this);
5942 um.un('update', this.onLoad, this);
5943 um.un('failure', this.onLoad, this);
5954 * @class Roo.bootstrap.Table
5955 * @extends Roo.bootstrap.Component
5956 * Bootstrap Table class
5957 * @cfg {String} cls table class
5958 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5959 * @cfg {String} bgcolor Specifies the background color for a table
5960 * @cfg {Number} border Specifies whether the table cells should have borders or not
5961 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5962 * @cfg {Number} cellspacing Specifies the space between cells
5963 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5964 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5965 * @cfg {String} sortable Specifies that the table should be sortable
5966 * @cfg {String} summary Specifies a summary of the content of a table
5967 * @cfg {Number} width Specifies the width of a table
5968 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5970 * @cfg {boolean} striped Should the rows be alternative striped
5971 * @cfg {boolean} bordered Add borders to the table
5972 * @cfg {boolean} hover Add hover highlighting
5973 * @cfg {boolean} condensed Format condensed
5974 * @cfg {boolean} responsive Format condensed
5975 * @cfg {Boolean} loadMask (true|false) default false
5976 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5977 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5978 * @cfg {Boolean} rowSelection (true|false) default false
5979 * @cfg {Boolean} cellSelection (true|false) default false
5980 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5981 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5982 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5983 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5987 * Create a new Table
5988 * @param {Object} config The config object
5991 Roo.bootstrap.Table = function(config){
5992 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5997 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5998 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5999 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6000 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6002 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6004 this.sm.grid = this;
6005 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6006 this.sm = this.selModel;
6007 this.sm.xmodule = this.xmodule || false;
6010 if (this.cm && typeof(this.cm.config) == 'undefined') {
6011 this.colModel = new Roo.grid.ColumnModel(this.cm);
6012 this.cm = this.colModel;
6013 this.cm.xmodule = this.xmodule || false;
6016 this.store= Roo.factory(this.store, Roo.data);
6017 this.ds = this.store;
6018 this.ds.xmodule = this.xmodule || false;
6021 if (this.footer && this.store) {
6022 this.footer.dataSource = this.ds;
6023 this.footer = Roo.factory(this.footer);
6030 * Fires when a cell is clicked
6031 * @param {Roo.bootstrap.Table} this
6032 * @param {Roo.Element} el
6033 * @param {Number} rowIndex
6034 * @param {Number} columnIndex
6035 * @param {Roo.EventObject} e
6039 * @event celldblclick
6040 * Fires when a cell is double clicked
6041 * @param {Roo.bootstrap.Table} this
6042 * @param {Roo.Element} el
6043 * @param {Number} rowIndex
6044 * @param {Number} columnIndex
6045 * @param {Roo.EventObject} e
6047 "celldblclick" : true,
6050 * Fires when a row is clicked
6051 * @param {Roo.bootstrap.Table} this
6052 * @param {Roo.Element} el
6053 * @param {Number} rowIndex
6054 * @param {Roo.EventObject} e
6058 * @event rowdblclick
6059 * Fires when a row is double clicked
6060 * @param {Roo.bootstrap.Table} this
6061 * @param {Roo.Element} el
6062 * @param {Number} rowIndex
6063 * @param {Roo.EventObject} e
6065 "rowdblclick" : true,
6068 * Fires when a mouseover occur
6069 * @param {Roo.bootstrap.Table} this
6070 * @param {Roo.Element} el
6071 * @param {Number} rowIndex
6072 * @param {Number} columnIndex
6073 * @param {Roo.EventObject} e
6078 * Fires when a mouseout occur
6079 * @param {Roo.bootstrap.Table} this
6080 * @param {Roo.Element} el
6081 * @param {Number} rowIndex
6082 * @param {Number} columnIndex
6083 * @param {Roo.EventObject} e
6088 * Fires when a row is rendered, so you can change add a style to it.
6089 * @param {Roo.bootstrap.Table} this
6090 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6094 * @event rowsrendered
6095 * Fires when all the rows have been rendered
6096 * @param {Roo.bootstrap.Table} this
6098 'rowsrendered' : true,
6100 * @event contextmenu
6101 * The raw contextmenu event for the entire grid.
6102 * @param {Roo.EventObject} e
6104 "contextmenu" : true,
6106 * @event rowcontextmenu
6107 * Fires when a row is right clicked
6108 * @param {Roo.bootstrap.Table} this
6109 * @param {Number} rowIndex
6110 * @param {Roo.EventObject} e
6112 "rowcontextmenu" : true,
6114 * @event cellcontextmenu
6115 * Fires when a cell is right clicked
6116 * @param {Roo.bootstrap.Table} this
6117 * @param {Number} rowIndex
6118 * @param {Number} cellIndex
6119 * @param {Roo.EventObject} e
6121 "cellcontextmenu" : true,
6123 * @event headercontextmenu
6124 * Fires when a header is right clicked
6125 * @param {Roo.bootstrap.Table} this
6126 * @param {Number} columnIndex
6127 * @param {Roo.EventObject} e
6129 "headercontextmenu" : true
6133 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6159 rowSelection : false,
6160 cellSelection : false,
6163 // Roo.Element - the tbody
6165 // Roo.Element - thead element
6168 container: false, // used by gridpanel...
6174 auto_hide_footer : false,
6176 getAutoCreate : function()
6178 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6185 if (this.scrollBody) {
6186 cfg.cls += ' table-body-fixed';
6189 cfg.cls += ' table-striped';
6193 cfg.cls += ' table-hover';
6195 if (this.bordered) {
6196 cfg.cls += ' table-bordered';
6198 if (this.condensed) {
6199 cfg.cls += ' table-condensed';
6201 if (this.responsive) {
6202 cfg.cls += ' table-responsive';
6206 cfg.cls+= ' ' +this.cls;
6209 // this lot should be simplifed...
6222 ].forEach(function(k) {
6230 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6233 if(this.store || this.cm){
6234 if(this.headerShow){
6235 cfg.cn.push(this.renderHeader());
6238 cfg.cn.push(this.renderBody());
6240 if(this.footerShow){
6241 cfg.cn.push(this.renderFooter());
6243 // where does this come from?
6244 //cfg.cls+= ' TableGrid';
6247 return { cn : [ cfg ] };
6250 initEvents : function()
6252 if(!this.store || !this.cm){
6255 if (this.selModel) {
6256 this.selModel.initEvents();
6260 //Roo.log('initEvents with ds!!!!');
6262 this.mainBody = this.el.select('tbody', true).first();
6263 this.mainHead = this.el.select('thead', true).first();
6264 this.mainFoot = this.el.select('tfoot', true).first();
6270 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6271 e.on('click', _this.sort, _this);
6274 this.mainBody.on("click", this.onClick, this);
6275 this.mainBody.on("dblclick", this.onDblClick, this);
6277 // why is this done????? = it breaks dialogs??
6278 //this.parent().el.setStyle('position', 'relative');
6282 this.footer.parentId = this.id;
6283 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6286 this.el.select('tfoot tr td').first().addClass('hide');
6291 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6294 this.store.on('load', this.onLoad, this);
6295 this.store.on('beforeload', this.onBeforeLoad, this);
6296 this.store.on('update', this.onUpdate, this);
6297 this.store.on('add', this.onAdd, this);
6298 this.store.on("clear", this.clear, this);
6300 this.el.on("contextmenu", this.onContextMenu, this);
6302 this.mainBody.on('scroll', this.onBodyScroll, this);
6304 this.cm.on("headerchange", this.onHeaderChange, this);
6306 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6310 onContextMenu : function(e, t)
6312 this.processEvent("contextmenu", e);
6315 processEvent : function(name, e)
6317 if (name != 'touchstart' ) {
6318 this.fireEvent(name, e);
6321 var t = e.getTarget();
6323 var cell = Roo.get(t);
6329 if(cell.findParent('tfoot', false, true)){
6333 if(cell.findParent('thead', false, true)){
6335 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6336 cell = Roo.get(t).findParent('th', false, true);
6338 Roo.log("failed to find th in thead?");
6339 Roo.log(e.getTarget());
6344 var cellIndex = cell.dom.cellIndex;
6346 var ename = name == 'touchstart' ? 'click' : name;
6347 this.fireEvent("header" + ename, this, cellIndex, e);
6352 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353 cell = Roo.get(t).findParent('td', false, true);
6355 Roo.log("failed to find th in tbody?");
6356 Roo.log(e.getTarget());
6361 var row = cell.findParent('tr', false, true);
6362 var cellIndex = cell.dom.cellIndex;
6363 var rowIndex = row.dom.rowIndex - 1;
6367 this.fireEvent("row" + name, this, rowIndex, e);
6371 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6377 onMouseover : function(e, el)
6379 var cell = Roo.get(el);
6385 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6386 cell = cell.findParent('td', false, true);
6389 var row = cell.findParent('tr', false, true);
6390 var cellIndex = cell.dom.cellIndex;
6391 var rowIndex = row.dom.rowIndex - 1; // start from 0
6393 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6397 onMouseout : function(e, el)
6399 var cell = Roo.get(el);
6405 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406 cell = cell.findParent('td', false, true);
6409 var row = cell.findParent('tr', false, true);
6410 var cellIndex = cell.dom.cellIndex;
6411 var rowIndex = row.dom.rowIndex - 1; // start from 0
6413 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6417 onClick : function(e, el)
6419 var cell = Roo.get(el);
6421 if(!cell || (!this.cellSelection && !this.rowSelection)){
6425 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6426 cell = cell.findParent('td', false, true);
6429 if(!cell || typeof(cell) == 'undefined'){
6433 var row = cell.findParent('tr', false, true);
6435 if(!row || typeof(row) == 'undefined'){
6439 var cellIndex = cell.dom.cellIndex;
6440 var rowIndex = this.getRowIndex(row);
6442 // why??? - should these not be based on SelectionModel?
6443 if(this.cellSelection){
6444 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6447 if(this.rowSelection){
6448 this.fireEvent('rowclick', this, row, rowIndex, e);
6454 onDblClick : function(e,el)
6456 var cell = Roo.get(el);
6458 if(!cell || (!this.cellSelection && !this.rowSelection)){
6462 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6463 cell = cell.findParent('td', false, true);
6466 if(!cell || typeof(cell) == 'undefined'){
6470 var row = cell.findParent('tr', false, true);
6472 if(!row || typeof(row) == 'undefined'){
6476 var cellIndex = cell.dom.cellIndex;
6477 var rowIndex = this.getRowIndex(row);
6479 if(this.cellSelection){
6480 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6483 if(this.rowSelection){
6484 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6488 sort : function(e,el)
6490 var col = Roo.get(el);
6492 if(!col.hasClass('sortable')){
6496 var sort = col.attr('sort');
6499 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6503 this.store.sortInfo = {field : sort, direction : dir};
6506 Roo.log("calling footer first");
6507 this.footer.onClick('first');
6510 this.store.load({ params : { start : 0 } });
6514 renderHeader : function()
6522 this.totalWidth = 0;
6524 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6526 var config = cm.config[i];
6530 cls : 'x-hcol-' + i,
6532 html: cm.getColumnHeader(i)
6537 if(typeof(config.sortable) != 'undefined' && config.sortable){
6539 c.html = '<i class="glyphicon"></i>' + c.html;
6542 if(typeof(config.lgHeader) != 'undefined'){
6543 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6546 if(typeof(config.mdHeader) != 'undefined'){
6547 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6550 if(typeof(config.smHeader) != 'undefined'){
6551 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6554 if(typeof(config.xsHeader) != 'undefined'){
6555 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6562 if(typeof(config.tooltip) != 'undefined'){
6563 c.tooltip = config.tooltip;
6566 if(typeof(config.colspan) != 'undefined'){
6567 c.colspan = config.colspan;
6570 if(typeof(config.hidden) != 'undefined' && config.hidden){
6571 c.style += ' display:none;';
6574 if(typeof(config.dataIndex) != 'undefined'){
6575 c.sort = config.dataIndex;
6580 if(typeof(config.align) != 'undefined' && config.align.length){
6581 c.style += ' text-align:' + config.align + ';';
6584 if(typeof(config.width) != 'undefined'){
6585 c.style += ' width:' + config.width + 'px;';
6586 this.totalWidth += config.width;
6588 this.totalWidth += 100; // assume minimum of 100 per column?
6591 if(typeof(config.cls) != 'undefined'){
6592 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6595 ['xs','sm','md','lg'].map(function(size){
6597 if(typeof(config[size]) == 'undefined'){
6601 if (!config[size]) { // 0 = hidden
6602 c.cls += ' hidden-' + size;
6606 c.cls += ' col-' + size + '-' + config[size];
6616 renderBody : function()
6626 colspan : this.cm.getColumnCount()
6636 renderFooter : function()
6646 colspan : this.cm.getColumnCount()
6660 // Roo.log('ds onload');
6665 var ds = this.store;
6667 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6668 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6669 if (_this.store.sortInfo) {
6671 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6672 e.select('i', true).addClass(['glyphicon-arrow-up']);
6675 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6676 e.select('i', true).addClass(['glyphicon-arrow-down']);
6681 var tbody = this.mainBody;
6683 if(ds.getCount() > 0){
6684 ds.data.each(function(d,rowIndex){
6685 var row = this.renderRow(cm, ds, rowIndex);
6687 tbody.createChild(row);
6691 if(row.cellObjects.length){
6692 Roo.each(row.cellObjects, function(r){
6693 _this.renderCellObject(r);
6700 var tfoot = this.el.select('tfoot', true).first();
6702 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6704 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6706 var total = this.ds.getTotalCount();
6708 if(this.footer.pageSize < total){
6709 this.mainFoot.show();
6713 Roo.each(this.el.select('tbody td', true).elements, function(e){
6714 e.on('mouseover', _this.onMouseover, _this);
6717 Roo.each(this.el.select('tbody td', true).elements, function(e){
6718 e.on('mouseout', _this.onMouseout, _this);
6720 this.fireEvent('rowsrendered', this);
6726 onUpdate : function(ds,record)
6728 this.refreshRow(record);
6732 onRemove : function(ds, record, index, isUpdate){
6733 if(isUpdate !== true){
6734 this.fireEvent("beforerowremoved", this, index, record);
6736 var bt = this.mainBody.dom;
6738 var rows = this.el.select('tbody > tr', true).elements;
6740 if(typeof(rows[index]) != 'undefined'){
6741 bt.removeChild(rows[index].dom);
6744 // if(bt.rows[index]){
6745 // bt.removeChild(bt.rows[index]);
6748 if(isUpdate !== true){
6749 //this.stripeRows(index);
6750 //this.syncRowHeights(index, index);
6752 this.fireEvent("rowremoved", this, index, record);
6756 onAdd : function(ds, records, rowIndex)
6758 //Roo.log('on Add called');
6759 // - note this does not handle multiple adding very well..
6760 var bt = this.mainBody.dom;
6761 for (var i =0 ; i < records.length;i++) {
6762 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6763 //Roo.log(records[i]);
6764 //Roo.log(this.store.getAt(rowIndex+i));
6765 this.insertRow(this.store, rowIndex + i, false);
6772 refreshRow : function(record){
6773 var ds = this.store, index;
6774 if(typeof record == 'number'){
6776 record = ds.getAt(index);
6778 index = ds.indexOf(record);
6780 this.insertRow(ds, index, true);
6782 this.onRemove(ds, record, index+1, true);
6784 //this.syncRowHeights(index, index);
6786 this.fireEvent("rowupdated", this, index, record);
6789 insertRow : function(dm, rowIndex, isUpdate){
6792 this.fireEvent("beforerowsinserted", this, rowIndex);
6794 //var s = this.getScrollState();
6795 var row = this.renderRow(this.cm, this.store, rowIndex);
6796 // insert before rowIndex..
6797 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6801 if(row.cellObjects.length){
6802 Roo.each(row.cellObjects, function(r){
6803 _this.renderCellObject(r);
6808 this.fireEvent("rowsinserted", this, rowIndex);
6809 //this.syncRowHeights(firstRow, lastRow);
6810 //this.stripeRows(firstRow);
6817 getRowDom : function(rowIndex)
6819 var rows = this.el.select('tbody > tr', true).elements;
6821 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6824 // returns the object tree for a tr..
6827 renderRow : function(cm, ds, rowIndex)
6829 var d = ds.getAt(rowIndex);
6833 cls : 'x-row-' + rowIndex,
6837 var cellObjects = [];
6839 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6840 var config = cm.config[i];
6842 var renderer = cm.getRenderer(i);
6846 if(typeof(renderer) !== 'undefined'){
6847 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6849 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6850 // and are rendered into the cells after the row is rendered - using the id for the element.
6852 if(typeof(value) === 'object'){
6862 rowIndex : rowIndex,
6867 this.fireEvent('rowclass', this, rowcfg);
6871 cls : rowcfg.rowClass + ' x-col-' + i,
6873 html: (typeof(value) === 'object') ? '' : value
6880 if(typeof(config.colspan) != 'undefined'){
6881 td.colspan = config.colspan;
6884 if(typeof(config.hidden) != 'undefined' && config.hidden){
6885 td.style += ' display:none;';
6888 if(typeof(config.align) != 'undefined' && config.align.length){
6889 td.style += ' text-align:' + config.align + ';';
6891 if(typeof(config.valign) != 'undefined' && config.valign.length){
6892 td.style += ' vertical-align:' + config.valign + ';';
6895 if(typeof(config.width) != 'undefined'){
6896 td.style += ' width:' + config.width + 'px;';
6899 if(typeof(config.cursor) != 'undefined'){
6900 td.style += ' cursor:' + config.cursor + ';';
6903 if(typeof(config.cls) != 'undefined'){
6904 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6907 ['xs','sm','md','lg'].map(function(size){
6909 if(typeof(config[size]) == 'undefined'){
6913 if (!config[size]) { // 0 = hidden
6914 td.cls += ' hidden-' + size;
6918 td.cls += ' col-' + size + '-' + config[size];
6926 row.cellObjects = cellObjects;
6934 onBeforeLoad : function()
6943 this.el.select('tbody', true).first().dom.innerHTML = '';
6946 * Show or hide a row.
6947 * @param {Number} rowIndex to show or hide
6948 * @param {Boolean} state hide
6950 setRowVisibility : function(rowIndex, state)
6952 var bt = this.mainBody.dom;
6954 var rows = this.el.select('tbody > tr', true).elements;
6956 if(typeof(rows[rowIndex]) == 'undefined'){
6959 rows[rowIndex].dom.style.display = state ? '' : 'none';
6963 getSelectionModel : function(){
6965 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6967 return this.selModel;
6970 * Render the Roo.bootstrap object from renderder
6972 renderCellObject : function(r)
6976 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6978 var t = r.cfg.render(r.container);
6981 Roo.each(r.cfg.cn, function(c){
6983 container: t.getChildContainer(),
6986 _this.renderCellObject(child);
6991 getRowIndex : function(row)
6995 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7006 * Returns the grid's underlying element = used by panel.Grid
7007 * @return {Element} The element
7009 getGridEl : function(){
7013 * Forces a resize - used by panel.Grid
7014 * @return {Element} The element
7016 autoSize : function()
7018 //var ctr = Roo.get(this.container.dom.parentElement);
7019 var ctr = Roo.get(this.el.dom);
7021 var thd = this.getGridEl().select('thead',true).first();
7022 var tbd = this.getGridEl().select('tbody', true).first();
7023 var tfd = this.getGridEl().select('tfoot', true).first();
7025 var cw = ctr.getWidth();
7029 tbd.setSize(ctr.getWidth(),
7030 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7032 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7035 cw = Math.max(cw, this.totalWidth);
7036 this.getGridEl().select('tr',true).setWidth(cw);
7037 // resize 'expandable coloumn?
7039 return; // we doe not have a view in this design..
7042 onBodyScroll: function()
7044 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7046 this.mainHead.setStyle({
7047 'position' : 'relative',
7048 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7054 var scrollHeight = this.mainBody.dom.scrollHeight;
7056 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7058 var height = this.mainBody.getHeight();
7060 if(scrollHeight - height == scrollTop) {
7062 var total = this.ds.getTotalCount();
7064 if(this.footer.cursor + this.footer.pageSize < total){
7066 this.footer.ds.load({
7068 start : this.footer.cursor + this.footer.pageSize,
7069 limit : this.footer.pageSize
7079 onHeaderChange : function()
7081 var header = this.renderHeader();
7082 var table = this.el.select('table', true).first();
7084 this.mainHead.remove();
7085 this.mainHead = table.createChild(header, this.mainBody, false);
7088 onHiddenChange : function(colModel, colIndex, hidden)
7090 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7091 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7093 this.CSS.updateRule(thSelector, "display", "");
7094 this.CSS.updateRule(tdSelector, "display", "");
7097 this.CSS.updateRule(thSelector, "display", "none");
7098 this.CSS.updateRule(tdSelector, "display", "none");
7101 this.onHeaderChange();
7118 * @class Roo.bootstrap.TableCell
7119 * @extends Roo.bootstrap.Component
7120 * Bootstrap TableCell class
7121 * @cfg {String} html cell contain text
7122 * @cfg {String} cls cell class
7123 * @cfg {String} tag cell tag (td|th) default td
7124 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7125 * @cfg {String} align Aligns the content in a cell
7126 * @cfg {String} axis Categorizes cells
7127 * @cfg {String} bgcolor Specifies the background color of a cell
7128 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7129 * @cfg {Number} colspan Specifies the number of columns a cell should span
7130 * @cfg {String} headers Specifies one or more header cells a cell is related to
7131 * @cfg {Number} height Sets the height of a cell
7132 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7133 * @cfg {Number} rowspan Sets the number of rows a cell should span
7134 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7135 * @cfg {String} valign Vertical aligns the content in a cell
7136 * @cfg {Number} width Specifies the width of a cell
7139 * Create a new TableCell
7140 * @param {Object} config The config object
7143 Roo.bootstrap.TableCell = function(config){
7144 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7147 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7167 getAutoCreate : function(){
7168 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7188 cfg.align=this.align
7194 cfg.bgcolor=this.bgcolor
7197 cfg.charoff=this.charoff
7200 cfg.colspan=this.colspan
7203 cfg.headers=this.headers
7206 cfg.height=this.height
7209 cfg.nowrap=this.nowrap
7212 cfg.rowspan=this.rowspan
7215 cfg.scope=this.scope
7218 cfg.valign=this.valign
7221 cfg.width=this.width
7240 * @class Roo.bootstrap.TableRow
7241 * @extends Roo.bootstrap.Component
7242 * Bootstrap TableRow class
7243 * @cfg {String} cls row class
7244 * @cfg {String} align Aligns the content in a table row
7245 * @cfg {String} bgcolor Specifies a background color for a table row
7246 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7247 * @cfg {String} valign Vertical aligns the content in a table row
7250 * Create a new TableRow
7251 * @param {Object} config The config object
7254 Roo.bootstrap.TableRow = function(config){
7255 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7258 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7266 getAutoCreate : function(){
7267 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7277 cfg.align = this.align;
7280 cfg.bgcolor = this.bgcolor;
7283 cfg.charoff = this.charoff;
7286 cfg.valign = this.valign;
7304 * @class Roo.bootstrap.TableBody
7305 * @extends Roo.bootstrap.Component
7306 * Bootstrap TableBody class
7307 * @cfg {String} cls element class
7308 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7309 * @cfg {String} align Aligns the content inside the element
7310 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7311 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7314 * Create a new TableBody
7315 * @param {Object} config The config object
7318 Roo.bootstrap.TableBody = function(config){
7319 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7322 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7330 getAutoCreate : function(){
7331 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7345 cfg.align = this.align;
7348 cfg.charoff = this.charoff;
7351 cfg.valign = this.valign;
7358 // initEvents : function()
7365 // this.store = Roo.factory(this.store, Roo.data);
7366 // this.store.on('load', this.onLoad, this);
7368 // this.store.load();
7372 // onLoad: function ()
7374 // this.fireEvent('load', this);
7384 * Ext JS Library 1.1.1
7385 * Copyright(c) 2006-2007, Ext JS, LLC.
7387 * Originally Released Under LGPL - original licence link has changed is not relivant.
7390 * <script type="text/javascript">
7393 // as we use this in bootstrap.
7394 Roo.namespace('Roo.form');
7396 * @class Roo.form.Action
7397 * Internal Class used to handle form actions
7399 * @param {Roo.form.BasicForm} el The form element or its id
7400 * @param {Object} config Configuration options
7405 // define the action interface
7406 Roo.form.Action = function(form, options){
7408 this.options = options || {};
7411 * Client Validation Failed
7414 Roo.form.Action.CLIENT_INVALID = 'client';
7416 * Server Validation Failed
7419 Roo.form.Action.SERVER_INVALID = 'server';
7421 * Connect to Server Failed
7424 Roo.form.Action.CONNECT_FAILURE = 'connect';
7426 * Reading Data from Server Failed
7429 Roo.form.Action.LOAD_FAILURE = 'load';
7431 Roo.form.Action.prototype = {
7433 failureType : undefined,
7434 response : undefined,
7438 run : function(options){
7443 success : function(response){
7448 handleResponse : function(response){
7452 // default connection failure
7453 failure : function(response){
7455 this.response = response;
7456 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7457 this.form.afterAction(this, false);
7460 processResponse : function(response){
7461 this.response = response;
7462 if(!response.responseText){
7465 this.result = this.handleResponse(response);
7469 // utility functions used internally
7470 getUrl : function(appendParams){
7471 var url = this.options.url || this.form.url || this.form.el.dom.action;
7473 var p = this.getParams();
7475 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7481 getMethod : function(){
7482 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7485 getParams : function(){
7486 var bp = this.form.baseParams;
7487 var p = this.options.params;
7489 if(typeof p == "object"){
7490 p = Roo.urlEncode(Roo.applyIf(p, bp));
7491 }else if(typeof p == 'string' && bp){
7492 p += '&' + Roo.urlEncode(bp);
7495 p = Roo.urlEncode(bp);
7500 createCallback : function(){
7502 success: this.success,
7503 failure: this.failure,
7505 timeout: (this.form.timeout*1000),
7506 upload: this.form.fileUpload ? this.success : undefined
7511 Roo.form.Action.Submit = function(form, options){
7512 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7515 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7518 haveProgress : false,
7519 uploadComplete : false,
7521 // uploadProgress indicator.
7522 uploadProgress : function()
7524 if (!this.form.progressUrl) {
7528 if (!this.haveProgress) {
7529 Roo.MessageBox.progress("Uploading", "Uploading");
7531 if (this.uploadComplete) {
7532 Roo.MessageBox.hide();
7536 this.haveProgress = true;
7538 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7540 var c = new Roo.data.Connection();
7542 url : this.form.progressUrl,
7547 success : function(req){
7548 //console.log(data);
7552 rdata = Roo.decode(req.responseText)
7554 Roo.log("Invalid data from server..");
7558 if (!rdata || !rdata.success) {
7560 Roo.MessageBox.alert(Roo.encode(rdata));
7563 var data = rdata.data;
7565 if (this.uploadComplete) {
7566 Roo.MessageBox.hide();
7571 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7572 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7575 this.uploadProgress.defer(2000,this);
7578 failure: function(data) {
7579 Roo.log('progress url failed ');
7590 // run get Values on the form, so it syncs any secondary forms.
7591 this.form.getValues();
7593 var o = this.options;
7594 var method = this.getMethod();
7595 var isPost = method == 'POST';
7596 if(o.clientValidation === false || this.form.isValid()){
7598 if (this.form.progressUrl) {
7599 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7600 (new Date() * 1) + '' + Math.random());
7605 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7606 form:this.form.el.dom,
7607 url:this.getUrl(!isPost),
7609 params:isPost ? this.getParams() : null,
7610 isUpload: this.form.fileUpload
7613 this.uploadProgress();
7615 }else if (o.clientValidation !== false){ // client validation failed
7616 this.failureType = Roo.form.Action.CLIENT_INVALID;
7617 this.form.afterAction(this, false);
7621 success : function(response)
7623 this.uploadComplete= true;
7624 if (this.haveProgress) {
7625 Roo.MessageBox.hide();
7629 var result = this.processResponse(response);
7630 if(result === true || result.success){
7631 this.form.afterAction(this, true);
7635 this.form.markInvalid(result.errors);
7636 this.failureType = Roo.form.Action.SERVER_INVALID;
7638 this.form.afterAction(this, false);
7640 failure : function(response)
7642 this.uploadComplete= true;
7643 if (this.haveProgress) {
7644 Roo.MessageBox.hide();
7647 this.response = response;
7648 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7649 this.form.afterAction(this, false);
7652 handleResponse : function(response){
7653 if(this.form.errorReader){
7654 var rs = this.form.errorReader.read(response);
7657 for(var i = 0, len = rs.records.length; i < len; i++) {
7658 var r = rs.records[i];
7662 if(errors.length < 1){
7666 success : rs.success,
7672 ret = Roo.decode(response.responseText);
7676 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7686 Roo.form.Action.Load = function(form, options){
7687 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7688 this.reader = this.form.reader;
7691 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7696 Roo.Ajax.request(Roo.apply(
7697 this.createCallback(), {
7698 method:this.getMethod(),
7699 url:this.getUrl(false),
7700 params:this.getParams()
7704 success : function(response){
7706 var result = this.processResponse(response);
7707 if(result === true || !result.success || !result.data){
7708 this.failureType = Roo.form.Action.LOAD_FAILURE;
7709 this.form.afterAction(this, false);
7712 this.form.clearInvalid();
7713 this.form.setValues(result.data);
7714 this.form.afterAction(this, true);
7717 handleResponse : function(response){
7718 if(this.form.reader){
7719 var rs = this.form.reader.read(response);
7720 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7722 success : rs.success,
7726 return Roo.decode(response.responseText);
7730 Roo.form.Action.ACTION_TYPES = {
7731 'load' : Roo.form.Action.Load,
7732 'submit' : Roo.form.Action.Submit
7741 * @class Roo.bootstrap.Form
7742 * @extends Roo.bootstrap.Component
7743 * Bootstrap Form class
7744 * @cfg {String} method GET | POST (default POST)
7745 * @cfg {String} labelAlign top | left (default top)
7746 * @cfg {String} align left | right - for navbars
7747 * @cfg {Boolean} loadMask load mask when submit (default true)
7752 * @param {Object} config The config object
7756 Roo.bootstrap.Form = function(config){
7758 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7760 Roo.bootstrap.Form.popover.apply();
7764 * @event clientvalidation
7765 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7766 * @param {Form} this
7767 * @param {Boolean} valid true if the form has passed client-side validation
7769 clientvalidation: true,
7771 * @event beforeaction
7772 * Fires before any action is performed. Return false to cancel the action.
7773 * @param {Form} this
7774 * @param {Action} action The action to be performed
7778 * @event actionfailed
7779 * Fires when an action fails.
7780 * @param {Form} this
7781 * @param {Action} action The action that failed
7783 actionfailed : true,
7785 * @event actioncomplete
7786 * Fires when an action is completed.
7787 * @param {Form} this
7788 * @param {Action} action The action that completed
7790 actioncomplete : true
7794 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7797 * @cfg {String} method
7798 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7803 * The URL to use for form actions if one isn't supplied in the action options.
7806 * @cfg {Boolean} fileUpload
7807 * Set to true if this form is a file upload.
7811 * @cfg {Object} baseParams
7812 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7816 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7820 * @cfg {Sting} align (left|right) for navbar forms
7825 activeAction : null,
7828 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7829 * element by passing it or its id or mask the form itself by passing in true.
7832 waitMsgTarget : false,
7837 * @cfg {Boolean} errorMask (true|false) default false
7842 * @cfg {Number} maskOffset Default 100
7847 * @cfg {Boolean} maskBody
7851 getAutoCreate : function(){
7855 method : this.method || 'POST',
7856 id : this.id || Roo.id(),
7859 if (this.parent().xtype.match(/^Nav/)) {
7860 cfg.cls = 'navbar-form navbar-' + this.align;
7864 if (this.labelAlign == 'left' ) {
7865 cfg.cls += ' form-horizontal';
7871 initEvents : function()
7873 this.el.on('submit', this.onSubmit, this);
7874 // this was added as random key presses on the form where triggering form submit.
7875 this.el.on('keypress', function(e) {
7876 if (e.getCharCode() != 13) {
7879 // we might need to allow it for textareas.. and some other items.
7880 // check e.getTarget().
7882 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7886 Roo.log("keypress blocked");
7894 onSubmit : function(e){
7899 * Returns true if client-side validation on the form is successful.
7902 isValid : function(){
7903 var items = this.getItems();
7907 items.each(function(f){
7913 Roo.log('invalid field: ' + f.name);
7917 if(!target && f.el.isVisible(true)){
7923 if(this.errorMask && !valid){
7924 Roo.bootstrap.Form.popover.mask(this, target);
7931 * Returns true if any fields in this form have changed since their original load.
7934 isDirty : function(){
7936 var items = this.getItems();
7937 items.each(function(f){
7947 * Performs a predefined action (submit or load) or custom actions you define on this form.
7948 * @param {String} actionName The name of the action type
7949 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7950 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7951 * accept other config options):
7953 Property Type Description
7954 ---------------- --------------- ----------------------------------------------------------------------------------
7955 url String The url for the action (defaults to the form's url)
7956 method String The form method to use (defaults to the form's method, or POST if not defined)
7957 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7958 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7959 validate the form on the client (defaults to false)
7961 * @return {BasicForm} this
7963 doAction : function(action, options){
7964 if(typeof action == 'string'){
7965 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7967 if(this.fireEvent('beforeaction', this, action) !== false){
7968 this.beforeAction(action);
7969 action.run.defer(100, action);
7975 beforeAction : function(action){
7976 var o = action.options;
7981 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7983 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7986 // not really supported yet.. ??
7988 //if(this.waitMsgTarget === true){
7989 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7990 //}else if(this.waitMsgTarget){
7991 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7992 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7994 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8000 afterAction : function(action, success){
8001 this.activeAction = null;
8002 var o = action.options;
8007 Roo.get(document.body).unmask();
8013 //if(this.waitMsgTarget === true){
8014 // this.el.unmask();
8015 //}else if(this.waitMsgTarget){
8016 // this.waitMsgTarget.unmask();
8018 // Roo.MessageBox.updateProgress(1);
8019 // Roo.MessageBox.hide();
8026 Roo.callback(o.success, o.scope, [this, action]);
8027 this.fireEvent('actioncomplete', this, action);
8031 // failure condition..
8032 // we have a scenario where updates need confirming.
8033 // eg. if a locking scenario exists..
8034 // we look for { errors : { needs_confirm : true }} in the response.
8036 (typeof(action.result) != 'undefined') &&
8037 (typeof(action.result.errors) != 'undefined') &&
8038 (typeof(action.result.errors.needs_confirm) != 'undefined')
8041 Roo.log("not supported yet");
8044 Roo.MessageBox.confirm(
8045 "Change requires confirmation",
8046 action.result.errorMsg,
8051 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8061 Roo.callback(o.failure, o.scope, [this, action]);
8062 // show an error message if no failed handler is set..
8063 if (!this.hasListener('actionfailed')) {
8064 Roo.log("need to add dialog support");
8066 Roo.MessageBox.alert("Error",
8067 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8068 action.result.errorMsg :
8069 "Saving Failed, please check your entries or try again"
8074 this.fireEvent('actionfailed', this, action);
8079 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8080 * @param {String} id The value to search for
8083 findField : function(id){
8084 var items = this.getItems();
8085 var field = items.get(id);
8087 items.each(function(f){
8088 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8095 return field || null;
8098 * Mark fields in this form invalid in bulk.
8099 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8100 * @return {BasicForm} this
8102 markInvalid : function(errors){
8103 if(errors instanceof Array){
8104 for(var i = 0, len = errors.length; i < len; i++){
8105 var fieldError = errors[i];
8106 var f = this.findField(fieldError.id);
8108 f.markInvalid(fieldError.msg);
8114 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8115 field.markInvalid(errors[id]);
8119 //Roo.each(this.childForms || [], function (f) {
8120 // f.markInvalid(errors);
8127 * Set values for fields in this form in bulk.
8128 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8129 * @return {BasicForm} this
8131 setValues : function(values){
8132 if(values instanceof Array){ // array of objects
8133 for(var i = 0, len = values.length; i < len; i++){
8135 var f = this.findField(v.id);
8137 f.setValue(v.value);
8138 if(this.trackResetOnLoad){
8139 f.originalValue = f.getValue();
8143 }else{ // object hash
8146 if(typeof values[id] != 'function' && (field = this.findField(id))){
8148 if (field.setFromData &&
8150 field.displayField &&
8151 // combos' with local stores can
8152 // be queried via setValue()
8153 // to set their value..
8154 (field.store && !field.store.isLocal)
8158 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8159 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8160 field.setFromData(sd);
8162 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8164 field.setFromData(values);
8167 field.setValue(values[id]);
8171 if(this.trackResetOnLoad){
8172 field.originalValue = field.getValue();
8178 //Roo.each(this.childForms || [], function (f) {
8179 // f.setValues(values);
8186 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8187 * they are returned as an array.
8188 * @param {Boolean} asString
8191 getValues : function(asString){
8192 //if (this.childForms) {
8193 // copy values from the child forms
8194 // Roo.each(this.childForms, function (f) {
8195 // this.setValues(f.getValues());
8201 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8202 if(asString === true){
8205 return Roo.urlDecode(fs);
8209 * Returns the fields in this form as an object with key/value pairs.
8210 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8213 getFieldValues : function(with_hidden)
8215 var items = this.getItems();
8217 items.each(function(f){
8223 var v = f.getValue();
8225 if (f.inputType =='radio') {
8226 if (typeof(ret[f.getName()]) == 'undefined') {
8227 ret[f.getName()] = ''; // empty..
8230 if (!f.el.dom.checked) {
8238 if(f.xtype == 'MoneyField'){
8239 ret[f.currencyName] = f.getCurrency();
8242 // not sure if this supported any more..
8243 if ((typeof(v) == 'object') && f.getRawValue) {
8244 v = f.getRawValue() ; // dates..
8246 // combo boxes where name != hiddenName...
8247 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8248 ret[f.name] = f.getRawValue();
8250 ret[f.getName()] = v;
8257 * Clears all invalid messages in this form.
8258 * @return {BasicForm} this
8260 clearInvalid : function(){
8261 var items = this.getItems();
8263 items.each(function(f){
8272 * @return {BasicForm} this
8275 var items = this.getItems();
8276 items.each(function(f){
8280 Roo.each(this.childForms || [], function (f) {
8288 getItems : function()
8290 var r=new Roo.util.MixedCollection(false, function(o){
8291 return o.id || (o.id = Roo.id());
8293 var iter = function(el) {
8300 Roo.each(el.items,function(e) {
8309 hideFields : function(items)
8311 Roo.each(items, function(i){
8313 var f = this.findField(i);
8319 if(f.xtype == 'DateField'){
8320 f.setVisible(false);
8329 showFields : function(items)
8331 Roo.each(items, function(i){
8333 var f = this.findField(i);
8339 if(f.xtype == 'DateField'){
8351 Roo.apply(Roo.bootstrap.Form, {
8378 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8379 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8380 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8381 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8384 this.maskEl.top.enableDisplayMode("block");
8385 this.maskEl.left.enableDisplayMode("block");
8386 this.maskEl.bottom.enableDisplayMode("block");
8387 this.maskEl.right.enableDisplayMode("block");
8389 this.toolTip = new Roo.bootstrap.Tooltip({
8390 cls : 'roo-form-error-popover',
8392 'left' : ['r-l', [-2,0], 'right'],
8393 'right' : ['l-r', [2,0], 'left'],
8394 'bottom' : ['tl-bl', [0,2], 'top'],
8395 'top' : [ 'bl-tl', [0,-2], 'bottom']
8399 this.toolTip.render(Roo.get(document.body));
8401 this.toolTip.el.enableDisplayMode("block");
8403 Roo.get(document.body).on('click', function(){
8407 Roo.get(document.body).on('touchstart', function(){
8411 this.isApplied = true
8414 mask : function(form, target)
8418 this.target = target;
8420 if(!this.form.errorMask || !target.el){
8424 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8426 Roo.log(scrollable);
8428 var ot = this.target.el.calcOffsetsTo(scrollable);
8430 var scrollTo = ot[1] - this.form.maskOffset;
8432 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8434 scrollable.scrollTo('top', scrollTo);
8436 var box = this.target.el.getBox();
8438 var zIndex = Roo.bootstrap.Modal.zIndex++;
8441 this.maskEl.top.setStyle('position', 'absolute');
8442 this.maskEl.top.setStyle('z-index', zIndex);
8443 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8444 this.maskEl.top.setLeft(0);
8445 this.maskEl.top.setTop(0);
8446 this.maskEl.top.show();
8448 this.maskEl.left.setStyle('position', 'absolute');
8449 this.maskEl.left.setStyle('z-index', zIndex);
8450 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8451 this.maskEl.left.setLeft(0);
8452 this.maskEl.left.setTop(box.y - this.padding);
8453 this.maskEl.left.show();
8455 this.maskEl.bottom.setStyle('position', 'absolute');
8456 this.maskEl.bottom.setStyle('z-index', zIndex);
8457 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8458 this.maskEl.bottom.setLeft(0);
8459 this.maskEl.bottom.setTop(box.bottom + this.padding);
8460 this.maskEl.bottom.show();
8462 this.maskEl.right.setStyle('position', 'absolute');
8463 this.maskEl.right.setStyle('z-index', zIndex);
8464 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8465 this.maskEl.right.setLeft(box.right + this.padding);
8466 this.maskEl.right.setTop(box.y - this.padding);
8467 this.maskEl.right.show();
8469 this.toolTip.bindEl = this.target.el;
8471 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8473 var tip = this.target.blankText;
8475 if(this.target.getValue() !== '' ) {
8477 if (this.target.invalidText.length) {
8478 tip = this.target.invalidText;
8479 } else if (this.target.regexText.length){
8480 tip = this.target.regexText;
8484 this.toolTip.show(tip);
8486 this.intervalID = window.setInterval(function() {
8487 Roo.bootstrap.Form.popover.unmask();
8490 window.onwheel = function(){ return false;};
8492 (function(){ this.isMasked = true; }).defer(500, this);
8498 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8502 this.maskEl.top.setStyle('position', 'absolute');
8503 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8504 this.maskEl.top.hide();
8506 this.maskEl.left.setStyle('position', 'absolute');
8507 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8508 this.maskEl.left.hide();
8510 this.maskEl.bottom.setStyle('position', 'absolute');
8511 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8512 this.maskEl.bottom.hide();
8514 this.maskEl.right.setStyle('position', 'absolute');
8515 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8516 this.maskEl.right.hide();
8518 this.toolTip.hide();
8520 this.toolTip.el.hide();
8522 window.onwheel = function(){ return true;};
8524 if(this.intervalID){
8525 window.clearInterval(this.intervalID);
8526 this.intervalID = false;
8529 this.isMasked = false;
8539 * Ext JS Library 1.1.1
8540 * Copyright(c) 2006-2007, Ext JS, LLC.
8542 * Originally Released Under LGPL - original licence link has changed is not relivant.
8545 * <script type="text/javascript">
8548 * @class Roo.form.VTypes
8549 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8552 Roo.form.VTypes = function(){
8553 // closure these in so they are only created once.
8554 var alpha = /^[a-zA-Z_]+$/;
8555 var alphanum = /^[a-zA-Z0-9_]+$/;
8556 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8557 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8559 // All these messages and functions are configurable
8562 * The function used to validate email addresses
8563 * @param {String} value The email address
8565 'email' : function(v){
8566 return email.test(v);
8569 * The error text to display when the email validation function returns false
8572 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8574 * The keystroke filter mask to be applied on email input
8577 'emailMask' : /[a-z0-9_\.\-@]/i,
8580 * The function used to validate URLs
8581 * @param {String} value The URL
8583 'url' : function(v){
8587 * The error text to display when the url validation function returns false
8590 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8593 * The function used to validate alpha values
8594 * @param {String} value The value
8596 'alpha' : function(v){
8597 return alpha.test(v);
8600 * The error text to display when the alpha validation function returns false
8603 'alphaText' : 'This field should only contain letters and _',
8605 * The keystroke filter mask to be applied on alpha input
8608 'alphaMask' : /[a-z_]/i,
8611 * The function used to validate alphanumeric values
8612 * @param {String} value The value
8614 'alphanum' : function(v){
8615 return alphanum.test(v);
8618 * The error text to display when the alphanumeric validation function returns false
8621 'alphanumText' : 'This field should only contain letters, numbers and _',
8623 * The keystroke filter mask to be applied on alphanumeric input
8626 'alphanumMask' : /[a-z0-9_]/i
8636 * @class Roo.bootstrap.Input
8637 * @extends Roo.bootstrap.Component
8638 * Bootstrap Input class
8639 * @cfg {Boolean} disabled is it disabled
8640 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8641 * @cfg {String} name name of the input
8642 * @cfg {string} fieldLabel - the label associated
8643 * @cfg {string} placeholder - placeholder to put in text.
8644 * @cfg {string} before - input group add on before
8645 * @cfg {string} after - input group add on after
8646 * @cfg {string} size - (lg|sm) or leave empty..
8647 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8648 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8649 * @cfg {Number} md colspan out of 12 for computer-sized screens
8650 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8651 * @cfg {string} value default value of the input
8652 * @cfg {Number} labelWidth set the width of label
8653 * @cfg {Number} labellg set the width of label (1-12)
8654 * @cfg {Number} labelmd set the width of label (1-12)
8655 * @cfg {Number} labelsm set the width of label (1-12)
8656 * @cfg {Number} labelxs set the width of label (1-12)
8657 * @cfg {String} labelAlign (top|left)
8658 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8659 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8660 * @cfg {String} indicatorpos (left|right) default left
8661 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8662 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8664 * @cfg {String} align (left|center|right) Default left
8665 * @cfg {Boolean} forceFeedback (true|false) Default false
8668 * Create a new Input
8669 * @param {Object} config The config object
8672 Roo.bootstrap.Input = function(config){
8674 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8679 * Fires when this field receives input focus.
8680 * @param {Roo.form.Field} this
8685 * Fires when this field loses input focus.
8686 * @param {Roo.form.Field} this
8691 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8692 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8693 * @param {Roo.form.Field} this
8694 * @param {Roo.EventObject} e The event object
8699 * Fires just before the field blurs if the field value has changed.
8700 * @param {Roo.form.Field} this
8701 * @param {Mixed} newValue The new value
8702 * @param {Mixed} oldValue The original value
8707 * Fires after the field has been marked as invalid.
8708 * @param {Roo.form.Field} this
8709 * @param {String} msg The validation message
8714 * Fires after the field has been validated with no errors.
8715 * @param {Roo.form.Field} this
8720 * Fires after the key up
8721 * @param {Roo.form.Field} this
8722 * @param {Roo.EventObject} e The event Object
8728 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8730 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8731 automatic validation (defaults to "keyup").
8733 validationEvent : "keyup",
8735 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8737 validateOnBlur : true,
8739 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8741 validationDelay : 250,
8743 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8745 focusClass : "x-form-focus", // not needed???
8749 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8751 invalidClass : "has-warning",
8754 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8756 validClass : "has-success",
8759 * @cfg {Boolean} hasFeedback (true|false) default true
8764 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8766 invalidFeedbackClass : "glyphicon-warning-sign",
8769 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8771 validFeedbackClass : "glyphicon-ok",
8774 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8776 selectOnFocus : false,
8779 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8783 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8788 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8790 disableKeyFilter : false,
8793 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8797 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8801 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8803 blankText : "Please complete this mandatory field",
8806 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8810 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8812 maxLength : Number.MAX_VALUE,
8814 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8816 minLengthText : "The minimum length for this field is {0}",
8818 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8820 maxLengthText : "The maximum length for this field is {0}",
8824 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8825 * If available, this function will be called only after the basic validators all return true, and will be passed the
8826 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8830 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8831 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8832 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8836 * @cfg {String} regexText -- Depricated - use Invalid Text
8841 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8847 autocomplete: false,
8866 formatedValue : false,
8867 forceFeedback : false,
8869 indicatorpos : 'left',
8879 parentLabelAlign : function()
8882 while (parent.parent()) {
8883 parent = parent.parent();
8884 if (typeof(parent.labelAlign) !='undefined') {
8885 return parent.labelAlign;
8892 getAutoCreate : function()
8894 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8900 if(this.inputType != 'hidden'){
8901 cfg.cls = 'form-group' //input-group
8907 type : this.inputType,
8909 cls : 'form-control',
8910 placeholder : this.placeholder || '',
8911 autocomplete : this.autocomplete || 'new-password'
8914 if(this.capture.length){
8915 input.capture = this.capture;
8918 if(this.accept.length){
8919 input.accept = this.accept + "/*";
8923 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8926 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8927 input.maxLength = this.maxLength;
8930 if (this.disabled) {
8931 input.disabled=true;
8934 if (this.readOnly) {
8935 input.readonly=true;
8939 input.name = this.name;
8943 input.cls += ' input-' + this.size;
8947 ['xs','sm','md','lg'].map(function(size){
8948 if (settings[size]) {
8949 cfg.cls += ' col-' + size + '-' + settings[size];
8953 var inputblock = input;
8957 cls: 'glyphicon form-control-feedback'
8960 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8963 cls : 'has-feedback',
8971 if (this.before || this.after) {
8974 cls : 'input-group',
8978 if (this.before && typeof(this.before) == 'string') {
8980 inputblock.cn.push({
8982 cls : 'roo-input-before input-group-addon',
8986 if (this.before && typeof(this.before) == 'object') {
8987 this.before = Roo.factory(this.before);
8989 inputblock.cn.push({
8991 cls : 'roo-input-before input-group-' +
8992 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8996 inputblock.cn.push(input);
8998 if (this.after && typeof(this.after) == 'string') {
8999 inputblock.cn.push({
9001 cls : 'roo-input-after input-group-addon',
9005 if (this.after && typeof(this.after) == 'object') {
9006 this.after = Roo.factory(this.after);
9008 inputblock.cn.push({
9010 cls : 'roo-input-after input-group-' +
9011 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9015 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9016 inputblock.cls += ' has-feedback';
9017 inputblock.cn.push(feedback);
9021 if (align ==='left' && this.fieldLabel.length) {
9023 cfg.cls += ' roo-form-group-label-left';
9028 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9029 tooltip : 'This field is required'
9034 cls : 'control-label',
9035 html : this.fieldLabel
9046 var labelCfg = cfg.cn[1];
9047 var contentCfg = cfg.cn[2];
9049 if(this.indicatorpos == 'right'){
9054 cls : 'control-label',
9058 html : this.fieldLabel
9062 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9063 tooltip : 'This field is required'
9076 labelCfg = cfg.cn[0];
9077 contentCfg = cfg.cn[1];
9081 if(this.labelWidth > 12){
9082 labelCfg.style = "width: " + this.labelWidth + 'px';
9085 if(this.labelWidth < 13 && this.labelmd == 0){
9086 this.labelmd = this.labelWidth;
9089 if(this.labellg > 0){
9090 labelCfg.cls += ' col-lg-' + this.labellg;
9091 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9094 if(this.labelmd > 0){
9095 labelCfg.cls += ' col-md-' + this.labelmd;
9096 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9099 if(this.labelsm > 0){
9100 labelCfg.cls += ' col-sm-' + this.labelsm;
9101 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9104 if(this.labelxs > 0){
9105 labelCfg.cls += ' col-xs-' + this.labelxs;
9106 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9110 } else if ( this.fieldLabel.length) {
9115 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9116 tooltip : 'This field is required'
9120 //cls : 'input-group-addon',
9121 html : this.fieldLabel
9129 if(this.indicatorpos == 'right'){
9134 //cls : 'input-group-addon',
9135 html : this.fieldLabel
9140 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9141 tooltip : 'This field is required'
9161 if (this.parentType === 'Navbar' && this.parent().bar) {
9162 cfg.cls += ' navbar-form';
9165 if (this.parentType === 'NavGroup') {
9166 cfg.cls += ' navbar-form';
9174 * return the real input element.
9176 inputEl: function ()
9178 return this.el.select('input.form-control',true).first();
9181 tooltipEl : function()
9183 return this.inputEl();
9186 indicatorEl : function()
9188 var indicator = this.el.select('i.roo-required-indicator',true).first();
9198 setDisabled : function(v)
9200 var i = this.inputEl().dom;
9202 i.removeAttribute('disabled');
9206 i.setAttribute('disabled','true');
9208 initEvents : function()
9211 this.inputEl().on("keydown" , this.fireKey, this);
9212 this.inputEl().on("focus", this.onFocus, this);
9213 this.inputEl().on("blur", this.onBlur, this);
9215 this.inputEl().relayEvent('keyup', this);
9217 this.indicator = this.indicatorEl();
9220 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9223 // reference to original value for reset
9224 this.originalValue = this.getValue();
9225 //Roo.form.TextField.superclass.initEvents.call(this);
9226 if(this.validationEvent == 'keyup'){
9227 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9228 this.inputEl().on('keyup', this.filterValidation, this);
9230 else if(this.validationEvent !== false){
9231 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9234 if(this.selectOnFocus){
9235 this.on("focus", this.preFocus, this);
9238 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9239 this.inputEl().on("keypress", this.filterKeys, this);
9241 this.inputEl().relayEvent('keypress', this);
9244 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9245 this.el.on("click", this.autoSize, this);
9248 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9249 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9252 if (typeof(this.before) == 'object') {
9253 this.before.render(this.el.select('.roo-input-before',true).first());
9255 if (typeof(this.after) == 'object') {
9256 this.after.render(this.el.select('.roo-input-after',true).first());
9259 this.inputEl().on('change', this.onChange, this);
9262 filterValidation : function(e){
9263 if(!e.isNavKeyPress()){
9264 this.validationTask.delay(this.validationDelay);
9268 * Validates the field value
9269 * @return {Boolean} True if the value is valid, else false
9271 validate : function(){
9272 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9273 if(this.disabled || this.validateValue(this.getRawValue())){
9284 * Validates a value according to the field's validation rules and marks the field as invalid
9285 * if the validation fails
9286 * @param {Mixed} value The value to validate
9287 * @return {Boolean} True if the value is valid, else false
9289 validateValue : function(value)
9291 if(this.getVisibilityEl().hasClass('hidden')){
9295 if(value.length < 1) { // if it's blank
9296 if(this.allowBlank){
9302 if(value.length < this.minLength){
9305 if(value.length > this.maxLength){
9309 var vt = Roo.form.VTypes;
9310 if(!vt[this.vtype](value, this)){
9314 if(typeof this.validator == "function"){
9315 var msg = this.validator(value);
9319 if (typeof(msg) == 'string') {
9320 this.invalidText = msg;
9324 if(this.regex && !this.regex.test(value)){
9332 fireKey : function(e){
9333 //Roo.log('field ' + e.getKey());
9334 if(e.isNavKeyPress()){
9335 this.fireEvent("specialkey", this, e);
9338 focus : function (selectText){
9340 this.inputEl().focus();
9341 if(selectText === true){
9342 this.inputEl().dom.select();
9348 onFocus : function(){
9349 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9350 // this.el.addClass(this.focusClass);
9353 this.hasFocus = true;
9354 this.startValue = this.getValue();
9355 this.fireEvent("focus", this);
9359 beforeBlur : Roo.emptyFn,
9363 onBlur : function(){
9365 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9366 //this.el.removeClass(this.focusClass);
9368 this.hasFocus = false;
9369 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9372 var v = this.getValue();
9373 if(String(v) !== String(this.startValue)){
9374 this.fireEvent('change', this, v, this.startValue);
9376 this.fireEvent("blur", this);
9379 onChange : function(e)
9381 var v = this.getValue();
9382 if(String(v) !== String(this.startValue)){
9383 this.fireEvent('change', this, v, this.startValue);
9389 * Resets the current field value to the originally loaded value and clears any validation messages
9392 this.setValue(this.originalValue);
9396 * Returns the name of the field
9397 * @return {Mixed} name The name field
9399 getName: function(){
9403 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9404 * @return {Mixed} value The field value
9406 getValue : function(){
9408 var v = this.inputEl().getValue();
9413 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9414 * @return {Mixed} value The field value
9416 getRawValue : function(){
9417 var v = this.inputEl().getValue();
9423 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9424 * @param {Mixed} value The value to set
9426 setRawValue : function(v){
9427 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9430 selectText : function(start, end){
9431 var v = this.getRawValue();
9433 start = start === undefined ? 0 : start;
9434 end = end === undefined ? v.length : end;
9435 var d = this.inputEl().dom;
9436 if(d.setSelectionRange){
9437 d.setSelectionRange(start, end);
9438 }else if(d.createTextRange){
9439 var range = d.createTextRange();
9440 range.moveStart("character", start);
9441 range.moveEnd("character", v.length-end);
9448 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9449 * @param {Mixed} value The value to set
9451 setValue : function(v){
9454 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9460 processValue : function(value){
9461 if(this.stripCharsRe){
9462 var newValue = value.replace(this.stripCharsRe, '');
9463 if(newValue !== value){
9464 this.setRawValue(newValue);
9471 preFocus : function(){
9473 if(this.selectOnFocus){
9474 this.inputEl().dom.select();
9477 filterKeys : function(e){
9479 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9482 var c = e.getCharCode(), cc = String.fromCharCode(c);
9483 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9486 if(!this.maskRe.test(cc)){
9491 * Clear any invalid styles/messages for this field
9493 clearInvalid : function(){
9495 if(!this.el || this.preventMark){ // not rendered
9500 this.el.removeClass(this.invalidClass);
9502 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9504 var feedback = this.el.select('.form-control-feedback', true).first();
9507 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9513 this.indicator.removeClass('visible');
9514 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9517 this.fireEvent('valid', this);
9521 * Mark this field as valid
9523 markValid : function()
9525 if(!this.el || this.preventMark){ // not rendered...
9529 this.el.removeClass([this.invalidClass, this.validClass]);
9531 var feedback = this.el.select('.form-control-feedback', true).first();
9534 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9538 this.indicator.removeClass('visible');
9539 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9546 if(this.allowBlank && !this.getRawValue().length){
9550 this.el.addClass(this.validClass);
9552 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9554 var feedback = this.el.select('.form-control-feedback', true).first();
9557 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9558 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9563 this.fireEvent('valid', this);
9567 * Mark this field as invalid
9568 * @param {String} msg The validation message
9570 markInvalid : function(msg)
9572 if(!this.el || this.preventMark){ // not rendered
9576 this.el.removeClass([this.invalidClass, this.validClass]);
9578 var feedback = this.el.select('.form-control-feedback', true).first();
9581 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9588 if(this.allowBlank && !this.getRawValue().length){
9593 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9594 this.indicator.addClass('visible');
9597 this.el.addClass(this.invalidClass);
9599 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9601 var feedback = this.el.select('.form-control-feedback', true).first();
9604 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9606 if(this.getValue().length || this.forceFeedback){
9607 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9614 this.fireEvent('invalid', this, msg);
9617 SafariOnKeyDown : function(event)
9619 // this is a workaround for a password hang bug on chrome/ webkit.
9620 if (this.inputEl().dom.type != 'password') {
9624 var isSelectAll = false;
9626 if(this.inputEl().dom.selectionEnd > 0){
9627 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9629 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9630 event.preventDefault();
9635 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9637 event.preventDefault();
9638 // this is very hacky as keydown always get's upper case.
9640 var cc = String.fromCharCode(event.getCharCode());
9641 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9645 adjustWidth : function(tag, w){
9646 tag = tag.toLowerCase();
9647 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9648 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9652 if(tag == 'textarea'){
9655 }else if(Roo.isOpera){
9659 if(tag == 'textarea'){
9667 setFieldLabel : function(v)
9674 var ar = this.el.select('label > span',true);
9676 if (ar.elements.length) {
9677 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9678 this.fieldLabel = v;
9682 var br = this.el.select('label',true);
9684 if(br.elements.length) {
9685 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9686 this.fieldLabel = v;
9690 Roo.log('Cannot Found any of label > span || label in input');
9694 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9695 this.fieldLabel = v;
9710 * @class Roo.bootstrap.TextArea
9711 * @extends Roo.bootstrap.Input
9712 * Bootstrap TextArea class
9713 * @cfg {Number} cols Specifies the visible width of a text area
9714 * @cfg {Number} rows Specifies the visible number of lines in a text area
9715 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9716 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9717 * @cfg {string} html text
9720 * Create a new TextArea
9721 * @param {Object} config The config object
9724 Roo.bootstrap.TextArea = function(config){
9725 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9729 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9739 getAutoCreate : function(){
9741 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9747 if(this.inputType != 'hidden'){
9748 cfg.cls = 'form-group' //input-group
9756 value : this.value || '',
9757 html: this.html || '',
9758 cls : 'form-control',
9759 placeholder : this.placeholder || ''
9763 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9764 input.maxLength = this.maxLength;
9768 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9772 input.cols = this.cols;
9775 if (this.readOnly) {
9776 input.readonly = true;
9780 input.name = this.name;
9784 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9788 ['xs','sm','md','lg'].map(function(size){
9789 if (settings[size]) {
9790 cfg.cls += ' col-' + size + '-' + settings[size];
9794 var inputblock = input;
9796 if(this.hasFeedback && !this.allowBlank){
9800 cls: 'glyphicon form-control-feedback'
9804 cls : 'has-feedback',
9813 if (this.before || this.after) {
9816 cls : 'input-group',
9820 inputblock.cn.push({
9822 cls : 'input-group-addon',
9827 inputblock.cn.push(input);
9829 if(this.hasFeedback && !this.allowBlank){
9830 inputblock.cls += ' has-feedback';
9831 inputblock.cn.push(feedback);
9835 inputblock.cn.push({
9837 cls : 'input-group-addon',
9844 if (align ==='left' && this.fieldLabel.length) {
9849 cls : 'control-label',
9850 html : this.fieldLabel
9861 if(this.labelWidth > 12){
9862 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9865 if(this.labelWidth < 13 && this.labelmd == 0){
9866 this.labelmd = this.labelWidth;
9869 if(this.labellg > 0){
9870 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9871 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9874 if(this.labelmd > 0){
9875 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9876 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9879 if(this.labelsm > 0){
9880 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9881 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9884 if(this.labelxs > 0){
9885 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9886 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9889 } else if ( this.fieldLabel.length) {
9894 //cls : 'input-group-addon',
9895 html : this.fieldLabel
9913 if (this.disabled) {
9914 input.disabled=true;
9921 * return the real textarea element.
9923 inputEl: function ()
9925 return this.el.select('textarea.form-control',true).first();
9929 * Clear any invalid styles/messages for this field
9931 clearInvalid : function()
9934 if(!this.el || this.preventMark){ // not rendered
9938 var label = this.el.select('label', true).first();
9939 var icon = this.el.select('i.fa-star', true).first();
9945 this.el.removeClass(this.invalidClass);
9947 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9949 var feedback = this.el.select('.form-control-feedback', true).first();
9952 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9957 this.fireEvent('valid', this);
9961 * Mark this field as valid
9963 markValid : function()
9965 if(!this.el || this.preventMark){ // not rendered
9969 this.el.removeClass([this.invalidClass, this.validClass]);
9971 var feedback = this.el.select('.form-control-feedback', true).first();
9974 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9977 if(this.disabled || this.allowBlank){
9981 var label = this.el.select('label', true).first();
9982 var icon = this.el.select('i.fa-star', true).first();
9988 this.el.addClass(this.validClass);
9990 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9992 var feedback = this.el.select('.form-control-feedback', true).first();
9995 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9996 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10001 this.fireEvent('valid', this);
10005 * Mark this field as invalid
10006 * @param {String} msg The validation message
10008 markInvalid : function(msg)
10010 if(!this.el || this.preventMark){ // not rendered
10014 this.el.removeClass([this.invalidClass, this.validClass]);
10016 var feedback = this.el.select('.form-control-feedback', true).first();
10019 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10022 if(this.disabled || this.allowBlank){
10026 var label = this.el.select('label', true).first();
10027 var icon = this.el.select('i.fa-star', true).first();
10029 if(!this.getValue().length && label && !icon){
10030 this.el.createChild({
10032 cls : 'text-danger fa fa-lg fa-star',
10033 tooltip : 'This field is required',
10034 style : 'margin-right:5px;'
10038 this.el.addClass(this.invalidClass);
10040 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10042 var feedback = this.el.select('.form-control-feedback', true).first();
10045 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10047 if(this.getValue().length || this.forceFeedback){
10048 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10055 this.fireEvent('invalid', this, msg);
10063 * trigger field - base class for combo..
10068 * @class Roo.bootstrap.TriggerField
10069 * @extends Roo.bootstrap.Input
10070 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10071 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10072 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10073 * for which you can provide a custom implementation. For example:
10075 var trigger = new Roo.bootstrap.TriggerField();
10076 trigger.onTriggerClick = myTriggerFn;
10077 trigger.applyTo('my-field');
10080 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10081 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10082 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10083 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10084 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10087 * Create a new TriggerField.
10088 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10089 * to the base TextField)
10091 Roo.bootstrap.TriggerField = function(config){
10092 this.mimicing = false;
10093 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10096 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10098 * @cfg {String} triggerClass A CSS class to apply to the trigger
10101 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10106 * @cfg {Boolean} removable (true|false) special filter default false
10110 /** @cfg {Boolean} grow @hide */
10111 /** @cfg {Number} growMin @hide */
10112 /** @cfg {Number} growMax @hide */
10118 autoSize: Roo.emptyFn,
10122 deferHeight : true,
10125 actionMode : 'wrap',
10130 getAutoCreate : function(){
10132 var align = this.labelAlign || this.parentLabelAlign();
10137 cls: 'form-group' //input-group
10144 type : this.inputType,
10145 cls : 'form-control',
10146 autocomplete: 'new-password',
10147 placeholder : this.placeholder || ''
10151 input.name = this.name;
10154 input.cls += ' input-' + this.size;
10157 if (this.disabled) {
10158 input.disabled=true;
10161 var inputblock = input;
10163 if(this.hasFeedback && !this.allowBlank){
10167 cls: 'glyphicon form-control-feedback'
10170 if(this.removable && !this.editable && !this.tickable){
10172 cls : 'has-feedback',
10178 cls : 'roo-combo-removable-btn close'
10185 cls : 'has-feedback',
10194 if(this.removable && !this.editable && !this.tickable){
10196 cls : 'roo-removable',
10202 cls : 'roo-combo-removable-btn close'
10209 if (this.before || this.after) {
10212 cls : 'input-group',
10216 inputblock.cn.push({
10218 cls : 'input-group-addon',
10223 inputblock.cn.push(input);
10225 if(this.hasFeedback && !this.allowBlank){
10226 inputblock.cls += ' has-feedback';
10227 inputblock.cn.push(feedback);
10231 inputblock.cn.push({
10233 cls : 'input-group-addon',
10246 cls: 'form-hidden-field'
10260 cls: 'form-hidden-field'
10264 cls: 'roo-select2-choices',
10268 cls: 'roo-select2-search-field',
10281 cls: 'roo-select2-container input-group',
10286 // cls: 'typeahead typeahead-long dropdown-menu',
10287 // style: 'display:none'
10292 if(!this.multiple && this.showToggleBtn){
10298 if (this.caret != false) {
10301 cls: 'fa fa-' + this.caret
10308 cls : 'input-group-addon btn dropdown-toggle',
10313 cls: 'combobox-clear',
10327 combobox.cls += ' roo-select2-container-multi';
10330 if (align ==='left' && this.fieldLabel.length) {
10332 cfg.cls += ' roo-form-group-label-left';
10337 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10338 tooltip : 'This field is required'
10343 cls : 'control-label',
10344 html : this.fieldLabel
10356 var labelCfg = cfg.cn[1];
10357 var contentCfg = cfg.cn[2];
10359 if(this.indicatorpos == 'right'){
10364 cls : 'control-label',
10368 html : this.fieldLabel
10372 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10373 tooltip : 'This field is required'
10386 labelCfg = cfg.cn[0];
10387 contentCfg = cfg.cn[1];
10390 if(this.labelWidth > 12){
10391 labelCfg.style = "width: " + this.labelWidth + 'px';
10394 if(this.labelWidth < 13 && this.labelmd == 0){
10395 this.labelmd = this.labelWidth;
10398 if(this.labellg > 0){
10399 labelCfg.cls += ' col-lg-' + this.labellg;
10400 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10403 if(this.labelmd > 0){
10404 labelCfg.cls += ' col-md-' + this.labelmd;
10405 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10408 if(this.labelsm > 0){
10409 labelCfg.cls += ' col-sm-' + this.labelsm;
10410 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10413 if(this.labelxs > 0){
10414 labelCfg.cls += ' col-xs-' + this.labelxs;
10415 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10418 } else if ( this.fieldLabel.length) {
10419 // Roo.log(" label");
10423 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10424 tooltip : 'This field is required'
10428 //cls : 'input-group-addon',
10429 html : this.fieldLabel
10437 if(this.indicatorpos == 'right'){
10445 html : this.fieldLabel
10449 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10450 tooltip : 'This field is required'
10463 // Roo.log(" no label && no align");
10470 ['xs','sm','md','lg'].map(function(size){
10471 if (settings[size]) {
10472 cfg.cls += ' col-' + size + '-' + settings[size];
10483 onResize : function(w, h){
10484 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10485 // if(typeof w == 'number'){
10486 // var x = w - this.trigger.getWidth();
10487 // this.inputEl().setWidth(this.adjustWidth('input', x));
10488 // this.trigger.setStyle('left', x+'px');
10493 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10496 getResizeEl : function(){
10497 return this.inputEl();
10501 getPositionEl : function(){
10502 return this.inputEl();
10506 alignErrorIcon : function(){
10507 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10511 initEvents : function(){
10515 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10516 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10517 if(!this.multiple && this.showToggleBtn){
10518 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10519 if(this.hideTrigger){
10520 this.trigger.setDisplayed(false);
10522 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10526 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10529 if(this.removable && !this.editable && !this.tickable){
10530 var close = this.closeTriggerEl();
10533 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10534 close.on('click', this.removeBtnClick, this, close);
10538 //this.trigger.addClassOnOver('x-form-trigger-over');
10539 //this.trigger.addClassOnClick('x-form-trigger-click');
10542 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10546 closeTriggerEl : function()
10548 var close = this.el.select('.roo-combo-removable-btn', true).first();
10549 return close ? close : false;
10552 removeBtnClick : function(e, h, el)
10554 e.preventDefault();
10556 if(this.fireEvent("remove", this) !== false){
10558 this.fireEvent("afterremove", this)
10562 createList : function()
10564 this.list = Roo.get(document.body).createChild({
10566 cls: 'typeahead typeahead-long dropdown-menu',
10567 style: 'display:none'
10570 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10575 initTrigger : function(){
10580 onDestroy : function(){
10582 this.trigger.removeAllListeners();
10583 // this.trigger.remove();
10586 // this.wrap.remove();
10588 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10592 onFocus : function(){
10593 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10595 if(!this.mimicing){
10596 this.wrap.addClass('x-trigger-wrap-focus');
10597 this.mimicing = true;
10598 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10599 if(this.monitorTab){
10600 this.el.on("keydown", this.checkTab, this);
10607 checkTab : function(e){
10608 if(e.getKey() == e.TAB){
10609 this.triggerBlur();
10614 onBlur : function(){
10619 mimicBlur : function(e, t){
10621 if(!this.wrap.contains(t) && this.validateBlur()){
10622 this.triggerBlur();
10628 triggerBlur : function(){
10629 this.mimicing = false;
10630 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10631 if(this.monitorTab){
10632 this.el.un("keydown", this.checkTab, this);
10634 //this.wrap.removeClass('x-trigger-wrap-focus');
10635 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10639 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10640 validateBlur : function(e, t){
10645 onDisable : function(){
10646 this.inputEl().dom.disabled = true;
10647 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10649 // this.wrap.addClass('x-item-disabled');
10654 onEnable : function(){
10655 this.inputEl().dom.disabled = false;
10656 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10658 // this.el.removeClass('x-item-disabled');
10663 onShow : function(){
10664 var ae = this.getActionEl();
10667 ae.dom.style.display = '';
10668 ae.dom.style.visibility = 'visible';
10674 onHide : function(){
10675 var ae = this.getActionEl();
10676 ae.dom.style.display = 'none';
10680 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10681 * by an implementing function.
10683 * @param {EventObject} e
10685 onTriggerClick : Roo.emptyFn
10689 * Ext JS Library 1.1.1
10690 * Copyright(c) 2006-2007, Ext JS, LLC.
10692 * Originally Released Under LGPL - original licence link has changed is not relivant.
10695 * <script type="text/javascript">
10700 * @class Roo.data.SortTypes
10702 * Defines the default sorting (casting?) comparison functions used when sorting data.
10704 Roo.data.SortTypes = {
10706 * Default sort that does nothing
10707 * @param {Mixed} s The value being converted
10708 * @return {Mixed} The comparison value
10710 none : function(s){
10715 * The regular expression used to strip tags
10719 stripTagsRE : /<\/?[^>]+>/gi,
10722 * Strips all HTML tags to sort on text only
10723 * @param {Mixed} s The value being converted
10724 * @return {String} The comparison value
10726 asText : function(s){
10727 return String(s).replace(this.stripTagsRE, "");
10731 * Strips all HTML tags to sort on text only - Case insensitive
10732 * @param {Mixed} s The value being converted
10733 * @return {String} The comparison value
10735 asUCText : function(s){
10736 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10740 * Case insensitive string
10741 * @param {Mixed} s The value being converted
10742 * @return {String} The comparison value
10744 asUCString : function(s) {
10745 return String(s).toUpperCase();
10750 * @param {Mixed} s The value being converted
10751 * @return {Number} The comparison value
10753 asDate : function(s) {
10757 if(s instanceof Date){
10758 return s.getTime();
10760 return Date.parse(String(s));
10765 * @param {Mixed} s The value being converted
10766 * @return {Float} The comparison value
10768 asFloat : function(s) {
10769 var val = parseFloat(String(s).replace(/,/g, ""));
10778 * @param {Mixed} s The value being converted
10779 * @return {Number} The comparison value
10781 asInt : function(s) {
10782 var val = parseInt(String(s).replace(/,/g, ""));
10790 * Ext JS Library 1.1.1
10791 * Copyright(c) 2006-2007, Ext JS, LLC.
10793 * Originally Released Under LGPL - original licence link has changed is not relivant.
10796 * <script type="text/javascript">
10800 * @class Roo.data.Record
10801 * Instances of this class encapsulate both record <em>definition</em> information, and record
10802 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10803 * to access Records cached in an {@link Roo.data.Store} object.<br>
10805 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10806 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10809 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10811 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10812 * {@link #create}. The parameters are the same.
10813 * @param {Array} data An associative Array of data values keyed by the field name.
10814 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10815 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10816 * not specified an integer id is generated.
10818 Roo.data.Record = function(data, id){
10819 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10824 * Generate a constructor for a specific record layout.
10825 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10826 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10827 * Each field definition object may contain the following properties: <ul>
10828 * <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,
10829 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10830 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10831 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10832 * is being used, then this is a string containing the javascript expression to reference the data relative to
10833 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10834 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10835 * this may be omitted.</p></li>
10836 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10837 * <ul><li>auto (Default, implies no conversion)</li>
10842 * <li>date</li></ul></p></li>
10843 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10844 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10845 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10846 * by the Reader into an object that will be stored in the Record. It is passed the
10847 * following parameters:<ul>
10848 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10850 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10852 * <br>usage:<br><pre><code>
10853 var TopicRecord = Roo.data.Record.create(
10854 {name: 'title', mapping: 'topic_title'},
10855 {name: 'author', mapping: 'username'},
10856 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10857 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10858 {name: 'lastPoster', mapping: 'user2'},
10859 {name: 'excerpt', mapping: 'post_text'}
10862 var myNewRecord = new TopicRecord({
10863 title: 'Do my job please',
10866 lastPost: new Date(),
10867 lastPoster: 'Animal',
10868 excerpt: 'No way dude!'
10870 myStore.add(myNewRecord);
10875 Roo.data.Record.create = function(o){
10876 var f = function(){
10877 f.superclass.constructor.apply(this, arguments);
10879 Roo.extend(f, Roo.data.Record);
10880 var p = f.prototype;
10881 p.fields = new Roo.util.MixedCollection(false, function(field){
10884 for(var i = 0, len = o.length; i < len; i++){
10885 p.fields.add(new Roo.data.Field(o[i]));
10887 f.getField = function(name){
10888 return p.fields.get(name);
10893 Roo.data.Record.AUTO_ID = 1000;
10894 Roo.data.Record.EDIT = 'edit';
10895 Roo.data.Record.REJECT = 'reject';
10896 Roo.data.Record.COMMIT = 'commit';
10898 Roo.data.Record.prototype = {
10900 * Readonly flag - true if this record has been modified.
10909 join : function(store){
10910 this.store = store;
10914 * Set the named field to the specified value.
10915 * @param {String} name The name of the field to set.
10916 * @param {Object} value The value to set the field to.
10918 set : function(name, value){
10919 if(this.data[name] == value){
10923 if(!this.modified){
10924 this.modified = {};
10926 if(typeof this.modified[name] == 'undefined'){
10927 this.modified[name] = this.data[name];
10929 this.data[name] = value;
10930 if(!this.editing && this.store){
10931 this.store.afterEdit(this);
10936 * Get the value of the named field.
10937 * @param {String} name The name of the field to get the value of.
10938 * @return {Object} The value of the field.
10940 get : function(name){
10941 return this.data[name];
10945 beginEdit : function(){
10946 this.editing = true;
10947 this.modified = {};
10951 cancelEdit : function(){
10952 this.editing = false;
10953 delete this.modified;
10957 endEdit : function(){
10958 this.editing = false;
10959 if(this.dirty && this.store){
10960 this.store.afterEdit(this);
10965 * Usually called by the {@link Roo.data.Store} which owns the Record.
10966 * Rejects all changes made to the Record since either creation, or the last commit operation.
10967 * Modified fields are reverted to their original values.
10969 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10970 * of reject operations.
10972 reject : function(){
10973 var m = this.modified;
10975 if(typeof m[n] != "function"){
10976 this.data[n] = m[n];
10979 this.dirty = false;
10980 delete this.modified;
10981 this.editing = false;
10983 this.store.afterReject(this);
10988 * Usually called by the {@link Roo.data.Store} which owns the Record.
10989 * Commits all changes made to the Record since either creation, or the last commit operation.
10991 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10992 * of commit operations.
10994 commit : function(){
10995 this.dirty = false;
10996 delete this.modified;
10997 this.editing = false;
10999 this.store.afterCommit(this);
11004 hasError : function(){
11005 return this.error != null;
11009 clearError : function(){
11014 * Creates a copy of this record.
11015 * @param {String} id (optional) A new record id if you don't want to use this record's id
11018 copy : function(newId) {
11019 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11023 * Ext JS Library 1.1.1
11024 * Copyright(c) 2006-2007, Ext JS, LLC.
11026 * Originally Released Under LGPL - original licence link has changed is not relivant.
11029 * <script type="text/javascript">
11035 * @class Roo.data.Store
11036 * @extends Roo.util.Observable
11037 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11038 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11040 * 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
11041 * has no knowledge of the format of the data returned by the Proxy.<br>
11043 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11044 * instances from the data object. These records are cached and made available through accessor functions.
11046 * Creates a new Store.
11047 * @param {Object} config A config object containing the objects needed for the Store to access data,
11048 * and read the data into Records.
11050 Roo.data.Store = function(config){
11051 this.data = new Roo.util.MixedCollection(false);
11052 this.data.getKey = function(o){
11055 this.baseParams = {};
11057 this.paramNames = {
11062 "multisort" : "_multisort"
11065 if(config && config.data){
11066 this.inlineData = config.data;
11067 delete config.data;
11070 Roo.apply(this, config);
11072 if(this.reader){ // reader passed
11073 this.reader = Roo.factory(this.reader, Roo.data);
11074 this.reader.xmodule = this.xmodule || false;
11075 if(!this.recordType){
11076 this.recordType = this.reader.recordType;
11078 if(this.reader.onMetaChange){
11079 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11083 if(this.recordType){
11084 this.fields = this.recordType.prototype.fields;
11086 this.modified = [];
11090 * @event datachanged
11091 * Fires when the data cache has changed, and a widget which is using this Store
11092 * as a Record cache should refresh its view.
11093 * @param {Store} this
11095 datachanged : true,
11097 * @event metachange
11098 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11099 * @param {Store} this
11100 * @param {Object} meta The JSON metadata
11105 * Fires when Records have been added to the Store
11106 * @param {Store} this
11107 * @param {Roo.data.Record[]} records The array of Records added
11108 * @param {Number} index The index at which the record(s) were added
11113 * Fires when a Record has been removed from the Store
11114 * @param {Store} this
11115 * @param {Roo.data.Record} record The Record that was removed
11116 * @param {Number} index The index at which the record was removed
11121 * Fires when a Record has been updated
11122 * @param {Store} this
11123 * @param {Roo.data.Record} record The Record that was updated
11124 * @param {String} operation The update operation being performed. Value may be one of:
11126 Roo.data.Record.EDIT
11127 Roo.data.Record.REJECT
11128 Roo.data.Record.COMMIT
11134 * Fires when the data cache has been cleared.
11135 * @param {Store} this
11139 * @event beforeload
11140 * Fires before a request is made for a new data object. If the beforeload handler returns false
11141 * the load action will be canceled.
11142 * @param {Store} this
11143 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11147 * @event beforeloadadd
11148 * Fires after a new set of Records has been loaded.
11149 * @param {Store} this
11150 * @param {Roo.data.Record[]} records The Records that were loaded
11151 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11153 beforeloadadd : true,
11156 * Fires after a new set of Records has been loaded, before they are added to the store.
11157 * @param {Store} this
11158 * @param {Roo.data.Record[]} records The Records that were loaded
11159 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11160 * @params {Object} return from reader
11164 * @event loadexception
11165 * Fires if an exception occurs in the Proxy during loading.
11166 * Called with the signature of the Proxy's "loadexception" event.
11167 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11170 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11171 * @param {Object} load options
11172 * @param {Object} jsonData from your request (normally this contains the Exception)
11174 loadexception : true
11178 this.proxy = Roo.factory(this.proxy, Roo.data);
11179 this.proxy.xmodule = this.xmodule || false;
11180 this.relayEvents(this.proxy, ["loadexception"]);
11182 this.sortToggle = {};
11183 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11185 Roo.data.Store.superclass.constructor.call(this);
11187 if(this.inlineData){
11188 this.loadData(this.inlineData);
11189 delete this.inlineData;
11193 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11195 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11196 * without a remote query - used by combo/forms at present.
11200 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11203 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11206 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11207 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11210 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11211 * on any HTTP request
11214 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11217 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11221 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11222 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11224 remoteSort : false,
11227 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11228 * loaded or when a record is removed. (defaults to false).
11230 pruneModifiedRecords : false,
11233 lastOptions : null,
11236 * Add Records to the Store and fires the add event.
11237 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11239 add : function(records){
11240 records = [].concat(records);
11241 for(var i = 0, len = records.length; i < len; i++){
11242 records[i].join(this);
11244 var index = this.data.length;
11245 this.data.addAll(records);
11246 this.fireEvent("add", this, records, index);
11250 * Remove a Record from the Store and fires the remove event.
11251 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11253 remove : function(record){
11254 var index = this.data.indexOf(record);
11255 this.data.removeAt(index);
11257 if(this.pruneModifiedRecords){
11258 this.modified.remove(record);
11260 this.fireEvent("remove", this, record, index);
11264 * Remove all Records from the Store and fires the clear event.
11266 removeAll : function(){
11268 if(this.pruneModifiedRecords){
11269 this.modified = [];
11271 this.fireEvent("clear", this);
11275 * Inserts Records to the Store at the given index and fires the add event.
11276 * @param {Number} index The start index at which to insert the passed Records.
11277 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11279 insert : function(index, records){
11280 records = [].concat(records);
11281 for(var i = 0, len = records.length; i < len; i++){
11282 this.data.insert(index, records[i]);
11283 records[i].join(this);
11285 this.fireEvent("add", this, records, index);
11289 * Get the index within the cache of the passed Record.
11290 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11291 * @return {Number} The index of the passed Record. Returns -1 if not found.
11293 indexOf : function(record){
11294 return this.data.indexOf(record);
11298 * Get the index within the cache of the Record with the passed id.
11299 * @param {String} id The id of the Record to find.
11300 * @return {Number} The index of the Record. Returns -1 if not found.
11302 indexOfId : function(id){
11303 return this.data.indexOfKey(id);
11307 * Get the Record with the specified id.
11308 * @param {String} id The id of the Record to find.
11309 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11311 getById : function(id){
11312 return this.data.key(id);
11316 * Get the Record at the specified index.
11317 * @param {Number} index The index of the Record to find.
11318 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11320 getAt : function(index){
11321 return this.data.itemAt(index);
11325 * Returns a range of Records between specified indices.
11326 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11327 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11328 * @return {Roo.data.Record[]} An array of Records
11330 getRange : function(start, end){
11331 return this.data.getRange(start, end);
11335 storeOptions : function(o){
11336 o = Roo.apply({}, o);
11339 this.lastOptions = o;
11343 * Loads the Record cache from the configured Proxy using the configured Reader.
11345 * If using remote paging, then the first load call must specify the <em>start</em>
11346 * and <em>limit</em> properties in the options.params property to establish the initial
11347 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11349 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11350 * and this call will return before the new data has been loaded. Perform any post-processing
11351 * in a callback function, or in a "load" event handler.</strong>
11353 * @param {Object} options An object containing properties which control loading options:<ul>
11354 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11355 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11356 * passed the following arguments:<ul>
11357 * <li>r : Roo.data.Record[]</li>
11358 * <li>options: Options object from the load call</li>
11359 * <li>success: Boolean success indicator</li></ul></li>
11360 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11361 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11364 load : function(options){
11365 options = options || {};
11366 if(this.fireEvent("beforeload", this, options) !== false){
11367 this.storeOptions(options);
11368 var p = Roo.apply(options.params || {}, this.baseParams);
11369 // if meta was not loaded from remote source.. try requesting it.
11370 if (!this.reader.metaFromRemote) {
11371 p._requestMeta = 1;
11373 if(this.sortInfo && this.remoteSort){
11374 var pn = this.paramNames;
11375 p[pn["sort"]] = this.sortInfo.field;
11376 p[pn["dir"]] = this.sortInfo.direction;
11378 if (this.multiSort) {
11379 var pn = this.paramNames;
11380 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11383 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11388 * Reloads the Record cache from the configured Proxy using the configured Reader and
11389 * the options from the last load operation performed.
11390 * @param {Object} options (optional) An object containing properties which may override the options
11391 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11392 * the most recently used options are reused).
11394 reload : function(options){
11395 this.load(Roo.applyIf(options||{}, this.lastOptions));
11399 // Called as a callback by the Reader during a load operation.
11400 loadRecords : function(o, options, success){
11401 if(!o || success === false){
11402 if(success !== false){
11403 this.fireEvent("load", this, [], options, o);
11405 if(options.callback){
11406 options.callback.call(options.scope || this, [], options, false);
11410 // if data returned failure - throw an exception.
11411 if (o.success === false) {
11412 // show a message if no listener is registered.
11413 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11414 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11416 // loadmask wil be hooked into this..
11417 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11420 var r = o.records, t = o.totalRecords || r.length;
11422 this.fireEvent("beforeloadadd", this, r, options, o);
11424 if(!options || options.add !== true){
11425 if(this.pruneModifiedRecords){
11426 this.modified = [];
11428 for(var i = 0, len = r.length; i < len; i++){
11432 this.data = this.snapshot;
11433 delete this.snapshot;
11436 this.data.addAll(r);
11437 this.totalLength = t;
11439 this.fireEvent("datachanged", this);
11441 this.totalLength = Math.max(t, this.data.length+r.length);
11445 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11447 var e = new Roo.data.Record({});
11449 e.set(this.parent.displayField, this.parent.emptyTitle);
11450 e.set(this.parent.valueField, '');
11455 this.fireEvent("load", this, r, options, o);
11456 if(options.callback){
11457 options.callback.call(options.scope || this, r, options, true);
11463 * Loads data from a passed data block. A Reader which understands the format of the data
11464 * must have been configured in the constructor.
11465 * @param {Object} data The data block from which to read the Records. The format of the data expected
11466 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11467 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11469 loadData : function(o, append){
11470 var r = this.reader.readRecords(o);
11471 this.loadRecords(r, {add: append}, true);
11475 * Gets the number of cached records.
11477 * <em>If using paging, this may not be the total size of the dataset. If the data object
11478 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11479 * the data set size</em>
11481 getCount : function(){
11482 return this.data.length || 0;
11486 * Gets the total number of records in the dataset as returned by the server.
11488 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11489 * the dataset size</em>
11491 getTotalCount : function(){
11492 return this.totalLength || 0;
11496 * Returns the sort state of the Store as an object with two properties:
11498 field {String} The name of the field by which the Records are sorted
11499 direction {String} The sort order, "ASC" or "DESC"
11502 getSortState : function(){
11503 return this.sortInfo;
11507 applySort : function(){
11508 if(this.sortInfo && !this.remoteSort){
11509 var s = this.sortInfo, f = s.field;
11510 var st = this.fields.get(f).sortType;
11511 var fn = function(r1, r2){
11512 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11513 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11515 this.data.sort(s.direction, fn);
11516 if(this.snapshot && this.snapshot != this.data){
11517 this.snapshot.sort(s.direction, fn);
11523 * Sets the default sort column and order to be used by the next load operation.
11524 * @param {String} fieldName The name of the field to sort by.
11525 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11527 setDefaultSort : function(field, dir){
11528 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11532 * Sort the Records.
11533 * If remote sorting is used, the sort is performed on the server, and the cache is
11534 * reloaded. If local sorting is used, the cache is sorted internally.
11535 * @param {String} fieldName The name of the field to sort by.
11536 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11538 sort : function(fieldName, dir){
11539 var f = this.fields.get(fieldName);
11541 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11543 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11544 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11549 this.sortToggle[f.name] = dir;
11550 this.sortInfo = {field: f.name, direction: dir};
11551 if(!this.remoteSort){
11553 this.fireEvent("datachanged", this);
11555 this.load(this.lastOptions);
11560 * Calls the specified function for each of the Records in the cache.
11561 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11562 * Returning <em>false</em> aborts and exits the iteration.
11563 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11565 each : function(fn, scope){
11566 this.data.each(fn, scope);
11570 * Gets all records modified since the last commit. Modified records are persisted across load operations
11571 * (e.g., during paging).
11572 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11574 getModifiedRecords : function(){
11575 return this.modified;
11579 createFilterFn : function(property, value, anyMatch){
11580 if(!value.exec){ // not a regex
11581 value = String(value);
11582 if(value.length == 0){
11585 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11587 return function(r){
11588 return value.test(r.data[property]);
11593 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11594 * @param {String} property A field on your records
11595 * @param {Number} start The record index to start at (defaults to 0)
11596 * @param {Number} end The last record index to include (defaults to length - 1)
11597 * @return {Number} The sum
11599 sum : function(property, start, end){
11600 var rs = this.data.items, v = 0;
11601 start = start || 0;
11602 end = (end || end === 0) ? end : rs.length-1;
11604 for(var i = start; i <= end; i++){
11605 v += (rs[i].data[property] || 0);
11611 * Filter the records by a specified property.
11612 * @param {String} field A field on your records
11613 * @param {String/RegExp} value Either a string that the field
11614 * should start with or a RegExp to test against the field
11615 * @param {Boolean} anyMatch True to match any part not just the beginning
11617 filter : function(property, value, anyMatch){
11618 var fn = this.createFilterFn(property, value, anyMatch);
11619 return fn ? this.filterBy(fn) : this.clearFilter();
11623 * Filter by a function. The specified function will be called with each
11624 * record in this data source. If the function returns true the record is included,
11625 * otherwise it is filtered.
11626 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11627 * @param {Object} scope (optional) The scope of the function (defaults to this)
11629 filterBy : function(fn, scope){
11630 this.snapshot = this.snapshot || this.data;
11631 this.data = this.queryBy(fn, scope||this);
11632 this.fireEvent("datachanged", this);
11636 * Query the records by a specified property.
11637 * @param {String} field A field on your records
11638 * @param {String/RegExp} value Either a string that the field
11639 * should start with or a RegExp to test against the field
11640 * @param {Boolean} anyMatch True to match any part not just the beginning
11641 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11643 query : function(property, value, anyMatch){
11644 var fn = this.createFilterFn(property, value, anyMatch);
11645 return fn ? this.queryBy(fn) : this.data.clone();
11649 * Query by a function. The specified function will be called with each
11650 * record in this data source. If the function returns true the record is included
11652 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11653 * @param {Object} scope (optional) The scope of the function (defaults to this)
11654 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11656 queryBy : function(fn, scope){
11657 var data = this.snapshot || this.data;
11658 return data.filterBy(fn, scope||this);
11662 * Collects unique values for a particular dataIndex from this store.
11663 * @param {String} dataIndex The property to collect
11664 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11665 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11666 * @return {Array} An array of the unique values
11668 collect : function(dataIndex, allowNull, bypassFilter){
11669 var d = (bypassFilter === true && this.snapshot) ?
11670 this.snapshot.items : this.data.items;
11671 var v, sv, r = [], l = {};
11672 for(var i = 0, len = d.length; i < len; i++){
11673 v = d[i].data[dataIndex];
11675 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11684 * Revert to a view of the Record cache with no filtering applied.
11685 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11687 clearFilter : function(suppressEvent){
11688 if(this.snapshot && this.snapshot != this.data){
11689 this.data = this.snapshot;
11690 delete this.snapshot;
11691 if(suppressEvent !== true){
11692 this.fireEvent("datachanged", this);
11698 afterEdit : function(record){
11699 if(this.modified.indexOf(record) == -1){
11700 this.modified.push(record);
11702 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11706 afterReject : function(record){
11707 this.modified.remove(record);
11708 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11712 afterCommit : function(record){
11713 this.modified.remove(record);
11714 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11718 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11719 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11721 commitChanges : function(){
11722 var m = this.modified.slice(0);
11723 this.modified = [];
11724 for(var i = 0, len = m.length; i < len; i++){
11730 * Cancel outstanding changes on all changed records.
11732 rejectChanges : function(){
11733 var m = this.modified.slice(0);
11734 this.modified = [];
11735 for(var i = 0, len = m.length; i < len; i++){
11740 onMetaChange : function(meta, rtype, o){
11741 this.recordType = rtype;
11742 this.fields = rtype.prototype.fields;
11743 delete this.snapshot;
11744 this.sortInfo = meta.sortInfo || this.sortInfo;
11745 this.modified = [];
11746 this.fireEvent('metachange', this, this.reader.meta);
11749 moveIndex : function(data, type)
11751 var index = this.indexOf(data);
11753 var newIndex = index + type;
11757 this.insert(newIndex, data);
11762 * Ext JS Library 1.1.1
11763 * Copyright(c) 2006-2007, Ext JS, LLC.
11765 * Originally Released Under LGPL - original licence link has changed is not relivant.
11768 * <script type="text/javascript">
11772 * @class Roo.data.SimpleStore
11773 * @extends Roo.data.Store
11774 * Small helper class to make creating Stores from Array data easier.
11775 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11776 * @cfg {Array} fields An array of field definition objects, or field name strings.
11777 * @cfg {Array} data The multi-dimensional array of data
11779 * @param {Object} config
11781 Roo.data.SimpleStore = function(config){
11782 Roo.data.SimpleStore.superclass.constructor.call(this, {
11784 reader: new Roo.data.ArrayReader({
11787 Roo.data.Record.create(config.fields)
11789 proxy : new Roo.data.MemoryProxy(config.data)
11793 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11795 * Ext JS Library 1.1.1
11796 * Copyright(c) 2006-2007, Ext JS, LLC.
11798 * Originally Released Under LGPL - original licence link has changed is not relivant.
11801 * <script type="text/javascript">
11806 * @extends Roo.data.Store
11807 * @class Roo.data.JsonStore
11808 * Small helper class to make creating Stores for JSON data easier. <br/>
11810 var store = new Roo.data.JsonStore({
11811 url: 'get-images.php',
11813 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11816 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11817 * JsonReader and HttpProxy (unless inline data is provided).</b>
11818 * @cfg {Array} fields An array of field definition objects, or field name strings.
11820 * @param {Object} config
11822 Roo.data.JsonStore = function(c){
11823 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11824 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11825 reader: new Roo.data.JsonReader(c, c.fields)
11828 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11830 * Ext JS Library 1.1.1
11831 * Copyright(c) 2006-2007, Ext JS, LLC.
11833 * Originally Released Under LGPL - original licence link has changed is not relivant.
11836 * <script type="text/javascript">
11840 Roo.data.Field = function(config){
11841 if(typeof config == "string"){
11842 config = {name: config};
11844 Roo.apply(this, config);
11847 this.type = "auto";
11850 var st = Roo.data.SortTypes;
11851 // named sortTypes are supported, here we look them up
11852 if(typeof this.sortType == "string"){
11853 this.sortType = st[this.sortType];
11856 // set default sortType for strings and dates
11857 if(!this.sortType){
11860 this.sortType = st.asUCString;
11863 this.sortType = st.asDate;
11866 this.sortType = st.none;
11871 var stripRe = /[\$,%]/g;
11873 // prebuilt conversion function for this field, instead of
11874 // switching every time we're reading a value
11876 var cv, dateFormat = this.dateFormat;
11881 cv = function(v){ return v; };
11884 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11888 return v !== undefined && v !== null && v !== '' ?
11889 parseInt(String(v).replace(stripRe, ""), 10) : '';
11894 return v !== undefined && v !== null && v !== '' ?
11895 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11900 cv = function(v){ return v === true || v === "true" || v == 1; };
11907 if(v instanceof Date){
11911 if(dateFormat == "timestamp"){
11912 return new Date(v*1000);
11914 return Date.parseDate(v, dateFormat);
11916 var parsed = Date.parse(v);
11917 return parsed ? new Date(parsed) : null;
11926 Roo.data.Field.prototype = {
11934 * Ext JS Library 1.1.1
11935 * Copyright(c) 2006-2007, Ext JS, LLC.
11937 * Originally Released Under LGPL - original licence link has changed is not relivant.
11940 * <script type="text/javascript">
11943 // Base class for reading structured data from a data source. This class is intended to be
11944 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11947 * @class Roo.data.DataReader
11948 * Base class for reading structured data from a data source. This class is intended to be
11949 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11952 Roo.data.DataReader = function(meta, recordType){
11956 this.recordType = recordType instanceof Array ?
11957 Roo.data.Record.create(recordType) : recordType;
11960 Roo.data.DataReader.prototype = {
11962 * Create an empty record
11963 * @param {Object} data (optional) - overlay some values
11964 * @return {Roo.data.Record} record created.
11966 newRow : function(d) {
11968 this.recordType.prototype.fields.each(function(c) {
11970 case 'int' : da[c.name] = 0; break;
11971 case 'date' : da[c.name] = new Date(); break;
11972 case 'float' : da[c.name] = 0.0; break;
11973 case 'boolean' : da[c.name] = false; break;
11974 default : da[c.name] = ""; break;
11978 return new this.recordType(Roo.apply(da, d));
11983 * Ext JS Library 1.1.1
11984 * Copyright(c) 2006-2007, Ext JS, LLC.
11986 * Originally Released Under LGPL - original licence link has changed is not relivant.
11989 * <script type="text/javascript">
11993 * @class Roo.data.DataProxy
11994 * @extends Roo.data.Observable
11995 * This class is an abstract base class for implementations which provide retrieval of
11996 * unformatted data objects.<br>
11998 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11999 * (of the appropriate type which knows how to parse the data object) to provide a block of
12000 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12002 * Custom implementations must implement the load method as described in
12003 * {@link Roo.data.HttpProxy#load}.
12005 Roo.data.DataProxy = function(){
12008 * @event beforeload
12009 * Fires before a network request is made to retrieve a data object.
12010 * @param {Object} This DataProxy object.
12011 * @param {Object} params The params parameter to the load function.
12016 * Fires before the load method's callback is called.
12017 * @param {Object} This DataProxy object.
12018 * @param {Object} o The data object.
12019 * @param {Object} arg The callback argument object passed to the load function.
12023 * @event loadexception
12024 * Fires if an Exception occurs during data retrieval.
12025 * @param {Object} This DataProxy object.
12026 * @param {Object} o The data object.
12027 * @param {Object} arg The callback argument object passed to the load function.
12028 * @param {Object} e The Exception.
12030 loadexception : true
12032 Roo.data.DataProxy.superclass.constructor.call(this);
12035 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12038 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12042 * Ext JS Library 1.1.1
12043 * Copyright(c) 2006-2007, Ext JS, LLC.
12045 * Originally Released Under LGPL - original licence link has changed is not relivant.
12048 * <script type="text/javascript">
12051 * @class Roo.data.MemoryProxy
12052 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12053 * to the Reader when its load method is called.
12055 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12057 Roo.data.MemoryProxy = function(data){
12061 Roo.data.MemoryProxy.superclass.constructor.call(this);
12065 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12068 * Load data from the requested source (in this case an in-memory
12069 * data object passed to the constructor), read the data object into
12070 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12071 * process that block using the passed callback.
12072 * @param {Object} params This parameter is not used by the MemoryProxy class.
12073 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12074 * object into a block of Roo.data.Records.
12075 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12076 * The function must be passed <ul>
12077 * <li>The Record block object</li>
12078 * <li>The "arg" argument from the load function</li>
12079 * <li>A boolean success indicator</li>
12081 * @param {Object} scope The scope in which to call the callback
12082 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12084 load : function(params, reader, callback, scope, arg){
12085 params = params || {};
12088 result = reader.readRecords(this.data);
12090 this.fireEvent("loadexception", this, arg, null, e);
12091 callback.call(scope, null, arg, false);
12094 callback.call(scope, result, arg, true);
12098 update : function(params, records){
12103 * Ext JS Library 1.1.1
12104 * Copyright(c) 2006-2007, Ext JS, LLC.
12106 * Originally Released Under LGPL - original licence link has changed is not relivant.
12109 * <script type="text/javascript">
12112 * @class Roo.data.HttpProxy
12113 * @extends Roo.data.DataProxy
12114 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12115 * configured to reference a certain URL.<br><br>
12117 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12118 * from which the running page was served.<br><br>
12120 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12122 * Be aware that to enable the browser to parse an XML document, the server must set
12123 * the Content-Type header in the HTTP response to "text/xml".
12125 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12126 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12127 * will be used to make the request.
12129 Roo.data.HttpProxy = function(conn){
12130 Roo.data.HttpProxy.superclass.constructor.call(this);
12131 // is conn a conn config or a real conn?
12133 this.useAjax = !conn || !conn.events;
12137 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12138 // thse are take from connection...
12141 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12144 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12145 * extra parameters to each request made by this object. (defaults to undefined)
12148 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12149 * to each request made by this object. (defaults to undefined)
12152 * @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)
12155 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12158 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12164 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12168 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12169 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12170 * a finer-grained basis than the DataProxy events.
12172 getConnection : function(){
12173 return this.useAjax ? Roo.Ajax : this.conn;
12177 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12178 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12179 * process that block using the passed callback.
12180 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12181 * for the request to the remote server.
12182 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12183 * object into a block of Roo.data.Records.
12184 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12185 * The function must be passed <ul>
12186 * <li>The Record block object</li>
12187 * <li>The "arg" argument from the load function</li>
12188 * <li>A boolean success indicator</li>
12190 * @param {Object} scope The scope in which to call the callback
12191 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12193 load : function(params, reader, callback, scope, arg){
12194 if(this.fireEvent("beforeload", this, params) !== false){
12196 params : params || {},
12198 callback : callback,
12203 callback : this.loadResponse,
12207 Roo.applyIf(o, this.conn);
12208 if(this.activeRequest){
12209 Roo.Ajax.abort(this.activeRequest);
12211 this.activeRequest = Roo.Ajax.request(o);
12213 this.conn.request(o);
12216 callback.call(scope||this, null, arg, false);
12221 loadResponse : function(o, success, response){
12222 delete this.activeRequest;
12224 this.fireEvent("loadexception", this, o, response);
12225 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12230 result = o.reader.read(response);
12232 this.fireEvent("loadexception", this, o, response, e);
12233 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12237 this.fireEvent("load", this, o, o.request.arg);
12238 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12242 update : function(dataSet){
12247 updateResponse : function(dataSet){
12252 * Ext JS Library 1.1.1
12253 * Copyright(c) 2006-2007, Ext JS, LLC.
12255 * Originally Released Under LGPL - original licence link has changed is not relivant.
12258 * <script type="text/javascript">
12262 * @class Roo.data.ScriptTagProxy
12263 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12264 * other than the originating domain of the running page.<br><br>
12266 * <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
12267 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12269 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12270 * source code that is used as the source inside a <script> tag.<br><br>
12272 * In order for the browser to process the returned data, the server must wrap the data object
12273 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12274 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12275 * depending on whether the callback name was passed:
12278 boolean scriptTag = false;
12279 String cb = request.getParameter("callback");
12282 response.setContentType("text/javascript");
12284 response.setContentType("application/x-json");
12286 Writer out = response.getWriter();
12288 out.write(cb + "(");
12290 out.print(dataBlock.toJsonString());
12297 * @param {Object} config A configuration object.
12299 Roo.data.ScriptTagProxy = function(config){
12300 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12301 Roo.apply(this, config);
12302 this.head = document.getElementsByTagName("head")[0];
12305 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12307 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12309 * @cfg {String} url The URL from which to request the data object.
12312 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12316 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12317 * the server the name of the callback function set up by the load call to process the returned data object.
12318 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12319 * javascript output which calls this named function passing the data object as its only parameter.
12321 callbackParam : "callback",
12323 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12324 * name to the request.
12329 * Load data from the configured URL, read the data object into
12330 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12331 * process that block using the passed callback.
12332 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12333 * for the request to the remote server.
12334 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12335 * object into a block of Roo.data.Records.
12336 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12337 * The function must be passed <ul>
12338 * <li>The Record block object</li>
12339 * <li>The "arg" argument from the load function</li>
12340 * <li>A boolean success indicator</li>
12342 * @param {Object} scope The scope in which to call the callback
12343 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12345 load : function(params, reader, callback, scope, arg){
12346 if(this.fireEvent("beforeload", this, params) !== false){
12348 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12350 var url = this.url;
12351 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12353 url += "&_dc=" + (new Date().getTime());
12355 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12358 cb : "stcCallback"+transId,
12359 scriptId : "stcScript"+transId,
12363 callback : callback,
12369 window[trans.cb] = function(o){
12370 conn.handleResponse(o, trans);
12373 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12375 if(this.autoAbort !== false){
12379 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12381 var script = document.createElement("script");
12382 script.setAttribute("src", url);
12383 script.setAttribute("type", "text/javascript");
12384 script.setAttribute("id", trans.scriptId);
12385 this.head.appendChild(script);
12387 this.trans = trans;
12389 callback.call(scope||this, null, arg, false);
12394 isLoading : function(){
12395 return this.trans ? true : false;
12399 * Abort the current server request.
12401 abort : function(){
12402 if(this.isLoading()){
12403 this.destroyTrans(this.trans);
12408 destroyTrans : function(trans, isLoaded){
12409 this.head.removeChild(document.getElementById(trans.scriptId));
12410 clearTimeout(trans.timeoutId);
12412 window[trans.cb] = undefined;
12414 delete window[trans.cb];
12417 // if hasn't been loaded, wait for load to remove it to prevent script error
12418 window[trans.cb] = function(){
12419 window[trans.cb] = undefined;
12421 delete window[trans.cb];
12428 handleResponse : function(o, trans){
12429 this.trans = false;
12430 this.destroyTrans(trans, true);
12433 result = trans.reader.readRecords(o);
12435 this.fireEvent("loadexception", this, o, trans.arg, e);
12436 trans.callback.call(trans.scope||window, null, trans.arg, false);
12439 this.fireEvent("load", this, o, trans.arg);
12440 trans.callback.call(trans.scope||window, result, trans.arg, true);
12444 handleFailure : function(trans){
12445 this.trans = false;
12446 this.destroyTrans(trans, false);
12447 this.fireEvent("loadexception", this, null, trans.arg);
12448 trans.callback.call(trans.scope||window, null, trans.arg, false);
12452 * Ext JS Library 1.1.1
12453 * Copyright(c) 2006-2007, Ext JS, LLC.
12455 * Originally Released Under LGPL - original licence link has changed is not relivant.
12458 * <script type="text/javascript">
12462 * @class Roo.data.JsonReader
12463 * @extends Roo.data.DataReader
12464 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12465 * based on mappings in a provided Roo.data.Record constructor.
12467 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12468 * in the reply previously.
12473 var RecordDef = Roo.data.Record.create([
12474 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12475 {name: 'occupation'} // This field will use "occupation" as the mapping.
12477 var myReader = new Roo.data.JsonReader({
12478 totalProperty: "results", // The property which contains the total dataset size (optional)
12479 root: "rows", // The property which contains an Array of row objects
12480 id: "id" // The property within each row object that provides an ID for the record (optional)
12484 * This would consume a JSON file like this:
12486 { 'results': 2, 'rows': [
12487 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12488 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12491 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12492 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12493 * paged from the remote server.
12494 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12495 * @cfg {String} root name of the property which contains the Array of row objects.
12496 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12497 * @cfg {Array} fields Array of field definition objects
12499 * Create a new JsonReader
12500 * @param {Object} meta Metadata configuration options
12501 * @param {Object} recordType Either an Array of field definition objects,
12502 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12504 Roo.data.JsonReader = function(meta, recordType){
12507 // set some defaults:
12508 Roo.applyIf(meta, {
12509 totalProperty: 'total',
12510 successProperty : 'success',
12515 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12517 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12520 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12521 * Used by Store query builder to append _requestMeta to params.
12524 metaFromRemote : false,
12526 * This method is only used by a DataProxy which has retrieved data from a remote server.
12527 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12528 * @return {Object} data A data block which is used by an Roo.data.Store object as
12529 * a cache of Roo.data.Records.
12531 read : function(response){
12532 var json = response.responseText;
12534 var o = /* eval:var:o */ eval("("+json+")");
12536 throw {message: "JsonReader.read: Json object not found"};
12542 this.metaFromRemote = true;
12543 this.meta = o.metaData;
12544 this.recordType = Roo.data.Record.create(o.metaData.fields);
12545 this.onMetaChange(this.meta, this.recordType, o);
12547 return this.readRecords(o);
12550 // private function a store will implement
12551 onMetaChange : function(meta, recordType, o){
12558 simpleAccess: function(obj, subsc) {
12565 getJsonAccessor: function(){
12567 return function(expr) {
12569 return(re.test(expr))
12570 ? new Function("obj", "return obj." + expr)
12575 return Roo.emptyFn;
12580 * Create a data block containing Roo.data.Records from an XML document.
12581 * @param {Object} o An object which contains an Array of row objects in the property specified
12582 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12583 * which contains the total size of the dataset.
12584 * @return {Object} data A data block which is used by an Roo.data.Store object as
12585 * a cache of Roo.data.Records.
12587 readRecords : function(o){
12589 * After any data loads, the raw JSON data is available for further custom processing.
12593 var s = this.meta, Record = this.recordType,
12594 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12596 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12598 if(s.totalProperty) {
12599 this.getTotal = this.getJsonAccessor(s.totalProperty);
12601 if(s.successProperty) {
12602 this.getSuccess = this.getJsonAccessor(s.successProperty);
12604 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12606 var g = this.getJsonAccessor(s.id);
12607 this.getId = function(rec) {
12609 return (r === undefined || r === "") ? null : r;
12612 this.getId = function(){return null;};
12615 for(var jj = 0; jj < fl; jj++){
12617 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12618 this.ef[jj] = this.getJsonAccessor(map);
12622 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12623 if(s.totalProperty){
12624 var vt = parseInt(this.getTotal(o), 10);
12629 if(s.successProperty){
12630 var vs = this.getSuccess(o);
12631 if(vs === false || vs === 'false'){
12636 for(var i = 0; i < c; i++){
12639 var id = this.getId(n);
12640 for(var j = 0; j < fl; j++){
12642 var v = this.ef[j](n);
12644 Roo.log('missing convert for ' + f.name);
12648 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12650 var record = new Record(values, id);
12652 records[i] = record;
12658 totalRecords : totalRecords
12663 * Ext JS Library 1.1.1
12664 * Copyright(c) 2006-2007, Ext JS, LLC.
12666 * Originally Released Under LGPL - original licence link has changed is not relivant.
12669 * <script type="text/javascript">
12673 * @class Roo.data.ArrayReader
12674 * @extends Roo.data.DataReader
12675 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12676 * Each element of that Array represents a row of data fields. The
12677 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12678 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12682 var RecordDef = Roo.data.Record.create([
12683 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12684 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12686 var myReader = new Roo.data.ArrayReader({
12687 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12691 * This would consume an Array like this:
12693 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12695 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12697 * Create a new JsonReader
12698 * @param {Object} meta Metadata configuration options.
12699 * @param {Object} recordType Either an Array of field definition objects
12700 * as specified to {@link Roo.data.Record#create},
12701 * or an {@link Roo.data.Record} object
12702 * created using {@link Roo.data.Record#create}.
12704 Roo.data.ArrayReader = function(meta, recordType){
12705 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12708 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12710 * Create a data block containing Roo.data.Records from an XML document.
12711 * @param {Object} o An Array of row objects which represents the dataset.
12712 * @return {Object} data A data block which is used by an Roo.data.Store object as
12713 * a cache of Roo.data.Records.
12715 readRecords : function(o){
12716 var sid = this.meta ? this.meta.id : null;
12717 var recordType = this.recordType, fields = recordType.prototype.fields;
12720 for(var i = 0; i < root.length; i++){
12723 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12724 for(var j = 0, jlen = fields.length; j < jlen; j++){
12725 var f = fields.items[j];
12726 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12727 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12729 values[f.name] = v;
12731 var record = new recordType(values, id);
12733 records[records.length] = record;
12737 totalRecords : records.length
12746 * @class Roo.bootstrap.ComboBox
12747 * @extends Roo.bootstrap.TriggerField
12748 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12749 * @cfg {Boolean} append (true|false) default false
12750 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12751 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12752 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12753 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12754 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12755 * @cfg {Boolean} animate default true
12756 * @cfg {Boolean} emptyResultText only for touch device
12757 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12758 * @cfg {String} emptyTitle default ''
12760 * Create a new ComboBox.
12761 * @param {Object} config Configuration options
12763 Roo.bootstrap.ComboBox = function(config){
12764 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12768 * Fires when the dropdown list is expanded
12769 * @param {Roo.bootstrap.ComboBox} combo This combo box
12774 * Fires when the dropdown list is collapsed
12775 * @param {Roo.bootstrap.ComboBox} combo This combo box
12779 * @event beforeselect
12780 * Fires before a list item is selected. Return false to cancel the selection.
12781 * @param {Roo.bootstrap.ComboBox} combo This combo box
12782 * @param {Roo.data.Record} record The data record returned from the underlying store
12783 * @param {Number} index The index of the selected item in the dropdown list
12785 'beforeselect' : true,
12788 * Fires when a list item is selected
12789 * @param {Roo.bootstrap.ComboBox} combo This combo box
12790 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12791 * @param {Number} index The index of the selected item in the dropdown list
12795 * @event beforequery
12796 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12797 * The event object passed has these properties:
12798 * @param {Roo.bootstrap.ComboBox} combo This combo box
12799 * @param {String} query The query
12800 * @param {Boolean} forceAll true to force "all" query
12801 * @param {Boolean} cancel true to cancel the query
12802 * @param {Object} e The query event object
12804 'beforequery': true,
12807 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12808 * @param {Roo.bootstrap.ComboBox} combo This combo box
12813 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12814 * @param {Roo.bootstrap.ComboBox} combo This combo box
12815 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12820 * Fires when the remove value from the combobox array
12821 * @param {Roo.bootstrap.ComboBox} combo This combo box
12825 * @event afterremove
12826 * Fires when the remove value from the combobox array
12827 * @param {Roo.bootstrap.ComboBox} combo This combo box
12829 'afterremove' : true,
12831 * @event specialfilter
12832 * Fires when specialfilter
12833 * @param {Roo.bootstrap.ComboBox} combo This combo box
12835 'specialfilter' : true,
12838 * Fires when tick the element
12839 * @param {Roo.bootstrap.ComboBox} combo This combo box
12843 * @event touchviewdisplay
12844 * Fires when touch view require special display (default is using displayField)
12845 * @param {Roo.bootstrap.ComboBox} combo This combo box
12846 * @param {Object} cfg set html .
12848 'touchviewdisplay' : true
12853 this.tickItems = [];
12855 this.selectedIndex = -1;
12856 if(this.mode == 'local'){
12857 if(config.queryDelay === undefined){
12858 this.queryDelay = 10;
12860 if(config.minChars === undefined){
12866 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12869 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12870 * rendering into an Roo.Editor, defaults to false)
12873 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12874 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12877 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12880 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12881 * the dropdown list (defaults to undefined, with no header element)
12885 * @cfg {String/Roo.Template} tpl The template to use to render the output
12889 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12891 listWidth: undefined,
12893 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12894 * mode = 'remote' or 'text' if mode = 'local')
12896 displayField: undefined,
12899 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12900 * mode = 'remote' or 'value' if mode = 'local').
12901 * Note: use of a valueField requires the user make a selection
12902 * in order for a value to be mapped.
12904 valueField: undefined,
12906 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12911 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12912 * field's data value (defaults to the underlying DOM element's name)
12914 hiddenName: undefined,
12916 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12920 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12922 selectedClass: 'active',
12925 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12929 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12930 * anchor positions (defaults to 'tl-bl')
12932 listAlign: 'tl-bl?',
12934 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12938 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12939 * query specified by the allQuery config option (defaults to 'query')
12941 triggerAction: 'query',
12943 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12944 * (defaults to 4, does not apply if editable = false)
12948 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12949 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12953 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12954 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12958 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12959 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12963 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12964 * when editable = true (defaults to false)
12966 selectOnFocus:false,
12968 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12970 queryParam: 'query',
12972 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12973 * when mode = 'remote' (defaults to 'Loading...')
12975 loadingText: 'Loading...',
12977 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12981 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12985 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12986 * traditional select (defaults to true)
12990 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12994 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12998 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12999 * listWidth has a higher value)
13003 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13004 * allow the user to set arbitrary text into the field (defaults to false)
13006 forceSelection:false,
13008 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13009 * if typeAhead = true (defaults to 250)
13011 typeAheadDelay : 250,
13013 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13014 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13016 valueNotFoundText : undefined,
13018 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13020 blockFocus : false,
13023 * @cfg {Boolean} disableClear Disable showing of clear button.
13025 disableClear : false,
13027 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13029 alwaysQuery : false,
13032 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13037 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13039 invalidClass : "has-warning",
13042 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13044 validClass : "has-success",
13047 * @cfg {Boolean} specialFilter (true|false) special filter default false
13049 specialFilter : false,
13052 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13054 mobileTouchView : true,
13057 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13059 useNativeIOS : false,
13061 ios_options : false,
13073 btnPosition : 'right',
13074 triggerList : true,
13075 showToggleBtn : true,
13077 emptyResultText: 'Empty',
13078 triggerText : 'Select',
13081 // element that contains real text value.. (when hidden is used..)
13083 getAutoCreate : function()
13088 * Render classic select for iso
13091 if(Roo.isIOS && this.useNativeIOS){
13092 cfg = this.getAutoCreateNativeIOS();
13100 if(Roo.isTouch && this.mobileTouchView){
13101 cfg = this.getAutoCreateTouchView();
13108 if(!this.tickable){
13109 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13114 * ComboBox with tickable selections
13117 var align = this.labelAlign || this.parentLabelAlign();
13120 cls : 'form-group roo-combobox-tickable' //input-group
13123 var btn_text_select = '';
13124 var btn_text_done = '';
13125 var btn_text_cancel = '';
13127 if (this.btn_text_show) {
13128 btn_text_select = 'Select';
13129 btn_text_done = 'Done';
13130 btn_text_cancel = 'Cancel';
13135 cls : 'tickable-buttons',
13140 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13141 //html : this.triggerText
13142 html: btn_text_select
13148 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13150 html: btn_text_done
13156 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13158 html: btn_text_cancel
13164 buttons.cn.unshift({
13166 cls: 'roo-select2-search-field-input'
13172 Roo.each(buttons.cn, function(c){
13174 c.cls += ' btn-' + _this.size;
13177 if (_this.disabled) {
13188 cls: 'form-hidden-field'
13192 cls: 'roo-select2-choices',
13196 cls: 'roo-select2-search-field',
13207 cls: 'roo-select2-container input-group roo-select2-container-multi',
13212 // cls: 'typeahead typeahead-long dropdown-menu',
13213 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13218 if(this.hasFeedback && !this.allowBlank){
13222 cls: 'glyphicon form-control-feedback'
13225 combobox.cn.push(feedback);
13229 if (align ==='left' && this.fieldLabel.length) {
13231 cfg.cls += ' roo-form-group-label-left';
13236 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13237 tooltip : 'This field is required'
13242 cls : 'control-label',
13243 html : this.fieldLabel
13255 var labelCfg = cfg.cn[1];
13256 var contentCfg = cfg.cn[2];
13259 if(this.indicatorpos == 'right'){
13265 cls : 'control-label',
13269 html : this.fieldLabel
13273 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13274 tooltip : 'This field is required'
13289 labelCfg = cfg.cn[0];
13290 contentCfg = cfg.cn[1];
13294 if(this.labelWidth > 12){
13295 labelCfg.style = "width: " + this.labelWidth + 'px';
13298 if(this.labelWidth < 13 && this.labelmd == 0){
13299 this.labelmd = this.labelWidth;
13302 if(this.labellg > 0){
13303 labelCfg.cls += ' col-lg-' + this.labellg;
13304 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13307 if(this.labelmd > 0){
13308 labelCfg.cls += ' col-md-' + this.labelmd;
13309 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13312 if(this.labelsm > 0){
13313 labelCfg.cls += ' col-sm-' + this.labelsm;
13314 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13317 if(this.labelxs > 0){
13318 labelCfg.cls += ' col-xs-' + this.labelxs;
13319 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13323 } else if ( this.fieldLabel.length) {
13324 // Roo.log(" label");
13328 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13329 tooltip : 'This field is required'
13333 //cls : 'input-group-addon',
13334 html : this.fieldLabel
13339 if(this.indicatorpos == 'right'){
13343 //cls : 'input-group-addon',
13344 html : this.fieldLabel
13348 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13349 tooltip : 'This field is required'
13358 // Roo.log(" no label && no align");
13365 ['xs','sm','md','lg'].map(function(size){
13366 if (settings[size]) {
13367 cfg.cls += ' col-' + size + '-' + settings[size];
13375 _initEventsCalled : false,
13378 initEvents: function()
13380 if (this._initEventsCalled) { // as we call render... prevent looping...
13383 this._initEventsCalled = true;
13386 throw "can not find store for combo";
13389 this.indicator = this.indicatorEl();
13391 this.store = Roo.factory(this.store, Roo.data);
13392 this.store.parent = this;
13394 // if we are building from html. then this element is so complex, that we can not really
13395 // use the rendered HTML.
13396 // so we have to trash and replace the previous code.
13397 if (Roo.XComponent.build_from_html) {
13398 // remove this element....
13399 var e = this.el.dom, k=0;
13400 while (e ) { e = e.previousSibling; ++k;}
13405 this.rendered = false;
13407 this.render(this.parent().getChildContainer(true), k);
13410 if(Roo.isIOS && this.useNativeIOS){
13411 this.initIOSView();
13419 if(Roo.isTouch && this.mobileTouchView){
13420 this.initTouchView();
13425 this.initTickableEvents();
13429 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13431 if(this.hiddenName){
13433 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13435 this.hiddenField.dom.value =
13436 this.hiddenValue !== undefined ? this.hiddenValue :
13437 this.value !== undefined ? this.value : '';
13439 // prevent input submission
13440 this.el.dom.removeAttribute('name');
13441 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13446 // this.el.dom.setAttribute('autocomplete', 'off');
13449 var cls = 'x-combo-list';
13451 //this.list = new Roo.Layer({
13452 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13458 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13459 _this.list.setWidth(lw);
13462 this.list.on('mouseover', this.onViewOver, this);
13463 this.list.on('mousemove', this.onViewMove, this);
13464 this.list.on('scroll', this.onViewScroll, this);
13467 this.list.swallowEvent('mousewheel');
13468 this.assetHeight = 0;
13471 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13472 this.assetHeight += this.header.getHeight();
13475 this.innerList = this.list.createChild({cls:cls+'-inner'});
13476 this.innerList.on('mouseover', this.onViewOver, this);
13477 this.innerList.on('mousemove', this.onViewMove, this);
13478 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13480 if(this.allowBlank && !this.pageSize && !this.disableClear){
13481 this.footer = this.list.createChild({cls:cls+'-ft'});
13482 this.pageTb = new Roo.Toolbar(this.footer);
13486 this.footer = this.list.createChild({cls:cls+'-ft'});
13487 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13488 {pageSize: this.pageSize});
13492 if (this.pageTb && this.allowBlank && !this.disableClear) {
13494 this.pageTb.add(new Roo.Toolbar.Fill(), {
13495 cls: 'x-btn-icon x-btn-clear',
13497 handler: function()
13500 _this.clearValue();
13501 _this.onSelect(false, -1);
13506 this.assetHeight += this.footer.getHeight();
13511 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13514 this.view = new Roo.View(this.list, this.tpl, {
13515 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13517 //this.view.wrapEl.setDisplayed(false);
13518 this.view.on('click', this.onViewClick, this);
13521 this.store.on('beforeload', this.onBeforeLoad, this);
13522 this.store.on('load', this.onLoad, this);
13523 this.store.on('loadexception', this.onLoadException, this);
13525 if(this.resizable){
13526 this.resizer = new Roo.Resizable(this.list, {
13527 pinned:true, handles:'se'
13529 this.resizer.on('resize', function(r, w, h){
13530 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13531 this.listWidth = w;
13532 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13533 this.restrictHeight();
13535 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13538 if(!this.editable){
13539 this.editable = true;
13540 this.setEditable(false);
13545 if (typeof(this.events.add.listeners) != 'undefined') {
13547 this.addicon = this.wrap.createChild(
13548 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13550 this.addicon.on('click', function(e) {
13551 this.fireEvent('add', this);
13554 if (typeof(this.events.edit.listeners) != 'undefined') {
13556 this.editicon = this.wrap.createChild(
13557 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13558 if (this.addicon) {
13559 this.editicon.setStyle('margin-left', '40px');
13561 this.editicon.on('click', function(e) {
13563 // we fire even if inothing is selected..
13564 this.fireEvent('edit', this, this.lastData );
13570 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13571 "up" : function(e){
13572 this.inKeyMode = true;
13576 "down" : function(e){
13577 if(!this.isExpanded()){
13578 this.onTriggerClick();
13580 this.inKeyMode = true;
13585 "enter" : function(e){
13586 // this.onViewClick();
13590 if(this.fireEvent("specialkey", this, e)){
13591 this.onViewClick(false);
13597 "esc" : function(e){
13601 "tab" : function(e){
13604 if(this.fireEvent("specialkey", this, e)){
13605 this.onViewClick(false);
13613 doRelay : function(foo, bar, hname){
13614 if(hname == 'down' || this.scope.isExpanded()){
13615 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13624 this.queryDelay = Math.max(this.queryDelay || 10,
13625 this.mode == 'local' ? 10 : 250);
13628 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13630 if(this.typeAhead){
13631 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13633 if(this.editable !== false){
13634 this.inputEl().on("keyup", this.onKeyUp, this);
13636 if(this.forceSelection){
13637 this.inputEl().on('blur', this.doForce, this);
13641 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13642 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13646 initTickableEvents: function()
13650 if(this.hiddenName){
13652 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13654 this.hiddenField.dom.value =
13655 this.hiddenValue !== undefined ? this.hiddenValue :
13656 this.value !== undefined ? this.value : '';
13658 // prevent input submission
13659 this.el.dom.removeAttribute('name');
13660 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13665 // this.list = this.el.select('ul.dropdown-menu',true).first();
13667 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13668 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13669 if(this.triggerList){
13670 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13673 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13674 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13676 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13677 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13679 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13680 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13682 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13683 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13684 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13687 this.cancelBtn.hide();
13692 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13693 _this.list.setWidth(lw);
13696 this.list.on('mouseover', this.onViewOver, this);
13697 this.list.on('mousemove', this.onViewMove, this);
13699 this.list.on('scroll', this.onViewScroll, this);
13702 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13703 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13706 this.view = new Roo.View(this.list, this.tpl, {
13711 selectedClass: this.selectedClass
13714 //this.view.wrapEl.setDisplayed(false);
13715 this.view.on('click', this.onViewClick, this);
13719 this.store.on('beforeload', this.onBeforeLoad, this);
13720 this.store.on('load', this.onLoad, this);
13721 this.store.on('loadexception', this.onLoadException, this);
13724 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13725 "up" : function(e){
13726 this.inKeyMode = true;
13730 "down" : function(e){
13731 this.inKeyMode = true;
13735 "enter" : function(e){
13736 if(this.fireEvent("specialkey", this, e)){
13737 this.onViewClick(false);
13743 "esc" : function(e){
13744 this.onTickableFooterButtonClick(e, false, false);
13747 "tab" : function(e){
13748 this.fireEvent("specialkey", this, e);
13750 this.onTickableFooterButtonClick(e, false, false);
13757 doRelay : function(e, fn, key){
13758 if(this.scope.isExpanded()){
13759 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13768 this.queryDelay = Math.max(this.queryDelay || 10,
13769 this.mode == 'local' ? 10 : 250);
13772 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13774 if(this.typeAhead){
13775 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13778 if(this.editable !== false){
13779 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13782 this.indicator = this.indicatorEl();
13784 if(this.indicator){
13785 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13786 this.indicator.hide();
13791 onDestroy : function(){
13793 this.view.setStore(null);
13794 this.view.el.removeAllListeners();
13795 this.view.el.remove();
13796 this.view.purgeListeners();
13799 this.list.dom.innerHTML = '';
13803 this.store.un('beforeload', this.onBeforeLoad, this);
13804 this.store.un('load', this.onLoad, this);
13805 this.store.un('loadexception', this.onLoadException, this);
13807 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13811 fireKey : function(e){
13812 if(e.isNavKeyPress() && !this.list.isVisible()){
13813 this.fireEvent("specialkey", this, e);
13818 onResize: function(w, h){
13819 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13821 // if(typeof w != 'number'){
13822 // // we do not handle it!?!?
13825 // var tw = this.trigger.getWidth();
13826 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13827 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13829 // this.inputEl().setWidth( this.adjustWidth('input', x));
13831 // //this.trigger.setStyle('left', x+'px');
13833 // if(this.list && this.listWidth === undefined){
13834 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13835 // this.list.setWidth(lw);
13836 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13844 * Allow or prevent the user from directly editing the field text. If false is passed,
13845 * the user will only be able to select from the items defined in the dropdown list. This method
13846 * is the runtime equivalent of setting the 'editable' config option at config time.
13847 * @param {Boolean} value True to allow the user to directly edit the field text
13849 setEditable : function(value){
13850 if(value == this.editable){
13853 this.editable = value;
13855 this.inputEl().dom.setAttribute('readOnly', true);
13856 this.inputEl().on('mousedown', this.onTriggerClick, this);
13857 this.inputEl().addClass('x-combo-noedit');
13859 this.inputEl().dom.setAttribute('readOnly', false);
13860 this.inputEl().un('mousedown', this.onTriggerClick, this);
13861 this.inputEl().removeClass('x-combo-noedit');
13867 onBeforeLoad : function(combo,opts){
13868 if(!this.hasFocus){
13872 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13874 this.restrictHeight();
13875 this.selectedIndex = -1;
13879 onLoad : function(){
13881 this.hasQuery = false;
13883 if(!this.hasFocus){
13887 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13888 this.loading.hide();
13891 if(this.store.getCount() > 0){
13894 this.restrictHeight();
13895 if(this.lastQuery == this.allQuery){
13896 if(this.editable && !this.tickable){
13897 this.inputEl().dom.select();
13901 !this.selectByValue(this.value, true) &&
13904 !this.store.lastOptions ||
13905 typeof(this.store.lastOptions.add) == 'undefined' ||
13906 this.store.lastOptions.add != true
13909 this.select(0, true);
13912 if(this.autoFocus){
13915 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13916 this.taTask.delay(this.typeAheadDelay);
13920 this.onEmptyResults();
13926 onLoadException : function()
13928 this.hasQuery = false;
13930 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13931 this.loading.hide();
13934 if(this.tickable && this.editable){
13939 // only causes errors at present
13940 //Roo.log(this.store.reader.jsonData);
13941 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13943 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13949 onTypeAhead : function(){
13950 if(this.store.getCount() > 0){
13951 var r = this.store.getAt(0);
13952 var newValue = r.data[this.displayField];
13953 var len = newValue.length;
13954 var selStart = this.getRawValue().length;
13956 if(selStart != len){
13957 this.setRawValue(newValue);
13958 this.selectText(selStart, newValue.length);
13964 onSelect : function(record, index){
13966 if(this.fireEvent('beforeselect', this, record, index) !== false){
13968 this.setFromData(index > -1 ? record.data : false);
13971 this.fireEvent('select', this, record, index);
13976 * Returns the currently selected field value or empty string if no value is set.
13977 * @return {String} value The selected value
13979 getValue : function()
13981 if(Roo.isIOS && this.useNativeIOS){
13982 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13986 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13989 if(this.valueField){
13990 return typeof this.value != 'undefined' ? this.value : '';
13992 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13996 getRawValue : function()
13998 if(Roo.isIOS && this.useNativeIOS){
13999 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14002 var v = this.inputEl().getValue();
14008 * Clears any text/value currently set in the field
14010 clearValue : function(){
14012 if(this.hiddenField){
14013 this.hiddenField.dom.value = '';
14016 this.setRawValue('');
14017 this.lastSelectionText = '';
14018 this.lastData = false;
14020 var close = this.closeTriggerEl();
14031 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14032 * will be displayed in the field. If the value does not match the data value of an existing item,
14033 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14034 * Otherwise the field will be blank (although the value will still be set).
14035 * @param {String} value The value to match
14037 setValue : function(v)
14039 if(Roo.isIOS && this.useNativeIOS){
14040 this.setIOSValue(v);
14050 if(this.valueField){
14051 var r = this.findRecord(this.valueField, v);
14053 text = r.data[this.displayField];
14054 }else if(this.valueNotFoundText !== undefined){
14055 text = this.valueNotFoundText;
14058 this.lastSelectionText = text;
14059 if(this.hiddenField){
14060 this.hiddenField.dom.value = v;
14062 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14065 var close = this.closeTriggerEl();
14068 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14074 * @property {Object} the last set data for the element
14079 * Sets the value of the field based on a object which is related to the record format for the store.
14080 * @param {Object} value the value to set as. or false on reset?
14082 setFromData : function(o){
14089 var dv = ''; // display value
14090 var vv = ''; // value value..
14092 if (this.displayField) {
14093 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14095 // this is an error condition!!!
14096 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14099 if(this.valueField){
14100 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14103 var close = this.closeTriggerEl();
14106 if(dv.length || vv * 1 > 0){
14108 this.blockFocus=true;
14114 if(this.hiddenField){
14115 this.hiddenField.dom.value = vv;
14117 this.lastSelectionText = dv;
14118 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14122 // no hidden field.. - we store the value in 'value', but still display
14123 // display field!!!!
14124 this.lastSelectionText = dv;
14125 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14132 reset : function(){
14133 // overridden so that last data is reset..
14140 this.setValue(this.originalValue);
14141 //this.clearInvalid();
14142 this.lastData = false;
14144 this.view.clearSelections();
14150 findRecord : function(prop, value){
14152 if(this.store.getCount() > 0){
14153 this.store.each(function(r){
14154 if(r.data[prop] == value){
14164 getName: function()
14166 // returns hidden if it's set..
14167 if (!this.rendered) {return ''};
14168 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14172 onViewMove : function(e, t){
14173 this.inKeyMode = false;
14177 onViewOver : function(e, t){
14178 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14181 var item = this.view.findItemFromChild(t);
14184 var index = this.view.indexOf(item);
14185 this.select(index, false);
14190 onViewClick : function(view, doFocus, el, e)
14192 var index = this.view.getSelectedIndexes()[0];
14194 var r = this.store.getAt(index);
14198 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14205 Roo.each(this.tickItems, function(v,k){
14207 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14209 _this.tickItems.splice(k, 1);
14211 if(typeof(e) == 'undefined' && view == false){
14212 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14224 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14225 this.tickItems.push(r.data);
14228 if(typeof(e) == 'undefined' && view == false){
14229 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14236 this.onSelect(r, index);
14238 if(doFocus !== false && !this.blockFocus){
14239 this.inputEl().focus();
14244 restrictHeight : function(){
14245 //this.innerList.dom.style.height = '';
14246 //var inner = this.innerList.dom;
14247 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14248 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14249 //this.list.beginUpdate();
14250 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14251 this.list.alignTo(this.inputEl(), this.listAlign);
14252 this.list.alignTo(this.inputEl(), this.listAlign);
14253 //this.list.endUpdate();
14257 onEmptyResults : function(){
14259 if(this.tickable && this.editable){
14260 this.hasFocus = false;
14261 this.restrictHeight();
14269 * Returns true if the dropdown list is expanded, else false.
14271 isExpanded : function(){
14272 return this.list.isVisible();
14276 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14277 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14278 * @param {String} value The data value of the item to select
14279 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14280 * selected item if it is not currently in view (defaults to true)
14281 * @return {Boolean} True if the value matched an item in the list, else false
14283 selectByValue : function(v, scrollIntoView){
14284 if(v !== undefined && v !== null){
14285 var r = this.findRecord(this.valueField || this.displayField, v);
14287 this.select(this.store.indexOf(r), scrollIntoView);
14295 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14296 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14297 * @param {Number} index The zero-based index of the list item to select
14298 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14299 * selected item if it is not currently in view (defaults to true)
14301 select : function(index, scrollIntoView){
14302 this.selectedIndex = index;
14303 this.view.select(index);
14304 if(scrollIntoView !== false){
14305 var el = this.view.getNode(index);
14307 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14310 this.list.scrollChildIntoView(el, false);
14316 selectNext : function(){
14317 var ct = this.store.getCount();
14319 if(this.selectedIndex == -1){
14321 }else if(this.selectedIndex < ct-1){
14322 this.select(this.selectedIndex+1);
14328 selectPrev : function(){
14329 var ct = this.store.getCount();
14331 if(this.selectedIndex == -1){
14333 }else if(this.selectedIndex != 0){
14334 this.select(this.selectedIndex-1);
14340 onKeyUp : function(e){
14341 if(this.editable !== false && !e.isSpecialKey()){
14342 this.lastKey = e.getKey();
14343 this.dqTask.delay(this.queryDelay);
14348 validateBlur : function(){
14349 return !this.list || !this.list.isVisible();
14353 initQuery : function(){
14355 var v = this.getRawValue();
14357 if(this.tickable && this.editable){
14358 v = this.tickableInputEl().getValue();
14365 doForce : function(){
14366 if(this.inputEl().dom.value.length > 0){
14367 this.inputEl().dom.value =
14368 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14374 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14375 * query allowing the query action to be canceled if needed.
14376 * @param {String} query The SQL query to execute
14377 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14378 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14379 * saved in the current store (defaults to false)
14381 doQuery : function(q, forceAll){
14383 if(q === undefined || q === null){
14388 forceAll: forceAll,
14392 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14397 forceAll = qe.forceAll;
14398 if(forceAll === true || (q.length >= this.minChars)){
14400 this.hasQuery = true;
14402 if(this.lastQuery != q || this.alwaysQuery){
14403 this.lastQuery = q;
14404 if(this.mode == 'local'){
14405 this.selectedIndex = -1;
14407 this.store.clearFilter();
14410 if(this.specialFilter){
14411 this.fireEvent('specialfilter', this);
14416 this.store.filter(this.displayField, q);
14419 this.store.fireEvent("datachanged", this.store);
14426 this.store.baseParams[this.queryParam] = q;
14428 var options = {params : this.getParams(q)};
14431 options.add = true;
14432 options.params.start = this.page * this.pageSize;
14435 this.store.load(options);
14438 * this code will make the page width larger, at the beginning, the list not align correctly,
14439 * we should expand the list on onLoad
14440 * so command out it
14445 this.selectedIndex = -1;
14450 this.loadNext = false;
14454 getParams : function(q){
14456 //p[this.queryParam] = q;
14460 p.limit = this.pageSize;
14466 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14468 collapse : function(){
14469 if(!this.isExpanded()){
14475 this.hasFocus = false;
14479 this.cancelBtn.hide();
14480 this.trigger.show();
14483 this.tickableInputEl().dom.value = '';
14484 this.tickableInputEl().blur();
14489 Roo.get(document).un('mousedown', this.collapseIf, this);
14490 Roo.get(document).un('mousewheel', this.collapseIf, this);
14491 if (!this.editable) {
14492 Roo.get(document).un('keydown', this.listKeyPress, this);
14494 this.fireEvent('collapse', this);
14500 collapseIf : function(e){
14501 var in_combo = e.within(this.el);
14502 var in_list = e.within(this.list);
14503 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14505 if (in_combo || in_list || is_list) {
14506 //e.stopPropagation();
14511 this.onTickableFooterButtonClick(e, false, false);
14519 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14521 expand : function(){
14523 if(this.isExpanded() || !this.hasFocus){
14527 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14528 this.list.setWidth(lw);
14534 this.restrictHeight();
14538 this.tickItems = Roo.apply([], this.item);
14541 this.cancelBtn.show();
14542 this.trigger.hide();
14545 this.tickableInputEl().focus();
14550 Roo.get(document).on('mousedown', this.collapseIf, this);
14551 Roo.get(document).on('mousewheel', this.collapseIf, this);
14552 if (!this.editable) {
14553 Roo.get(document).on('keydown', this.listKeyPress, this);
14556 this.fireEvent('expand', this);
14560 // Implements the default empty TriggerField.onTriggerClick function
14561 onTriggerClick : function(e)
14563 Roo.log('trigger click');
14565 if(this.disabled || !this.triggerList){
14570 this.loadNext = false;
14572 if(this.isExpanded()){
14574 if (!this.blockFocus) {
14575 this.inputEl().focus();
14579 this.hasFocus = true;
14580 if(this.triggerAction == 'all') {
14581 this.doQuery(this.allQuery, true);
14583 this.doQuery(this.getRawValue());
14585 if (!this.blockFocus) {
14586 this.inputEl().focus();
14591 onTickableTriggerClick : function(e)
14598 this.loadNext = false;
14599 this.hasFocus = true;
14601 if(this.triggerAction == 'all') {
14602 this.doQuery(this.allQuery, true);
14604 this.doQuery(this.getRawValue());
14608 onSearchFieldClick : function(e)
14610 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14611 this.onTickableFooterButtonClick(e, false, false);
14615 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14620 this.loadNext = false;
14621 this.hasFocus = true;
14623 if(this.triggerAction == 'all') {
14624 this.doQuery(this.allQuery, true);
14626 this.doQuery(this.getRawValue());
14630 listKeyPress : function(e)
14632 //Roo.log('listkeypress');
14633 // scroll to first matching element based on key pres..
14634 if (e.isSpecialKey()) {
14637 var k = String.fromCharCode(e.getKey()).toUpperCase();
14640 var csel = this.view.getSelectedNodes();
14641 var cselitem = false;
14643 var ix = this.view.indexOf(csel[0]);
14644 cselitem = this.store.getAt(ix);
14645 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14651 this.store.each(function(v) {
14653 // start at existing selection.
14654 if (cselitem.id == v.id) {
14660 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14661 match = this.store.indexOf(v);
14667 if (match === false) {
14668 return true; // no more action?
14671 this.view.select(match);
14672 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14673 sn.scrollIntoView(sn.dom.parentNode, false);
14676 onViewScroll : function(e, t){
14678 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){
14682 this.hasQuery = true;
14684 this.loading = this.list.select('.loading', true).first();
14686 if(this.loading === null){
14687 this.list.createChild({
14689 cls: 'loading roo-select2-more-results roo-select2-active',
14690 html: 'Loading more results...'
14693 this.loading = this.list.select('.loading', true).first();
14695 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14697 this.loading.hide();
14700 this.loading.show();
14705 this.loadNext = true;
14707 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14712 addItem : function(o)
14714 var dv = ''; // display value
14716 if (this.displayField) {
14717 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14719 // this is an error condition!!!
14720 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14727 var choice = this.choices.createChild({
14729 cls: 'roo-select2-search-choice',
14738 cls: 'roo-select2-search-choice-close fa fa-times',
14743 }, this.searchField);
14745 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14747 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14755 this.inputEl().dom.value = '';
14760 onRemoveItem : function(e, _self, o)
14762 e.preventDefault();
14764 this.lastItem = Roo.apply([], this.item);
14766 var index = this.item.indexOf(o.data) * 1;
14769 Roo.log('not this item?!');
14773 this.item.splice(index, 1);
14778 this.fireEvent('remove', this, e);
14784 syncValue : function()
14786 if(!this.item.length){
14793 Roo.each(this.item, function(i){
14794 if(_this.valueField){
14795 value.push(i[_this.valueField]);
14802 this.value = value.join(',');
14804 if(this.hiddenField){
14805 this.hiddenField.dom.value = this.value;
14808 this.store.fireEvent("datachanged", this.store);
14813 clearItem : function()
14815 if(!this.multiple){
14821 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14829 if(this.tickable && !Roo.isTouch){
14830 this.view.refresh();
14834 inputEl: function ()
14836 if(Roo.isIOS && this.useNativeIOS){
14837 return this.el.select('select.roo-ios-select', true).first();
14840 if(Roo.isTouch && this.mobileTouchView){
14841 return this.el.select('input.form-control',true).first();
14845 return this.searchField;
14848 return this.el.select('input.form-control',true).first();
14851 onTickableFooterButtonClick : function(e, btn, el)
14853 e.preventDefault();
14855 this.lastItem = Roo.apply([], this.item);
14857 if(btn && btn.name == 'cancel'){
14858 this.tickItems = Roo.apply([], this.item);
14867 Roo.each(this.tickItems, function(o){
14875 validate : function()
14877 if(this.getVisibilityEl().hasClass('hidden')){
14881 var v = this.getRawValue();
14884 v = this.getValue();
14887 if(this.disabled || this.allowBlank || v.length){
14892 this.markInvalid();
14896 tickableInputEl : function()
14898 if(!this.tickable || !this.editable){
14899 return this.inputEl();
14902 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14906 getAutoCreateTouchView : function()
14911 cls: 'form-group' //input-group
14917 type : this.inputType,
14918 cls : 'form-control x-combo-noedit',
14919 autocomplete: 'new-password',
14920 placeholder : this.placeholder || '',
14925 input.name = this.name;
14929 input.cls += ' input-' + this.size;
14932 if (this.disabled) {
14933 input.disabled = true;
14944 inputblock.cls += ' input-group';
14946 inputblock.cn.unshift({
14948 cls : 'input-group-addon',
14953 if(this.removable && !this.multiple){
14954 inputblock.cls += ' roo-removable';
14956 inputblock.cn.push({
14959 cls : 'roo-combo-removable-btn close'
14963 if(this.hasFeedback && !this.allowBlank){
14965 inputblock.cls += ' has-feedback';
14967 inputblock.cn.push({
14969 cls: 'glyphicon form-control-feedback'
14976 inputblock.cls += (this.before) ? '' : ' input-group';
14978 inputblock.cn.push({
14980 cls : 'input-group-addon',
14991 cls: 'form-hidden-field'
15005 cls: 'form-hidden-field'
15009 cls: 'roo-select2-choices',
15013 cls: 'roo-select2-search-field',
15026 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15032 if(!this.multiple && this.showToggleBtn){
15039 if (this.caret != false) {
15042 cls: 'fa fa-' + this.caret
15049 cls : 'input-group-addon btn dropdown-toggle',
15054 cls: 'combobox-clear',
15068 combobox.cls += ' roo-select2-container-multi';
15071 var align = this.labelAlign || this.parentLabelAlign();
15073 if (align ==='left' && this.fieldLabel.length) {
15078 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15079 tooltip : 'This field is required'
15083 cls : 'control-label',
15084 html : this.fieldLabel
15095 var labelCfg = cfg.cn[1];
15096 var contentCfg = cfg.cn[2];
15099 if(this.indicatorpos == 'right'){
15104 cls : 'control-label',
15108 html : this.fieldLabel
15112 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15113 tooltip : 'This field is required'
15126 labelCfg = cfg.cn[0];
15127 contentCfg = cfg.cn[1];
15132 if(this.labelWidth > 12){
15133 labelCfg.style = "width: " + this.labelWidth + 'px';
15136 if(this.labelWidth < 13 && this.labelmd == 0){
15137 this.labelmd = this.labelWidth;
15140 if(this.labellg > 0){
15141 labelCfg.cls += ' col-lg-' + this.labellg;
15142 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15145 if(this.labelmd > 0){
15146 labelCfg.cls += ' col-md-' + this.labelmd;
15147 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15150 if(this.labelsm > 0){
15151 labelCfg.cls += ' col-sm-' + this.labelsm;
15152 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15155 if(this.labelxs > 0){
15156 labelCfg.cls += ' col-xs-' + this.labelxs;
15157 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15161 } else if ( this.fieldLabel.length) {
15165 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15166 tooltip : 'This field is required'
15170 cls : 'control-label',
15171 html : this.fieldLabel
15182 if(this.indicatorpos == 'right'){
15186 cls : 'control-label',
15187 html : this.fieldLabel,
15191 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15192 tooltip : 'This field is required'
15209 var settings = this;
15211 ['xs','sm','md','lg'].map(function(size){
15212 if (settings[size]) {
15213 cfg.cls += ' col-' + size + '-' + settings[size];
15220 initTouchView : function()
15222 this.renderTouchView();
15224 this.touchViewEl.on('scroll', function(){
15225 this.el.dom.scrollTop = 0;
15228 this.originalValue = this.getValue();
15230 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15232 this.inputEl().on("click", this.showTouchView, this);
15233 if (this.triggerEl) {
15234 this.triggerEl.on("click", this.showTouchView, this);
15238 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15239 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15241 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15243 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15244 this.store.on('load', this.onTouchViewLoad, this);
15245 this.store.on('loadexception', this.onTouchViewLoadException, this);
15247 if(this.hiddenName){
15249 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15251 this.hiddenField.dom.value =
15252 this.hiddenValue !== undefined ? this.hiddenValue :
15253 this.value !== undefined ? this.value : '';
15255 this.el.dom.removeAttribute('name');
15256 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15260 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15261 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15264 if(this.removable && !this.multiple){
15265 var close = this.closeTriggerEl();
15267 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15268 close.on('click', this.removeBtnClick, this, close);
15272 * fix the bug in Safari iOS8
15274 this.inputEl().on("focus", function(e){
15275 document.activeElement.blur();
15283 renderTouchView : function()
15285 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15286 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15288 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15289 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15291 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15292 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15293 this.touchViewBodyEl.setStyle('overflow', 'auto');
15295 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15296 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15298 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15299 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15303 showTouchView : function()
15309 this.touchViewHeaderEl.hide();
15311 if(this.modalTitle.length){
15312 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15313 this.touchViewHeaderEl.show();
15316 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15317 this.touchViewEl.show();
15319 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15321 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15322 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15324 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15326 if(this.modalTitle.length){
15327 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15330 this.touchViewBodyEl.setHeight(bodyHeight);
15334 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15336 this.touchViewEl.addClass('in');
15339 this.doTouchViewQuery();
15343 hideTouchView : function()
15345 this.touchViewEl.removeClass('in');
15349 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15351 this.touchViewEl.setStyle('display', 'none');
15356 setTouchViewValue : function()
15363 Roo.each(this.tickItems, function(o){
15368 this.hideTouchView();
15371 doTouchViewQuery : function()
15380 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15384 if(!this.alwaysQuery || this.mode == 'local'){
15385 this.onTouchViewLoad();
15392 onTouchViewBeforeLoad : function(combo,opts)
15398 onTouchViewLoad : function()
15400 if(this.store.getCount() < 1){
15401 this.onTouchViewEmptyResults();
15405 this.clearTouchView();
15407 var rawValue = this.getRawValue();
15409 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15411 this.tickItems = [];
15413 this.store.data.each(function(d, rowIndex){
15414 var row = this.touchViewListGroup.createChild(template);
15416 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15417 row.addClass(d.data.cls);
15420 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15423 html : d.data[this.displayField]
15426 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15427 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15430 row.removeClass('selected');
15431 if(!this.multiple && this.valueField &&
15432 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15435 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15436 row.addClass('selected');
15439 if(this.multiple && this.valueField &&
15440 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15444 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15445 this.tickItems.push(d.data);
15448 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15452 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15454 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15456 if(this.modalTitle.length){
15457 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15460 var listHeight = this.touchViewListGroup.getHeight();
15464 if(firstChecked && listHeight > bodyHeight){
15465 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15470 onTouchViewLoadException : function()
15472 this.hideTouchView();
15475 onTouchViewEmptyResults : function()
15477 this.clearTouchView();
15479 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15481 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15485 clearTouchView : function()
15487 this.touchViewListGroup.dom.innerHTML = '';
15490 onTouchViewClick : function(e, el, o)
15492 e.preventDefault();
15495 var rowIndex = o.rowIndex;
15497 var r = this.store.getAt(rowIndex);
15499 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15501 if(!this.multiple){
15502 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15503 c.dom.removeAttribute('checked');
15506 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15508 this.setFromData(r.data);
15510 var close = this.closeTriggerEl();
15516 this.hideTouchView();
15518 this.fireEvent('select', this, r, rowIndex);
15523 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15524 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15525 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15529 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15530 this.addItem(r.data);
15531 this.tickItems.push(r.data);
15535 getAutoCreateNativeIOS : function()
15538 cls: 'form-group' //input-group,
15543 cls : 'roo-ios-select'
15547 combobox.name = this.name;
15550 if (this.disabled) {
15551 combobox.disabled = true;
15554 var settings = this;
15556 ['xs','sm','md','lg'].map(function(size){
15557 if (settings[size]) {
15558 cfg.cls += ' col-' + size + '-' + settings[size];
15568 initIOSView : function()
15570 this.store.on('load', this.onIOSViewLoad, this);
15575 onIOSViewLoad : function()
15577 if(this.store.getCount() < 1){
15581 this.clearIOSView();
15583 if(this.allowBlank) {
15585 var default_text = '-- SELECT --';
15587 if(this.placeholder.length){
15588 default_text = this.placeholder;
15591 if(this.emptyTitle.length){
15592 default_text += ' - ' + this.emptyTitle + ' -';
15595 var opt = this.inputEl().createChild({
15598 html : default_text
15602 o[this.valueField] = 0;
15603 o[this.displayField] = default_text;
15605 this.ios_options.push({
15612 this.store.data.each(function(d, rowIndex){
15616 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15617 html = d.data[this.displayField];
15622 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15623 value = d.data[this.valueField];
15632 if(this.value == d.data[this.valueField]){
15633 option['selected'] = true;
15636 var opt = this.inputEl().createChild(option);
15638 this.ios_options.push({
15645 this.inputEl().on('change', function(){
15646 this.fireEvent('select', this);
15651 clearIOSView: function()
15653 this.inputEl().dom.innerHTML = '';
15655 this.ios_options = [];
15658 setIOSValue: function(v)
15662 if(!this.ios_options){
15666 Roo.each(this.ios_options, function(opts){
15668 opts.el.dom.removeAttribute('selected');
15670 if(opts.data[this.valueField] != v){
15674 opts.el.dom.setAttribute('selected', true);
15680 * @cfg {Boolean} grow
15684 * @cfg {Number} growMin
15688 * @cfg {Number} growMax
15697 Roo.apply(Roo.bootstrap.ComboBox, {
15701 cls: 'modal-header',
15723 cls: 'list-group-item',
15727 cls: 'roo-combobox-list-group-item-value'
15731 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15745 listItemCheckbox : {
15747 cls: 'list-group-item',
15751 cls: 'roo-combobox-list-group-item-value'
15755 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15771 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15776 cls: 'modal-footer',
15784 cls: 'col-xs-6 text-left',
15787 cls: 'btn btn-danger roo-touch-view-cancel',
15793 cls: 'col-xs-6 text-right',
15796 cls: 'btn btn-success roo-touch-view-ok',
15807 Roo.apply(Roo.bootstrap.ComboBox, {
15809 touchViewTemplate : {
15811 cls: 'modal fade roo-combobox-touch-view',
15815 cls: 'modal-dialog',
15816 style : 'position:fixed', // we have to fix position....
15820 cls: 'modal-content',
15822 Roo.bootstrap.ComboBox.header,
15823 Roo.bootstrap.ComboBox.body,
15824 Roo.bootstrap.ComboBox.footer
15833 * Ext JS Library 1.1.1
15834 * Copyright(c) 2006-2007, Ext JS, LLC.
15836 * Originally Released Under LGPL - original licence link has changed is not relivant.
15839 * <script type="text/javascript">
15844 * @extends Roo.util.Observable
15845 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15846 * This class also supports single and multi selection modes. <br>
15847 * Create a data model bound view:
15849 var store = new Roo.data.Store(...);
15851 var view = new Roo.View({
15853 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15855 singleSelect: true,
15856 selectedClass: "ydataview-selected",
15860 // listen for node click?
15861 view.on("click", function(vw, index, node, e){
15862 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15866 dataModel.load("foobar.xml");
15868 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15870 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15871 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15873 * Note: old style constructor is still suported (container, template, config)
15876 * Create a new View
15877 * @param {Object} config The config object
15880 Roo.View = function(config, depreciated_tpl, depreciated_config){
15882 this.parent = false;
15884 if (typeof(depreciated_tpl) == 'undefined') {
15885 // new way.. - universal constructor.
15886 Roo.apply(this, config);
15887 this.el = Roo.get(this.el);
15890 this.el = Roo.get(config);
15891 this.tpl = depreciated_tpl;
15892 Roo.apply(this, depreciated_config);
15894 this.wrapEl = this.el.wrap().wrap();
15895 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15898 if(typeof(this.tpl) == "string"){
15899 this.tpl = new Roo.Template(this.tpl);
15901 // support xtype ctors..
15902 this.tpl = new Roo.factory(this.tpl, Roo);
15906 this.tpl.compile();
15911 * @event beforeclick
15912 * Fires before a click is processed. Returns false to cancel the default action.
15913 * @param {Roo.View} this
15914 * @param {Number} index The index of the target node
15915 * @param {HTMLElement} node The target node
15916 * @param {Roo.EventObject} e The raw event object
15918 "beforeclick" : true,
15921 * Fires when a template node is clicked.
15922 * @param {Roo.View} this
15923 * @param {Number} index The index of the target node
15924 * @param {HTMLElement} node The target node
15925 * @param {Roo.EventObject} e The raw event object
15930 * Fires when a template node is double clicked.
15931 * @param {Roo.View} this
15932 * @param {Number} index The index of the target node
15933 * @param {HTMLElement} node The target node
15934 * @param {Roo.EventObject} e The raw event object
15938 * @event contextmenu
15939 * Fires when a template node is right clicked.
15940 * @param {Roo.View} this
15941 * @param {Number} index The index of the target node
15942 * @param {HTMLElement} node The target node
15943 * @param {Roo.EventObject} e The raw event object
15945 "contextmenu" : true,
15947 * @event selectionchange
15948 * Fires when the selected nodes change.
15949 * @param {Roo.View} this
15950 * @param {Array} selections Array of the selected nodes
15952 "selectionchange" : true,
15955 * @event beforeselect
15956 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15957 * @param {Roo.View} this
15958 * @param {HTMLElement} node The node to be selected
15959 * @param {Array} selections Array of currently selected nodes
15961 "beforeselect" : true,
15963 * @event preparedata
15964 * Fires on every row to render, to allow you to change the data.
15965 * @param {Roo.View} this
15966 * @param {Object} data to be rendered (change this)
15968 "preparedata" : true
15976 "click": this.onClick,
15977 "dblclick": this.onDblClick,
15978 "contextmenu": this.onContextMenu,
15982 this.selections = [];
15984 this.cmp = new Roo.CompositeElementLite([]);
15986 this.store = Roo.factory(this.store, Roo.data);
15987 this.setStore(this.store, true);
15990 if ( this.footer && this.footer.xtype) {
15992 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15994 this.footer.dataSource = this.store;
15995 this.footer.container = fctr;
15996 this.footer = Roo.factory(this.footer, Roo);
15997 fctr.insertFirst(this.el);
15999 // this is a bit insane - as the paging toolbar seems to detach the el..
16000 // dom.parentNode.parentNode.parentNode
16001 // they get detached?
16005 Roo.View.superclass.constructor.call(this);
16010 Roo.extend(Roo.View, Roo.util.Observable, {
16013 * @cfg {Roo.data.Store} store Data store to load data from.
16018 * @cfg {String|Roo.Element} el The container element.
16023 * @cfg {String|Roo.Template} tpl The template used by this View
16027 * @cfg {String} dataName the named area of the template to use as the data area
16028 * Works with domtemplates roo-name="name"
16032 * @cfg {String} selectedClass The css class to add to selected nodes
16034 selectedClass : "x-view-selected",
16036 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16041 * @cfg {String} text to display on mask (default Loading)
16045 * @cfg {Boolean} multiSelect Allow multiple selection
16047 multiSelect : false,
16049 * @cfg {Boolean} singleSelect Allow single selection
16051 singleSelect: false,
16054 * @cfg {Boolean} toggleSelect - selecting
16056 toggleSelect : false,
16059 * @cfg {Boolean} tickable - selecting
16064 * Returns the element this view is bound to.
16065 * @return {Roo.Element}
16067 getEl : function(){
16068 return this.wrapEl;
16074 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16076 refresh : function(){
16077 //Roo.log('refresh');
16080 // if we are using something like 'domtemplate', then
16081 // the what gets used is:
16082 // t.applySubtemplate(NAME, data, wrapping data..)
16083 // the outer template then get' applied with
16084 // the store 'extra data'
16085 // and the body get's added to the
16086 // roo-name="data" node?
16087 // <span class='roo-tpl-{name}'></span> ?????
16091 this.clearSelections();
16092 this.el.update("");
16094 var records = this.store.getRange();
16095 if(records.length < 1) {
16097 // is this valid?? = should it render a template??
16099 this.el.update(this.emptyText);
16103 if (this.dataName) {
16104 this.el.update(t.apply(this.store.meta)); //????
16105 el = this.el.child('.roo-tpl-' + this.dataName);
16108 for(var i = 0, len = records.length; i < len; i++){
16109 var data = this.prepareData(records[i].data, i, records[i]);
16110 this.fireEvent("preparedata", this, data, i, records[i]);
16112 var d = Roo.apply({}, data);
16115 Roo.apply(d, {'roo-id' : Roo.id()});
16119 Roo.each(this.parent.item, function(item){
16120 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16123 Roo.apply(d, {'roo-data-checked' : 'checked'});
16127 html[html.length] = Roo.util.Format.trim(
16129 t.applySubtemplate(this.dataName, d, this.store.meta) :
16136 el.update(html.join(""));
16137 this.nodes = el.dom.childNodes;
16138 this.updateIndexes(0);
16143 * Function to override to reformat the data that is sent to
16144 * the template for each node.
16145 * DEPRICATED - use the preparedata event handler.
16146 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16147 * a JSON object for an UpdateManager bound view).
16149 prepareData : function(data, index, record)
16151 this.fireEvent("preparedata", this, data, index, record);
16155 onUpdate : function(ds, record){
16156 // Roo.log('on update');
16157 this.clearSelections();
16158 var index = this.store.indexOf(record);
16159 var n = this.nodes[index];
16160 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16161 n.parentNode.removeChild(n);
16162 this.updateIndexes(index, index);
16168 onAdd : function(ds, records, index)
16170 //Roo.log(['on Add', ds, records, index] );
16171 this.clearSelections();
16172 if(this.nodes.length == 0){
16176 var n = this.nodes[index];
16177 for(var i = 0, len = records.length; i < len; i++){
16178 var d = this.prepareData(records[i].data, i, records[i]);
16180 this.tpl.insertBefore(n, d);
16183 this.tpl.append(this.el, d);
16186 this.updateIndexes(index);
16189 onRemove : function(ds, record, index){
16190 // Roo.log('onRemove');
16191 this.clearSelections();
16192 var el = this.dataName ?
16193 this.el.child('.roo-tpl-' + this.dataName) :
16196 el.dom.removeChild(this.nodes[index]);
16197 this.updateIndexes(index);
16201 * Refresh an individual node.
16202 * @param {Number} index
16204 refreshNode : function(index){
16205 this.onUpdate(this.store, this.store.getAt(index));
16208 updateIndexes : function(startIndex, endIndex){
16209 var ns = this.nodes;
16210 startIndex = startIndex || 0;
16211 endIndex = endIndex || ns.length - 1;
16212 for(var i = startIndex; i <= endIndex; i++){
16213 ns[i].nodeIndex = i;
16218 * Changes the data store this view uses and refresh the view.
16219 * @param {Store} store
16221 setStore : function(store, initial){
16222 if(!initial && this.store){
16223 this.store.un("datachanged", this.refresh);
16224 this.store.un("add", this.onAdd);
16225 this.store.un("remove", this.onRemove);
16226 this.store.un("update", this.onUpdate);
16227 this.store.un("clear", this.refresh);
16228 this.store.un("beforeload", this.onBeforeLoad);
16229 this.store.un("load", this.onLoad);
16230 this.store.un("loadexception", this.onLoad);
16234 store.on("datachanged", this.refresh, this);
16235 store.on("add", this.onAdd, this);
16236 store.on("remove", this.onRemove, this);
16237 store.on("update", this.onUpdate, this);
16238 store.on("clear", this.refresh, this);
16239 store.on("beforeload", this.onBeforeLoad, this);
16240 store.on("load", this.onLoad, this);
16241 store.on("loadexception", this.onLoad, this);
16249 * onbeforeLoad - masks the loading area.
16252 onBeforeLoad : function(store,opts)
16254 //Roo.log('onBeforeLoad');
16256 this.el.update("");
16258 this.el.mask(this.mask ? this.mask : "Loading" );
16260 onLoad : function ()
16267 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16268 * @param {HTMLElement} node
16269 * @return {HTMLElement} The template node
16271 findItemFromChild : function(node){
16272 var el = this.dataName ?
16273 this.el.child('.roo-tpl-' + this.dataName,true) :
16276 if(!node || node.parentNode == el){
16279 var p = node.parentNode;
16280 while(p && p != el){
16281 if(p.parentNode == el){
16290 onClick : function(e){
16291 var item = this.findItemFromChild(e.getTarget());
16293 var index = this.indexOf(item);
16294 if(this.onItemClick(item, index, e) !== false){
16295 this.fireEvent("click", this, index, item, e);
16298 this.clearSelections();
16303 onContextMenu : function(e){
16304 var item = this.findItemFromChild(e.getTarget());
16306 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16311 onDblClick : function(e){
16312 var item = this.findItemFromChild(e.getTarget());
16314 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16318 onItemClick : function(item, index, e)
16320 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16323 if (this.toggleSelect) {
16324 var m = this.isSelected(item) ? 'unselect' : 'select';
16327 _t[m](item, true, false);
16330 if(this.multiSelect || this.singleSelect){
16331 if(this.multiSelect && e.shiftKey && this.lastSelection){
16332 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16334 this.select(item, this.multiSelect && e.ctrlKey);
16335 this.lastSelection = item;
16338 if(!this.tickable){
16339 e.preventDefault();
16347 * Get the number of selected nodes.
16350 getSelectionCount : function(){
16351 return this.selections.length;
16355 * Get the currently selected nodes.
16356 * @return {Array} An array of HTMLElements
16358 getSelectedNodes : function(){
16359 return this.selections;
16363 * Get the indexes of the selected nodes.
16366 getSelectedIndexes : function(){
16367 var indexes = [], s = this.selections;
16368 for(var i = 0, len = s.length; i < len; i++){
16369 indexes.push(s[i].nodeIndex);
16375 * Clear all selections
16376 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16378 clearSelections : function(suppressEvent){
16379 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16380 this.cmp.elements = this.selections;
16381 this.cmp.removeClass(this.selectedClass);
16382 this.selections = [];
16383 if(!suppressEvent){
16384 this.fireEvent("selectionchange", this, this.selections);
16390 * Returns true if the passed node is selected
16391 * @param {HTMLElement/Number} node The node or node index
16392 * @return {Boolean}
16394 isSelected : function(node){
16395 var s = this.selections;
16399 node = this.getNode(node);
16400 return s.indexOf(node) !== -1;
16405 * @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
16406 * @param {Boolean} keepExisting (optional) true to keep existing selections
16407 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16409 select : function(nodeInfo, keepExisting, suppressEvent){
16410 if(nodeInfo instanceof Array){
16412 this.clearSelections(true);
16414 for(var i = 0, len = nodeInfo.length; i < len; i++){
16415 this.select(nodeInfo[i], true, true);
16419 var node = this.getNode(nodeInfo);
16420 if(!node || this.isSelected(node)){
16421 return; // already selected.
16424 this.clearSelections(true);
16427 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16428 Roo.fly(node).addClass(this.selectedClass);
16429 this.selections.push(node);
16430 if(!suppressEvent){
16431 this.fireEvent("selectionchange", this, this.selections);
16439 * @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
16440 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16441 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16443 unselect : function(nodeInfo, keepExisting, suppressEvent)
16445 if(nodeInfo instanceof Array){
16446 Roo.each(this.selections, function(s) {
16447 this.unselect(s, nodeInfo);
16451 var node = this.getNode(nodeInfo);
16452 if(!node || !this.isSelected(node)){
16453 //Roo.log("not selected");
16454 return; // not selected.
16458 Roo.each(this.selections, function(s) {
16460 Roo.fly(node).removeClass(this.selectedClass);
16467 this.selections= ns;
16468 this.fireEvent("selectionchange", this, this.selections);
16472 * Gets a template node.
16473 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16474 * @return {HTMLElement} The node or null if it wasn't found
16476 getNode : function(nodeInfo){
16477 if(typeof nodeInfo == "string"){
16478 return document.getElementById(nodeInfo);
16479 }else if(typeof nodeInfo == "number"){
16480 return this.nodes[nodeInfo];
16486 * Gets a range template nodes.
16487 * @param {Number} startIndex
16488 * @param {Number} endIndex
16489 * @return {Array} An array of nodes
16491 getNodes : function(start, end){
16492 var ns = this.nodes;
16493 start = start || 0;
16494 end = typeof end == "undefined" ? ns.length - 1 : end;
16497 for(var i = start; i <= end; i++){
16501 for(var i = start; i >= end; i--){
16509 * Finds the index of the passed node
16510 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16511 * @return {Number} The index of the node or -1
16513 indexOf : function(node){
16514 node = this.getNode(node);
16515 if(typeof node.nodeIndex == "number"){
16516 return node.nodeIndex;
16518 var ns = this.nodes;
16519 for(var i = 0, len = ns.length; i < len; i++){
16530 * based on jquery fullcalendar
16534 Roo.bootstrap = Roo.bootstrap || {};
16536 * @class Roo.bootstrap.Calendar
16537 * @extends Roo.bootstrap.Component
16538 * Bootstrap Calendar class
16539 * @cfg {Boolean} loadMask (true|false) default false
16540 * @cfg {Object} header generate the user specific header of the calendar, default false
16543 * Create a new Container
16544 * @param {Object} config The config object
16549 Roo.bootstrap.Calendar = function(config){
16550 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16554 * Fires when a date is selected
16555 * @param {DatePicker} this
16556 * @param {Date} date The selected date
16560 * @event monthchange
16561 * Fires when the displayed month changes
16562 * @param {DatePicker} this
16563 * @param {Date} date The selected month
16565 'monthchange': true,
16567 * @event evententer
16568 * Fires when mouse over an event
16569 * @param {Calendar} this
16570 * @param {event} Event
16572 'evententer': true,
16574 * @event eventleave
16575 * Fires when the mouse leaves an
16576 * @param {Calendar} this
16579 'eventleave': true,
16581 * @event eventclick
16582 * Fires when the mouse click an
16583 * @param {Calendar} this
16592 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16595 * @cfg {Number} startDay
16596 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16604 getAutoCreate : function(){
16607 var fc_button = function(name, corner, style, content ) {
16608 return Roo.apply({},{
16610 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16612 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16615 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16626 style : 'width:100%',
16633 cls : 'fc-header-left',
16635 fc_button('prev', 'left', 'arrow', '‹' ),
16636 fc_button('next', 'right', 'arrow', '›' ),
16637 { tag: 'span', cls: 'fc-header-space' },
16638 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16646 cls : 'fc-header-center',
16650 cls: 'fc-header-title',
16653 html : 'month / year'
16661 cls : 'fc-header-right',
16663 /* fc_button('month', 'left', '', 'month' ),
16664 fc_button('week', '', '', 'week' ),
16665 fc_button('day', 'right', '', 'day' )
16677 header = this.header;
16680 var cal_heads = function() {
16682 // fixme - handle this.
16684 for (var i =0; i < Date.dayNames.length; i++) {
16685 var d = Date.dayNames[i];
16688 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16689 html : d.substring(0,3)
16693 ret[0].cls += ' fc-first';
16694 ret[6].cls += ' fc-last';
16697 var cal_cell = function(n) {
16700 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16705 cls: 'fc-day-number',
16709 cls: 'fc-day-content',
16713 style: 'position: relative;' // height: 17px;
16725 var cal_rows = function() {
16728 for (var r = 0; r < 6; r++) {
16735 for (var i =0; i < Date.dayNames.length; i++) {
16736 var d = Date.dayNames[i];
16737 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16740 row.cn[0].cls+=' fc-first';
16741 row.cn[0].cn[0].style = 'min-height:90px';
16742 row.cn[6].cls+=' fc-last';
16746 ret[0].cls += ' fc-first';
16747 ret[4].cls += ' fc-prev-last';
16748 ret[5].cls += ' fc-last';
16755 cls: 'fc-border-separate',
16756 style : 'width:100%',
16764 cls : 'fc-first fc-last',
16782 cls : 'fc-content',
16783 style : "position: relative;",
16786 cls : 'fc-view fc-view-month fc-grid',
16787 style : 'position: relative',
16788 unselectable : 'on',
16791 cls : 'fc-event-container',
16792 style : 'position:absolute;z-index:8;top:0;left:0;'
16810 initEvents : function()
16813 throw "can not find store for calendar";
16819 style: "text-align:center",
16823 style: "background-color:white;width:50%;margin:250 auto",
16827 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16838 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16840 var size = this.el.select('.fc-content', true).first().getSize();
16841 this.maskEl.setSize(size.width, size.height);
16842 this.maskEl.enableDisplayMode("block");
16843 if(!this.loadMask){
16844 this.maskEl.hide();
16847 this.store = Roo.factory(this.store, Roo.data);
16848 this.store.on('load', this.onLoad, this);
16849 this.store.on('beforeload', this.onBeforeLoad, this);
16853 this.cells = this.el.select('.fc-day',true);
16854 //Roo.log(this.cells);
16855 this.textNodes = this.el.query('.fc-day-number');
16856 this.cells.addClassOnOver('fc-state-hover');
16858 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16859 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16860 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16861 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16863 this.on('monthchange', this.onMonthChange, this);
16865 this.update(new Date().clearTime());
16868 resize : function() {
16869 var sz = this.el.getSize();
16871 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16872 this.el.select('.fc-day-content div',true).setHeight(34);
16877 showPrevMonth : function(e){
16878 this.update(this.activeDate.add("mo", -1));
16880 showToday : function(e){
16881 this.update(new Date().clearTime());
16884 showNextMonth : function(e){
16885 this.update(this.activeDate.add("mo", 1));
16889 showPrevYear : function(){
16890 this.update(this.activeDate.add("y", -1));
16894 showNextYear : function(){
16895 this.update(this.activeDate.add("y", 1));
16900 update : function(date)
16902 var vd = this.activeDate;
16903 this.activeDate = date;
16904 // if(vd && this.el){
16905 // var t = date.getTime();
16906 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16907 // Roo.log('using add remove');
16909 // this.fireEvent('monthchange', this, date);
16911 // this.cells.removeClass("fc-state-highlight");
16912 // this.cells.each(function(c){
16913 // if(c.dateValue == t){
16914 // c.addClass("fc-state-highlight");
16915 // setTimeout(function(){
16916 // try{c.dom.firstChild.focus();}catch(e){}
16926 var days = date.getDaysInMonth();
16928 var firstOfMonth = date.getFirstDateOfMonth();
16929 var startingPos = firstOfMonth.getDay()-this.startDay;
16931 if(startingPos < this.startDay){
16935 var pm = date.add(Date.MONTH, -1);
16936 var prevStart = pm.getDaysInMonth()-startingPos;
16938 this.cells = this.el.select('.fc-day',true);
16939 this.textNodes = this.el.query('.fc-day-number');
16940 this.cells.addClassOnOver('fc-state-hover');
16942 var cells = this.cells.elements;
16943 var textEls = this.textNodes;
16945 Roo.each(cells, function(cell){
16946 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16949 days += startingPos;
16951 // convert everything to numbers so it's fast
16952 var day = 86400000;
16953 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16956 //Roo.log(prevStart);
16958 var today = new Date().clearTime().getTime();
16959 var sel = date.clearTime().getTime();
16960 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16961 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16962 var ddMatch = this.disabledDatesRE;
16963 var ddText = this.disabledDatesText;
16964 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16965 var ddaysText = this.disabledDaysText;
16966 var format = this.format;
16968 var setCellClass = function(cal, cell){
16972 //Roo.log('set Cell Class');
16974 var t = d.getTime();
16978 cell.dateValue = t;
16980 cell.className += " fc-today";
16981 cell.className += " fc-state-highlight";
16982 cell.title = cal.todayText;
16985 // disable highlight in other month..
16986 //cell.className += " fc-state-highlight";
16991 cell.className = " fc-state-disabled";
16992 cell.title = cal.minText;
16996 cell.className = " fc-state-disabled";
16997 cell.title = cal.maxText;
17001 if(ddays.indexOf(d.getDay()) != -1){
17002 cell.title = ddaysText;
17003 cell.className = " fc-state-disabled";
17006 if(ddMatch && format){
17007 var fvalue = d.dateFormat(format);
17008 if(ddMatch.test(fvalue)){
17009 cell.title = ddText.replace("%0", fvalue);
17010 cell.className = " fc-state-disabled";
17014 if (!cell.initialClassName) {
17015 cell.initialClassName = cell.dom.className;
17018 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17023 for(; i < startingPos; i++) {
17024 textEls[i].innerHTML = (++prevStart);
17025 d.setDate(d.getDate()+1);
17027 cells[i].className = "fc-past fc-other-month";
17028 setCellClass(this, cells[i]);
17033 for(; i < days; i++){
17034 intDay = i - startingPos + 1;
17035 textEls[i].innerHTML = (intDay);
17036 d.setDate(d.getDate()+1);
17038 cells[i].className = ''; // "x-date-active";
17039 setCellClass(this, cells[i]);
17043 for(; i < 42; i++) {
17044 textEls[i].innerHTML = (++extraDays);
17045 d.setDate(d.getDate()+1);
17047 cells[i].className = "fc-future fc-other-month";
17048 setCellClass(this, cells[i]);
17051 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17053 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17055 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17056 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17058 if(totalRows != 6){
17059 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17060 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17063 this.fireEvent('monthchange', this, date);
17067 if(!this.internalRender){
17068 var main = this.el.dom.firstChild;
17069 var w = main.offsetWidth;
17070 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17071 Roo.fly(main).setWidth(w);
17072 this.internalRender = true;
17073 // opera does not respect the auto grow header center column
17074 // then, after it gets a width opera refuses to recalculate
17075 // without a second pass
17076 if(Roo.isOpera && !this.secondPass){
17077 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17078 this.secondPass = true;
17079 this.update.defer(10, this, [date]);
17086 findCell : function(dt) {
17087 dt = dt.clearTime().getTime();
17089 this.cells.each(function(c){
17090 //Roo.log("check " +c.dateValue + '?=' + dt);
17091 if(c.dateValue == dt){
17101 findCells : function(ev) {
17102 var s = ev.start.clone().clearTime().getTime();
17104 var e= ev.end.clone().clearTime().getTime();
17107 this.cells.each(function(c){
17108 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17110 if(c.dateValue > e){
17113 if(c.dateValue < s){
17122 // findBestRow: function(cells)
17126 // for (var i =0 ; i < cells.length;i++) {
17127 // ret = Math.max(cells[i].rows || 0,ret);
17134 addItem : function(ev)
17136 // look for vertical location slot in
17137 var cells = this.findCells(ev);
17139 // ev.row = this.findBestRow(cells);
17141 // work out the location.
17145 for(var i =0; i < cells.length; i++) {
17147 cells[i].row = cells[0].row;
17150 cells[i].row = cells[i].row + 1;
17160 if (crow.start.getY() == cells[i].getY()) {
17162 crow.end = cells[i];
17179 cells[0].events.push(ev);
17181 this.calevents.push(ev);
17184 clearEvents: function() {
17186 if(!this.calevents){
17190 Roo.each(this.cells.elements, function(c){
17196 Roo.each(this.calevents, function(e) {
17197 Roo.each(e.els, function(el) {
17198 el.un('mouseenter' ,this.onEventEnter, this);
17199 el.un('mouseleave' ,this.onEventLeave, this);
17204 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17210 renderEvents: function()
17214 this.cells.each(function(c) {
17223 if(c.row != c.events.length){
17224 r = 4 - (4 - (c.row - c.events.length));
17227 c.events = ev.slice(0, r);
17228 c.more = ev.slice(r);
17230 if(c.more.length && c.more.length == 1){
17231 c.events.push(c.more.pop());
17234 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17238 this.cells.each(function(c) {
17240 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17243 for (var e = 0; e < c.events.length; e++){
17244 var ev = c.events[e];
17245 var rows = ev.rows;
17247 for(var i = 0; i < rows.length; i++) {
17249 // how many rows should it span..
17252 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17253 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17255 unselectable : "on",
17258 cls: 'fc-event-inner',
17262 // cls: 'fc-event-time',
17263 // html : cells.length > 1 ? '' : ev.time
17267 cls: 'fc-event-title',
17268 html : String.format('{0}', ev.title)
17275 cls: 'ui-resizable-handle ui-resizable-e',
17276 html : '  '
17283 cfg.cls += ' fc-event-start';
17285 if ((i+1) == rows.length) {
17286 cfg.cls += ' fc-event-end';
17289 var ctr = _this.el.select('.fc-event-container',true).first();
17290 var cg = ctr.createChild(cfg);
17292 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17293 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17295 var r = (c.more.length) ? 1 : 0;
17296 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17297 cg.setWidth(ebox.right - sbox.x -2);
17299 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17300 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17301 cg.on('click', _this.onEventClick, _this, ev);
17312 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17313 style : 'position: absolute',
17314 unselectable : "on",
17317 cls: 'fc-event-inner',
17321 cls: 'fc-event-title',
17329 cls: 'ui-resizable-handle ui-resizable-e',
17330 html : '  '
17336 var ctr = _this.el.select('.fc-event-container',true).first();
17337 var cg = ctr.createChild(cfg);
17339 var sbox = c.select('.fc-day-content',true).first().getBox();
17340 var ebox = c.select('.fc-day-content',true).first().getBox();
17342 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17343 cg.setWidth(ebox.right - sbox.x -2);
17345 cg.on('click', _this.onMoreEventClick, _this, c.more);
17355 onEventEnter: function (e, el,event,d) {
17356 this.fireEvent('evententer', this, el, event);
17359 onEventLeave: function (e, el,event,d) {
17360 this.fireEvent('eventleave', this, el, event);
17363 onEventClick: function (e, el,event,d) {
17364 this.fireEvent('eventclick', this, el, event);
17367 onMonthChange: function () {
17371 onMoreEventClick: function(e, el, more)
17375 this.calpopover.placement = 'right';
17376 this.calpopover.setTitle('More');
17378 this.calpopover.setContent('');
17380 var ctr = this.calpopover.el.select('.popover-content', true).first();
17382 Roo.each(more, function(m){
17384 cls : 'fc-event-hori fc-event-draggable',
17387 var cg = ctr.createChild(cfg);
17389 cg.on('click', _this.onEventClick, _this, m);
17392 this.calpopover.show(el);
17397 onLoad: function ()
17399 this.calevents = [];
17402 if(this.store.getCount() > 0){
17403 this.store.data.each(function(d){
17406 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17407 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17408 time : d.data.start_time,
17409 title : d.data.title,
17410 description : d.data.description,
17411 venue : d.data.venue
17416 this.renderEvents();
17418 if(this.calevents.length && this.loadMask){
17419 this.maskEl.hide();
17423 onBeforeLoad: function()
17425 this.clearEvents();
17427 this.maskEl.show();
17441 * @class Roo.bootstrap.Popover
17442 * @extends Roo.bootstrap.Component
17443 * Bootstrap Popover class
17444 * @cfg {String} html contents of the popover (or false to use children..)
17445 * @cfg {String} title of popover (or false to hide)
17446 * @cfg {String} placement how it is placed
17447 * @cfg {String} trigger click || hover (or false to trigger manually)
17448 * @cfg {String} over what (parent or false to trigger manually.)
17449 * @cfg {Number} delay - delay before showing
17452 * Create a new Popover
17453 * @param {Object} config The config object
17456 Roo.bootstrap.Popover = function(config){
17457 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17463 * After the popover show
17465 * @param {Roo.bootstrap.Popover} this
17470 * After the popover hide
17472 * @param {Roo.bootstrap.Popover} this
17478 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17480 title: 'Fill in a title',
17483 placement : 'right',
17484 trigger : 'hover', // hover
17490 can_build_overlaid : false,
17492 getChildContainer : function()
17494 return this.el.select('.popover-content',true).first();
17497 getAutoCreate : function(){
17500 cls : 'popover roo-dynamic',
17501 style: 'display:block',
17507 cls : 'popover-inner',
17511 cls: 'popover-title',
17515 cls : 'popover-content',
17526 setTitle: function(str)
17529 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17531 setContent: function(str)
17534 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17536 // as it get's added to the bottom of the page.
17537 onRender : function(ct, position)
17539 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17541 var cfg = Roo.apply({}, this.getAutoCreate());
17545 cfg.cls += ' ' + this.cls;
17548 cfg.style = this.style;
17550 //Roo.log("adding to ");
17551 this.el = Roo.get(document.body).createChild(cfg, position);
17552 // Roo.log(this.el);
17557 initEvents : function()
17559 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17560 this.el.enableDisplayMode('block');
17562 if (this.over === false) {
17565 if (this.triggers === false) {
17568 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17569 var triggers = this.trigger ? this.trigger.split(' ') : [];
17570 Roo.each(triggers, function(trigger) {
17572 if (trigger == 'click') {
17573 on_el.on('click', this.toggle, this);
17574 } else if (trigger != 'manual') {
17575 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17576 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17578 on_el.on(eventIn ,this.enter, this);
17579 on_el.on(eventOut, this.leave, this);
17590 toggle : function () {
17591 this.hoverState == 'in' ? this.leave() : this.enter();
17594 enter : function () {
17596 clearTimeout(this.timeout);
17598 this.hoverState = 'in';
17600 if (!this.delay || !this.delay.show) {
17605 this.timeout = setTimeout(function () {
17606 if (_t.hoverState == 'in') {
17609 }, this.delay.show)
17612 leave : function() {
17613 clearTimeout(this.timeout);
17615 this.hoverState = 'out';
17617 if (!this.delay || !this.delay.hide) {
17622 this.timeout = setTimeout(function () {
17623 if (_t.hoverState == 'out') {
17626 }, this.delay.hide)
17629 show : function (on_el)
17632 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17636 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17637 if (this.html !== false) {
17638 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17640 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17641 if (!this.title.length) {
17642 this.el.select('.popover-title',true).hide();
17645 var placement = typeof this.placement == 'function' ?
17646 this.placement.call(this, this.el, on_el) :
17649 var autoToken = /\s?auto?\s?/i;
17650 var autoPlace = autoToken.test(placement);
17652 placement = placement.replace(autoToken, '') || 'top';
17656 //this.el.setXY([0,0]);
17658 this.el.dom.style.display='block';
17659 this.el.addClass(placement);
17661 //this.el.appendTo(on_el);
17663 var p = this.getPosition();
17664 var box = this.el.getBox();
17669 var align = Roo.bootstrap.Popover.alignment[placement];
17672 this.el.alignTo(on_el, align[0],align[1]);
17673 //var arrow = this.el.select('.arrow',true).first();
17674 //arrow.set(align[2],
17676 this.el.addClass('in');
17679 if (this.el.hasClass('fade')) {
17683 this.hoverState = 'in';
17685 this.fireEvent('show', this);
17690 this.el.setXY([0,0]);
17691 this.el.removeClass('in');
17693 this.hoverState = null;
17695 this.fireEvent('hide', this);
17700 Roo.bootstrap.Popover.alignment = {
17701 'left' : ['r-l', [-10,0], 'right'],
17702 'right' : ['l-r', [10,0], 'left'],
17703 'bottom' : ['t-b', [0,10], 'top'],
17704 'top' : [ 'b-t', [0,-10], 'bottom']
17715 * @class Roo.bootstrap.Progress
17716 * @extends Roo.bootstrap.Component
17717 * Bootstrap Progress class
17718 * @cfg {Boolean} striped striped of the progress bar
17719 * @cfg {Boolean} active animated of the progress bar
17723 * Create a new Progress
17724 * @param {Object} config The config object
17727 Roo.bootstrap.Progress = function(config){
17728 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17731 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17736 getAutoCreate : function(){
17744 cfg.cls += ' progress-striped';
17748 cfg.cls += ' active';
17767 * @class Roo.bootstrap.ProgressBar
17768 * @extends Roo.bootstrap.Component
17769 * Bootstrap ProgressBar class
17770 * @cfg {Number} aria_valuenow aria-value now
17771 * @cfg {Number} aria_valuemin aria-value min
17772 * @cfg {Number} aria_valuemax aria-value max
17773 * @cfg {String} label label for the progress bar
17774 * @cfg {String} panel (success | info | warning | danger )
17775 * @cfg {String} role role of the progress bar
17776 * @cfg {String} sr_only text
17780 * Create a new ProgressBar
17781 * @param {Object} config The config object
17784 Roo.bootstrap.ProgressBar = function(config){
17785 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17788 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17792 aria_valuemax : 100,
17798 getAutoCreate : function()
17803 cls: 'progress-bar',
17804 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17816 cfg.role = this.role;
17819 if(this.aria_valuenow){
17820 cfg['aria-valuenow'] = this.aria_valuenow;
17823 if(this.aria_valuemin){
17824 cfg['aria-valuemin'] = this.aria_valuemin;
17827 if(this.aria_valuemax){
17828 cfg['aria-valuemax'] = this.aria_valuemax;
17831 if(this.label && !this.sr_only){
17832 cfg.html = this.label;
17836 cfg.cls += ' progress-bar-' + this.panel;
17842 update : function(aria_valuenow)
17844 this.aria_valuenow = aria_valuenow;
17846 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17861 * @class Roo.bootstrap.TabGroup
17862 * @extends Roo.bootstrap.Column
17863 * Bootstrap Column class
17864 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17865 * @cfg {Boolean} carousel true to make the group behave like a carousel
17866 * @cfg {Boolean} bullets show bullets for the panels
17867 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17868 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17869 * @cfg {Boolean} showarrow (true|false) show arrow default true
17872 * Create a new TabGroup
17873 * @param {Object} config The config object
17876 Roo.bootstrap.TabGroup = function(config){
17877 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17879 this.navId = Roo.id();
17882 Roo.bootstrap.TabGroup.register(this);
17886 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17889 transition : false,
17894 slideOnTouch : false,
17897 getAutoCreate : function()
17899 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17901 cfg.cls += ' tab-content';
17903 if (this.carousel) {
17904 cfg.cls += ' carousel slide';
17907 cls : 'carousel-inner',
17911 if(this.bullets && !Roo.isTouch){
17914 cls : 'carousel-bullets',
17918 if(this.bullets_cls){
17919 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17926 cfg.cn[0].cn.push(bullets);
17929 if(this.showarrow){
17930 cfg.cn[0].cn.push({
17932 class : 'carousel-arrow',
17936 class : 'carousel-prev',
17940 class : 'fa fa-chevron-left'
17946 class : 'carousel-next',
17950 class : 'fa fa-chevron-right'
17963 initEvents: function()
17965 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17966 // this.el.on("touchstart", this.onTouchStart, this);
17969 if(this.autoslide){
17972 this.slideFn = window.setInterval(function() {
17973 _this.showPanelNext();
17977 if(this.showarrow){
17978 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17979 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17985 // onTouchStart : function(e, el, o)
17987 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17991 // this.showPanelNext();
17995 getChildContainer : function()
17997 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18001 * register a Navigation item
18002 * @param {Roo.bootstrap.NavItem} the navitem to add
18004 register : function(item)
18006 this.tabs.push( item);
18007 item.navId = this.navId; // not really needed..
18012 getActivePanel : function()
18015 Roo.each(this.tabs, function(t) {
18025 getPanelByName : function(n)
18028 Roo.each(this.tabs, function(t) {
18029 if (t.tabId == n) {
18037 indexOfPanel : function(p)
18040 Roo.each(this.tabs, function(t,i) {
18041 if (t.tabId == p.tabId) {
18050 * show a specific panel
18051 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18052 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18054 showPanel : function (pan)
18056 if(this.transition || typeof(pan) == 'undefined'){
18057 Roo.log("waiting for the transitionend");
18061 if (typeof(pan) == 'number') {
18062 pan = this.tabs[pan];
18065 if (typeof(pan) == 'string') {
18066 pan = this.getPanelByName(pan);
18069 var cur = this.getActivePanel();
18072 Roo.log('pan or acitve pan is undefined');
18076 if (pan.tabId == this.getActivePanel().tabId) {
18080 if (false === cur.fireEvent('beforedeactivate')) {
18084 if(this.bullets > 0 && !Roo.isTouch){
18085 this.setActiveBullet(this.indexOfPanel(pan));
18088 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18090 this.transition = true;
18091 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18092 var lr = dir == 'next' ? 'left' : 'right';
18093 pan.el.addClass(dir); // or prev
18094 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18095 cur.el.addClass(lr); // or right
18096 pan.el.addClass(lr);
18099 cur.el.on('transitionend', function() {
18100 Roo.log("trans end?");
18102 pan.el.removeClass([lr,dir]);
18103 pan.setActive(true);
18105 cur.el.removeClass([lr]);
18106 cur.setActive(false);
18108 _this.transition = false;
18110 }, this, { single: true } );
18115 cur.setActive(false);
18116 pan.setActive(true);
18121 showPanelNext : function()
18123 var i = this.indexOfPanel(this.getActivePanel());
18125 if (i >= this.tabs.length - 1 && !this.autoslide) {
18129 if (i >= this.tabs.length - 1 && this.autoslide) {
18133 this.showPanel(this.tabs[i+1]);
18136 showPanelPrev : function()
18138 var i = this.indexOfPanel(this.getActivePanel());
18140 if (i < 1 && !this.autoslide) {
18144 if (i < 1 && this.autoslide) {
18145 i = this.tabs.length;
18148 this.showPanel(this.tabs[i-1]);
18152 addBullet: function()
18154 if(!this.bullets || Roo.isTouch){
18157 var ctr = this.el.select('.carousel-bullets',true).first();
18158 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18159 var bullet = ctr.createChild({
18160 cls : 'bullet bullet-' + i
18161 },ctr.dom.lastChild);
18166 bullet.on('click', (function(e, el, o, ii, t){
18168 e.preventDefault();
18170 this.showPanel(ii);
18172 if(this.autoslide && this.slideFn){
18173 clearInterval(this.slideFn);
18174 this.slideFn = window.setInterval(function() {
18175 _this.showPanelNext();
18179 }).createDelegate(this, [i, bullet], true));
18184 setActiveBullet : function(i)
18190 Roo.each(this.el.select('.bullet', true).elements, function(el){
18191 el.removeClass('selected');
18194 var bullet = this.el.select('.bullet-' + i, true).first();
18200 bullet.addClass('selected');
18211 Roo.apply(Roo.bootstrap.TabGroup, {
18215 * register a Navigation Group
18216 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18218 register : function(navgrp)
18220 this.groups[navgrp.navId] = navgrp;
18224 * fetch a Navigation Group based on the navigation ID
18225 * if one does not exist , it will get created.
18226 * @param {string} the navgroup to add
18227 * @returns {Roo.bootstrap.NavGroup} the navgroup
18229 get: function(navId) {
18230 if (typeof(this.groups[navId]) == 'undefined') {
18231 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18233 return this.groups[navId] ;
18248 * @class Roo.bootstrap.TabPanel
18249 * @extends Roo.bootstrap.Component
18250 * Bootstrap TabPanel class
18251 * @cfg {Boolean} active panel active
18252 * @cfg {String} html panel content
18253 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18254 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18255 * @cfg {String} href click to link..
18259 * Create a new TabPanel
18260 * @param {Object} config The config object
18263 Roo.bootstrap.TabPanel = function(config){
18264 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18268 * Fires when the active status changes
18269 * @param {Roo.bootstrap.TabPanel} this
18270 * @param {Boolean} state the new state
18275 * @event beforedeactivate
18276 * Fires before a tab is de-activated - can be used to do validation on a form.
18277 * @param {Roo.bootstrap.TabPanel} this
18278 * @return {Boolean} false if there is an error
18281 'beforedeactivate': true
18284 this.tabId = this.tabId || Roo.id();
18288 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18296 getAutoCreate : function(){
18299 // item is needed for carousel - not sure if it has any effect otherwise
18300 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18301 html: this.html || ''
18305 cfg.cls += ' active';
18309 cfg.tabId = this.tabId;
18316 initEvents: function()
18318 var p = this.parent();
18320 this.navId = this.navId || p.navId;
18322 if (typeof(this.navId) != 'undefined') {
18323 // not really needed.. but just in case.. parent should be a NavGroup.
18324 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18328 var i = tg.tabs.length - 1;
18330 if(this.active && tg.bullets > 0 && i < tg.bullets){
18331 tg.setActiveBullet(i);
18335 this.el.on('click', this.onClick, this);
18338 this.el.on("touchstart", this.onTouchStart, this);
18339 this.el.on("touchmove", this.onTouchMove, this);
18340 this.el.on("touchend", this.onTouchEnd, this);
18345 onRender : function(ct, position)
18347 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18350 setActive : function(state)
18352 Roo.log("panel - set active " + this.tabId + "=" + state);
18354 this.active = state;
18356 this.el.removeClass('active');
18358 } else if (!this.el.hasClass('active')) {
18359 this.el.addClass('active');
18362 this.fireEvent('changed', this, state);
18365 onClick : function(e)
18367 e.preventDefault();
18369 if(!this.href.length){
18373 window.location.href = this.href;
18382 onTouchStart : function(e)
18384 this.swiping = false;
18386 this.startX = e.browserEvent.touches[0].clientX;
18387 this.startY = e.browserEvent.touches[0].clientY;
18390 onTouchMove : function(e)
18392 this.swiping = true;
18394 this.endX = e.browserEvent.touches[0].clientX;
18395 this.endY = e.browserEvent.touches[0].clientY;
18398 onTouchEnd : function(e)
18405 var tabGroup = this.parent();
18407 if(this.endX > this.startX){ // swiping right
18408 tabGroup.showPanelPrev();
18412 if(this.startX > this.endX){ // swiping left
18413 tabGroup.showPanelNext();
18432 * @class Roo.bootstrap.DateField
18433 * @extends Roo.bootstrap.Input
18434 * Bootstrap DateField class
18435 * @cfg {Number} weekStart default 0
18436 * @cfg {String} viewMode default empty, (months|years)
18437 * @cfg {String} minViewMode default empty, (months|years)
18438 * @cfg {Number} startDate default -Infinity
18439 * @cfg {Number} endDate default Infinity
18440 * @cfg {Boolean} todayHighlight default false
18441 * @cfg {Boolean} todayBtn default false
18442 * @cfg {Boolean} calendarWeeks default false
18443 * @cfg {Object} daysOfWeekDisabled default empty
18444 * @cfg {Boolean} singleMode default false (true | false)
18446 * @cfg {Boolean} keyboardNavigation default true
18447 * @cfg {String} language default en
18450 * Create a new DateField
18451 * @param {Object} config The config object
18454 Roo.bootstrap.DateField = function(config){
18455 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18459 * Fires when this field show.
18460 * @param {Roo.bootstrap.DateField} this
18461 * @param {Mixed} date The date value
18466 * Fires when this field hide.
18467 * @param {Roo.bootstrap.DateField} this
18468 * @param {Mixed} date The date value
18473 * Fires when select a date.
18474 * @param {Roo.bootstrap.DateField} this
18475 * @param {Mixed} date The date value
18479 * @event beforeselect
18480 * Fires when before select a date.
18481 * @param {Roo.bootstrap.DateField} this
18482 * @param {Mixed} date The date value
18484 beforeselect : true
18488 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18491 * @cfg {String} format
18492 * The default date format string which can be overriden for localization support. The format must be
18493 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18497 * @cfg {String} altFormats
18498 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18499 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18501 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18509 todayHighlight : false,
18515 keyboardNavigation: true,
18517 calendarWeeks: false,
18519 startDate: -Infinity,
18523 daysOfWeekDisabled: [],
18527 singleMode : false,
18529 UTCDate: function()
18531 return new Date(Date.UTC.apply(Date, arguments));
18534 UTCToday: function()
18536 var today = new Date();
18537 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18540 getDate: function() {
18541 var d = this.getUTCDate();
18542 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18545 getUTCDate: function() {
18549 setDate: function(d) {
18550 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18553 setUTCDate: function(d) {
18555 this.setValue(this.formatDate(this.date));
18558 onRender: function(ct, position)
18561 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18563 this.language = this.language || 'en';
18564 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18565 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18567 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18568 this.format = this.format || 'm/d/y';
18569 this.isInline = false;
18570 this.isInput = true;
18571 this.component = this.el.select('.add-on', true).first() || false;
18572 this.component = (this.component && this.component.length === 0) ? false : this.component;
18573 this.hasInput = this.component && this.inputEl().length;
18575 if (typeof(this.minViewMode === 'string')) {
18576 switch (this.minViewMode) {
18578 this.minViewMode = 1;
18581 this.minViewMode = 2;
18584 this.minViewMode = 0;
18589 if (typeof(this.viewMode === 'string')) {
18590 switch (this.viewMode) {
18603 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18605 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18607 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18609 this.picker().on('mousedown', this.onMousedown, this);
18610 this.picker().on('click', this.onClick, this);
18612 this.picker().addClass('datepicker-dropdown');
18614 this.startViewMode = this.viewMode;
18616 if(this.singleMode){
18617 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18618 v.setVisibilityMode(Roo.Element.DISPLAY);
18622 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18623 v.setStyle('width', '189px');
18627 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18628 if(!this.calendarWeeks){
18633 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18634 v.attr('colspan', function(i, val){
18635 return parseInt(val) + 1;
18640 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18642 this.setStartDate(this.startDate);
18643 this.setEndDate(this.endDate);
18645 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18652 if(this.isInline) {
18657 picker : function()
18659 return this.pickerEl;
18660 // return this.el.select('.datepicker', true).first();
18663 fillDow: function()
18665 var dowCnt = this.weekStart;
18674 if(this.calendarWeeks){
18682 while (dowCnt < this.weekStart + 7) {
18686 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18690 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18693 fillMonths: function()
18696 var months = this.picker().select('>.datepicker-months td', true).first();
18698 months.dom.innerHTML = '';
18704 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18707 months.createChild(month);
18714 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;
18716 if (this.date < this.startDate) {
18717 this.viewDate = new Date(this.startDate);
18718 } else if (this.date > this.endDate) {
18719 this.viewDate = new Date(this.endDate);
18721 this.viewDate = new Date(this.date);
18729 var d = new Date(this.viewDate),
18730 year = d.getUTCFullYear(),
18731 month = d.getUTCMonth(),
18732 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18733 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18734 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18735 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18736 currentDate = this.date && this.date.valueOf(),
18737 today = this.UTCToday();
18739 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18741 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18743 // this.picker.select('>tfoot th.today').
18744 // .text(dates[this.language].today)
18745 // .toggle(this.todayBtn !== false);
18747 this.updateNavArrows();
18750 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18752 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18754 prevMonth.setUTCDate(day);
18756 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18758 var nextMonth = new Date(prevMonth);
18760 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18762 nextMonth = nextMonth.valueOf();
18764 var fillMonths = false;
18766 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18768 while(prevMonth.valueOf() <= nextMonth) {
18771 if (prevMonth.getUTCDay() === this.weekStart) {
18773 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18781 if(this.calendarWeeks){
18782 // ISO 8601: First week contains first thursday.
18783 // ISO also states week starts on Monday, but we can be more abstract here.
18785 // Start of current week: based on weekstart/current date
18786 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18787 // Thursday of this week
18788 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18789 // First Thursday of year, year from thursday
18790 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18791 // Calendar week: ms between thursdays, div ms per day, div 7 days
18792 calWeek = (th - yth) / 864e5 / 7 + 1;
18794 fillMonths.cn.push({
18802 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18804 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18807 if (this.todayHighlight &&
18808 prevMonth.getUTCFullYear() == today.getFullYear() &&
18809 prevMonth.getUTCMonth() == today.getMonth() &&
18810 prevMonth.getUTCDate() == today.getDate()) {
18811 clsName += ' today';
18814 if (currentDate && prevMonth.valueOf() === currentDate) {
18815 clsName += ' active';
18818 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18819 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18820 clsName += ' disabled';
18823 fillMonths.cn.push({
18825 cls: 'day ' + clsName,
18826 html: prevMonth.getDate()
18829 prevMonth.setDate(prevMonth.getDate()+1);
18832 var currentYear = this.date && this.date.getUTCFullYear();
18833 var currentMonth = this.date && this.date.getUTCMonth();
18835 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18837 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18838 v.removeClass('active');
18840 if(currentYear === year && k === currentMonth){
18841 v.addClass('active');
18844 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18845 v.addClass('disabled');
18851 year = parseInt(year/10, 10) * 10;
18853 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18855 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18858 for (var i = -1; i < 11; i++) {
18859 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18861 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18869 showMode: function(dir)
18872 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18875 Roo.each(this.picker().select('>div',true).elements, function(v){
18876 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18879 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18884 if(this.isInline) {
18888 this.picker().removeClass(['bottom', 'top']);
18890 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18892 * place to the top of element!
18896 this.picker().addClass('top');
18897 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18902 this.picker().addClass('bottom');
18904 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18907 parseDate : function(value)
18909 if(!value || value instanceof Date){
18912 var v = Date.parseDate(value, this.format);
18913 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18914 v = Date.parseDate(value, 'Y-m-d');
18916 if(!v && this.altFormats){
18917 if(!this.altFormatsArray){
18918 this.altFormatsArray = this.altFormats.split("|");
18920 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18921 v = Date.parseDate(value, this.altFormatsArray[i]);
18927 formatDate : function(date, fmt)
18929 return (!date || !(date instanceof Date)) ?
18930 date : date.dateFormat(fmt || this.format);
18933 onFocus : function()
18935 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18939 onBlur : function()
18941 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18943 var d = this.inputEl().getValue();
18950 showPopup : function()
18952 this.picker().show();
18956 this.fireEvent('showpopup', this, this.date);
18959 hidePopup : function()
18961 if(this.isInline) {
18964 this.picker().hide();
18965 this.viewMode = this.startViewMode;
18968 this.fireEvent('hidepopup', this, this.date);
18972 onMousedown: function(e)
18974 e.stopPropagation();
18975 e.preventDefault();
18980 Roo.bootstrap.DateField.superclass.keyup.call(this);
18984 setValue: function(v)
18986 if(this.fireEvent('beforeselect', this, v) !== false){
18987 var d = new Date(this.parseDate(v) ).clearTime();
18989 if(isNaN(d.getTime())){
18990 this.date = this.viewDate = '';
18991 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18995 v = this.formatDate(d);
18997 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18999 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19003 this.fireEvent('select', this, this.date);
19007 getValue: function()
19009 return this.formatDate(this.date);
19012 fireKey: function(e)
19014 if (!this.picker().isVisible()){
19015 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19021 var dateChanged = false,
19023 newDate, newViewDate;
19028 e.preventDefault();
19032 if (!this.keyboardNavigation) {
19035 dir = e.keyCode == 37 ? -1 : 1;
19038 newDate = this.moveYear(this.date, dir);
19039 newViewDate = this.moveYear(this.viewDate, dir);
19040 } else if (e.shiftKey){
19041 newDate = this.moveMonth(this.date, dir);
19042 newViewDate = this.moveMonth(this.viewDate, dir);
19044 newDate = new Date(this.date);
19045 newDate.setUTCDate(this.date.getUTCDate() + dir);
19046 newViewDate = new Date(this.viewDate);
19047 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19049 if (this.dateWithinRange(newDate)){
19050 this.date = newDate;
19051 this.viewDate = newViewDate;
19052 this.setValue(this.formatDate(this.date));
19054 e.preventDefault();
19055 dateChanged = true;
19060 if (!this.keyboardNavigation) {
19063 dir = e.keyCode == 38 ? -1 : 1;
19065 newDate = this.moveYear(this.date, dir);
19066 newViewDate = this.moveYear(this.viewDate, dir);
19067 } else if (e.shiftKey){
19068 newDate = this.moveMonth(this.date, dir);
19069 newViewDate = this.moveMonth(this.viewDate, dir);
19071 newDate = new Date(this.date);
19072 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19073 newViewDate = new Date(this.viewDate);
19074 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19076 if (this.dateWithinRange(newDate)){
19077 this.date = newDate;
19078 this.viewDate = newViewDate;
19079 this.setValue(this.formatDate(this.date));
19081 e.preventDefault();
19082 dateChanged = true;
19086 this.setValue(this.formatDate(this.date));
19088 e.preventDefault();
19091 this.setValue(this.formatDate(this.date));
19105 onClick: function(e)
19107 e.stopPropagation();
19108 e.preventDefault();
19110 var target = e.getTarget();
19112 if(target.nodeName.toLowerCase() === 'i'){
19113 target = Roo.get(target).dom.parentNode;
19116 var nodeName = target.nodeName;
19117 var className = target.className;
19118 var html = target.innerHTML;
19119 //Roo.log(nodeName);
19121 switch(nodeName.toLowerCase()) {
19123 switch(className) {
19129 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19130 switch(this.viewMode){
19132 this.viewDate = this.moveMonth(this.viewDate, dir);
19136 this.viewDate = this.moveYear(this.viewDate, dir);
19142 var date = new Date();
19143 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19145 this.setValue(this.formatDate(this.date));
19152 if (className.indexOf('disabled') < 0) {
19153 this.viewDate.setUTCDate(1);
19154 if (className.indexOf('month') > -1) {
19155 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19157 var year = parseInt(html, 10) || 0;
19158 this.viewDate.setUTCFullYear(year);
19162 if(this.singleMode){
19163 this.setValue(this.formatDate(this.viewDate));
19174 //Roo.log(className);
19175 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19176 var day = parseInt(html, 10) || 1;
19177 var year = this.viewDate.getUTCFullYear(),
19178 month = this.viewDate.getUTCMonth();
19180 if (className.indexOf('old') > -1) {
19187 } else if (className.indexOf('new') > -1) {
19195 //Roo.log([year,month,day]);
19196 this.date = this.UTCDate(year, month, day,0,0,0,0);
19197 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19199 //Roo.log(this.formatDate(this.date));
19200 this.setValue(this.formatDate(this.date));
19207 setStartDate: function(startDate)
19209 this.startDate = startDate || -Infinity;
19210 if (this.startDate !== -Infinity) {
19211 this.startDate = this.parseDate(this.startDate);
19214 this.updateNavArrows();
19217 setEndDate: function(endDate)
19219 this.endDate = endDate || Infinity;
19220 if (this.endDate !== Infinity) {
19221 this.endDate = this.parseDate(this.endDate);
19224 this.updateNavArrows();
19227 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19229 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19230 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19231 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19233 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19234 return parseInt(d, 10);
19237 this.updateNavArrows();
19240 updateNavArrows: function()
19242 if(this.singleMode){
19246 var d = new Date(this.viewDate),
19247 year = d.getUTCFullYear(),
19248 month = d.getUTCMonth();
19250 Roo.each(this.picker().select('.prev', true).elements, function(v){
19252 switch (this.viewMode) {
19255 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19261 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19268 Roo.each(this.picker().select('.next', true).elements, function(v){
19270 switch (this.viewMode) {
19273 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19279 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19287 moveMonth: function(date, dir)
19292 var new_date = new Date(date.valueOf()),
19293 day = new_date.getUTCDate(),
19294 month = new_date.getUTCMonth(),
19295 mag = Math.abs(dir),
19297 dir = dir > 0 ? 1 : -1;
19300 // If going back one month, make sure month is not current month
19301 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19303 return new_date.getUTCMonth() == month;
19305 // If going forward one month, make sure month is as expected
19306 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19308 return new_date.getUTCMonth() != new_month;
19310 new_month = month + dir;
19311 new_date.setUTCMonth(new_month);
19312 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19313 if (new_month < 0 || new_month > 11) {
19314 new_month = (new_month + 12) % 12;
19317 // For magnitudes >1, move one month at a time...
19318 for (var i=0; i<mag; i++) {
19319 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19320 new_date = this.moveMonth(new_date, dir);
19322 // ...then reset the day, keeping it in the new month
19323 new_month = new_date.getUTCMonth();
19324 new_date.setUTCDate(day);
19326 return new_month != new_date.getUTCMonth();
19329 // Common date-resetting loop -- if date is beyond end of month, make it
19332 new_date.setUTCDate(--day);
19333 new_date.setUTCMonth(new_month);
19338 moveYear: function(date, dir)
19340 return this.moveMonth(date, dir*12);
19343 dateWithinRange: function(date)
19345 return date >= this.startDate && date <= this.endDate;
19351 this.picker().remove();
19354 validateValue : function(value)
19356 if(this.getVisibilityEl().hasClass('hidden')){
19360 if(value.length < 1) {
19361 if(this.allowBlank){
19367 if(value.length < this.minLength){
19370 if(value.length > this.maxLength){
19374 var vt = Roo.form.VTypes;
19375 if(!vt[this.vtype](value, this)){
19379 if(typeof this.validator == "function"){
19380 var msg = this.validator(value);
19386 if(this.regex && !this.regex.test(value)){
19390 if(typeof(this.parseDate(value)) == 'undefined'){
19394 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19398 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19408 this.date = this.viewDate = '';
19410 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19415 Roo.apply(Roo.bootstrap.DateField, {
19426 html: '<i class="fa fa-arrow-left"/>'
19436 html: '<i class="fa fa-arrow-right"/>'
19478 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19479 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19480 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19481 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19482 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19495 navFnc: 'FullYear',
19500 navFnc: 'FullYear',
19505 Roo.apply(Roo.bootstrap.DateField, {
19509 cls: 'datepicker dropdown-menu roo-dynamic',
19513 cls: 'datepicker-days',
19517 cls: 'table-condensed',
19519 Roo.bootstrap.DateField.head,
19523 Roo.bootstrap.DateField.footer
19530 cls: 'datepicker-months',
19534 cls: 'table-condensed',
19536 Roo.bootstrap.DateField.head,
19537 Roo.bootstrap.DateField.content,
19538 Roo.bootstrap.DateField.footer
19545 cls: 'datepicker-years',
19549 cls: 'table-condensed',
19551 Roo.bootstrap.DateField.head,
19552 Roo.bootstrap.DateField.content,
19553 Roo.bootstrap.DateField.footer
19572 * @class Roo.bootstrap.TimeField
19573 * @extends Roo.bootstrap.Input
19574 * Bootstrap DateField class
19578 * Create a new TimeField
19579 * @param {Object} config The config object
19582 Roo.bootstrap.TimeField = function(config){
19583 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19587 * Fires when this field show.
19588 * @param {Roo.bootstrap.DateField} thisthis
19589 * @param {Mixed} date The date value
19594 * Fires when this field hide.
19595 * @param {Roo.bootstrap.DateField} this
19596 * @param {Mixed} date The date value
19601 * Fires when select a date.
19602 * @param {Roo.bootstrap.DateField} this
19603 * @param {Mixed} date The date value
19609 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19612 * @cfg {String} format
19613 * The default time format string which can be overriden for localization support. The format must be
19614 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19618 onRender: function(ct, position)
19621 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19623 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19625 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19627 this.pop = this.picker().select('>.datepicker-time',true).first();
19628 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19630 this.picker().on('mousedown', this.onMousedown, this);
19631 this.picker().on('click', this.onClick, this);
19633 this.picker().addClass('datepicker-dropdown');
19638 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19639 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19640 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19641 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19642 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19643 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19647 fireKey: function(e){
19648 if (!this.picker().isVisible()){
19649 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19655 e.preventDefault();
19663 this.onTogglePeriod();
19666 this.onIncrementMinutes();
19669 this.onDecrementMinutes();
19678 onClick: function(e) {
19679 e.stopPropagation();
19680 e.preventDefault();
19683 picker : function()
19685 return this.el.select('.datepicker', true).first();
19688 fillTime: function()
19690 var time = this.pop.select('tbody', true).first();
19692 time.dom.innerHTML = '';
19707 cls: 'hours-up glyphicon glyphicon-chevron-up'
19727 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19748 cls: 'timepicker-hour',
19763 cls: 'timepicker-minute',
19778 cls: 'btn btn-primary period',
19800 cls: 'hours-down glyphicon glyphicon-chevron-down'
19820 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19838 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19845 var hours = this.time.getHours();
19846 var minutes = this.time.getMinutes();
19859 hours = hours - 12;
19863 hours = '0' + hours;
19867 minutes = '0' + minutes;
19870 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19871 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19872 this.pop.select('button', true).first().dom.innerHTML = period;
19878 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19880 var cls = ['bottom'];
19882 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19889 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19894 this.picker().addClass(cls.join('-'));
19898 Roo.each(cls, function(c){
19900 _this.picker().setTop(_this.inputEl().getHeight());
19904 _this.picker().setTop(0 - _this.picker().getHeight());
19909 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19913 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19920 onFocus : function()
19922 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19926 onBlur : function()
19928 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19934 this.picker().show();
19939 this.fireEvent('show', this, this.date);
19944 this.picker().hide();
19947 this.fireEvent('hide', this, this.date);
19950 setTime : function()
19953 this.setValue(this.time.format(this.format));
19955 this.fireEvent('select', this, this.date);
19960 onMousedown: function(e){
19961 e.stopPropagation();
19962 e.preventDefault();
19965 onIncrementHours: function()
19967 Roo.log('onIncrementHours');
19968 this.time = this.time.add(Date.HOUR, 1);
19973 onDecrementHours: function()
19975 Roo.log('onDecrementHours');
19976 this.time = this.time.add(Date.HOUR, -1);
19980 onIncrementMinutes: function()
19982 Roo.log('onIncrementMinutes');
19983 this.time = this.time.add(Date.MINUTE, 1);
19987 onDecrementMinutes: function()
19989 Roo.log('onDecrementMinutes');
19990 this.time = this.time.add(Date.MINUTE, -1);
19994 onTogglePeriod: function()
19996 Roo.log('onTogglePeriod');
19997 this.time = this.time.add(Date.HOUR, 12);
20004 Roo.apply(Roo.bootstrap.TimeField, {
20034 cls: 'btn btn-info ok',
20046 Roo.apply(Roo.bootstrap.TimeField, {
20050 cls: 'datepicker dropdown-menu',
20054 cls: 'datepicker-time',
20058 cls: 'table-condensed',
20060 Roo.bootstrap.TimeField.content,
20061 Roo.bootstrap.TimeField.footer
20080 * @class Roo.bootstrap.MonthField
20081 * @extends Roo.bootstrap.Input
20082 * Bootstrap MonthField class
20084 * @cfg {String} language default en
20087 * Create a new MonthField
20088 * @param {Object} config The config object
20091 Roo.bootstrap.MonthField = function(config){
20092 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20097 * Fires when this field show.
20098 * @param {Roo.bootstrap.MonthField} this
20099 * @param {Mixed} date The date value
20104 * Fires when this field hide.
20105 * @param {Roo.bootstrap.MonthField} this
20106 * @param {Mixed} date The date value
20111 * Fires when select a date.
20112 * @param {Roo.bootstrap.MonthField} this
20113 * @param {String} oldvalue The old value
20114 * @param {String} newvalue The new value
20120 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20122 onRender: function(ct, position)
20125 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20127 this.language = this.language || 'en';
20128 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20129 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20131 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20132 this.isInline = false;
20133 this.isInput = true;
20134 this.component = this.el.select('.add-on', true).first() || false;
20135 this.component = (this.component && this.component.length === 0) ? false : this.component;
20136 this.hasInput = this.component && this.inputEL().length;
20138 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20140 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20142 this.picker().on('mousedown', this.onMousedown, this);
20143 this.picker().on('click', this.onClick, this);
20145 this.picker().addClass('datepicker-dropdown');
20147 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20148 v.setStyle('width', '189px');
20155 if(this.isInline) {
20161 setValue: function(v, suppressEvent)
20163 var o = this.getValue();
20165 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20169 if(suppressEvent !== true){
20170 this.fireEvent('select', this, o, v);
20175 getValue: function()
20180 onClick: function(e)
20182 e.stopPropagation();
20183 e.preventDefault();
20185 var target = e.getTarget();
20187 if(target.nodeName.toLowerCase() === 'i'){
20188 target = Roo.get(target).dom.parentNode;
20191 var nodeName = target.nodeName;
20192 var className = target.className;
20193 var html = target.innerHTML;
20195 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20199 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20201 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20207 picker : function()
20209 return this.pickerEl;
20212 fillMonths: function()
20215 var months = this.picker().select('>.datepicker-months td', true).first();
20217 months.dom.innerHTML = '';
20223 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20226 months.createChild(month);
20235 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20236 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20239 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20240 e.removeClass('active');
20242 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20243 e.addClass('active');
20250 if(this.isInline) {
20254 this.picker().removeClass(['bottom', 'top']);
20256 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20258 * place to the top of element!
20262 this.picker().addClass('top');
20263 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20268 this.picker().addClass('bottom');
20270 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20273 onFocus : function()
20275 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20279 onBlur : function()
20281 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20283 var d = this.inputEl().getValue();
20292 this.picker().show();
20293 this.picker().select('>.datepicker-months', true).first().show();
20297 this.fireEvent('show', this, this.date);
20302 if(this.isInline) {
20305 this.picker().hide();
20306 this.fireEvent('hide', this, this.date);
20310 onMousedown: function(e)
20312 e.stopPropagation();
20313 e.preventDefault();
20318 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20322 fireKey: function(e)
20324 if (!this.picker().isVisible()){
20325 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20336 e.preventDefault();
20340 dir = e.keyCode == 37 ? -1 : 1;
20342 this.vIndex = this.vIndex + dir;
20344 if(this.vIndex < 0){
20348 if(this.vIndex > 11){
20352 if(isNaN(this.vIndex)){
20356 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20362 dir = e.keyCode == 38 ? -1 : 1;
20364 this.vIndex = this.vIndex + dir * 4;
20366 if(this.vIndex < 0){
20370 if(this.vIndex > 11){
20374 if(isNaN(this.vIndex)){
20378 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20383 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20384 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20388 e.preventDefault();
20391 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20392 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20408 this.picker().remove();
20413 Roo.apply(Roo.bootstrap.MonthField, {
20432 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20433 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20438 Roo.apply(Roo.bootstrap.MonthField, {
20442 cls: 'datepicker dropdown-menu roo-dynamic',
20446 cls: 'datepicker-months',
20450 cls: 'table-condensed',
20452 Roo.bootstrap.DateField.content
20472 * @class Roo.bootstrap.CheckBox
20473 * @extends Roo.bootstrap.Input
20474 * Bootstrap CheckBox class
20476 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20477 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20478 * @cfg {String} boxLabel The text that appears beside the checkbox
20479 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20480 * @cfg {Boolean} checked initnal the element
20481 * @cfg {Boolean} inline inline the element (default false)
20482 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20483 * @cfg {String} tooltip label tooltip
20486 * Create a new CheckBox
20487 * @param {Object} config The config object
20490 Roo.bootstrap.CheckBox = function(config){
20491 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20496 * Fires when the element is checked or unchecked.
20497 * @param {Roo.bootstrap.CheckBox} this This input
20498 * @param {Boolean} checked The new checked value
20503 * Fires when the element is click.
20504 * @param {Roo.bootstrap.CheckBox} this This input
20511 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20513 inputType: 'checkbox',
20522 getAutoCreate : function()
20524 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20530 cfg.cls = 'form-group ' + this.inputType; //input-group
20533 cfg.cls += ' ' + this.inputType + '-inline';
20539 type : this.inputType,
20540 value : this.inputValue,
20541 cls : 'roo-' + this.inputType, //'form-box',
20542 placeholder : this.placeholder || ''
20546 if(this.inputType != 'radio'){
20550 cls : 'roo-hidden-value',
20551 value : this.checked ? this.inputValue : this.valueOff
20556 if (this.weight) { // Validity check?
20557 cfg.cls += " " + this.inputType + "-" + this.weight;
20560 if (this.disabled) {
20561 input.disabled=true;
20565 input.checked = this.checked;
20570 input.name = this.name;
20572 if(this.inputType != 'radio'){
20573 hidden.name = this.name;
20574 input.name = '_hidden_' + this.name;
20579 input.cls += ' input-' + this.size;
20584 ['xs','sm','md','lg'].map(function(size){
20585 if (settings[size]) {
20586 cfg.cls += ' col-' + size + '-' + settings[size];
20590 var inputblock = input;
20592 if (this.before || this.after) {
20595 cls : 'input-group',
20600 inputblock.cn.push({
20602 cls : 'input-group-addon',
20607 inputblock.cn.push(input);
20609 if(this.inputType != 'radio'){
20610 inputblock.cn.push(hidden);
20614 inputblock.cn.push({
20616 cls : 'input-group-addon',
20623 if (align ==='left' && this.fieldLabel.length) {
20624 // Roo.log("left and has label");
20629 cls : 'control-label',
20630 html : this.fieldLabel
20640 if(this.labelWidth > 12){
20641 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20644 if(this.labelWidth < 13 && this.labelmd == 0){
20645 this.labelmd = this.labelWidth;
20648 if(this.labellg > 0){
20649 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20650 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20653 if(this.labelmd > 0){
20654 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20655 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20658 if(this.labelsm > 0){
20659 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20660 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20663 if(this.labelxs > 0){
20664 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20665 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20668 } else if ( this.fieldLabel.length) {
20669 // Roo.log(" label");
20673 tag: this.boxLabel ? 'span' : 'label',
20675 cls: 'control-label box-input-label',
20676 //cls : 'input-group-addon',
20677 html : this.fieldLabel
20686 // Roo.log(" no label && no align");
20687 cfg.cn = [ inputblock ] ;
20693 var boxLabelCfg = {
20695 //'for': id, // box label is handled by onclick - so no for...
20697 html: this.boxLabel
20701 boxLabelCfg.tooltip = this.tooltip;
20704 cfg.cn.push(boxLabelCfg);
20707 if(this.inputType != 'radio'){
20708 cfg.cn.push(hidden);
20716 * return the real input element.
20718 inputEl: function ()
20720 return this.el.select('input.roo-' + this.inputType,true).first();
20722 hiddenEl: function ()
20724 return this.el.select('input.roo-hidden-value',true).first();
20727 labelEl: function()
20729 return this.el.select('label.control-label',true).first();
20731 /* depricated... */
20735 return this.labelEl();
20738 boxLabelEl: function()
20740 return this.el.select('label.box-label',true).first();
20743 initEvents : function()
20745 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20747 this.inputEl().on('click', this.onClick, this);
20749 if (this.boxLabel) {
20750 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20753 this.startValue = this.getValue();
20756 Roo.bootstrap.CheckBox.register(this);
20760 onClick : function(e)
20762 if(this.fireEvent('click', this, e) !== false){
20763 this.setChecked(!this.checked);
20768 setChecked : function(state,suppressEvent)
20770 this.startValue = this.getValue();
20772 if(this.inputType == 'radio'){
20774 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20775 e.dom.checked = false;
20778 this.inputEl().dom.checked = true;
20780 this.inputEl().dom.value = this.inputValue;
20782 if(suppressEvent !== true){
20783 this.fireEvent('check', this, true);
20791 this.checked = state;
20793 this.inputEl().dom.checked = state;
20796 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20798 if(suppressEvent !== true){
20799 this.fireEvent('check', this, state);
20805 getValue : function()
20807 if(this.inputType == 'radio'){
20808 return this.getGroupValue();
20811 return this.hiddenEl().dom.value;
20815 getGroupValue : function()
20817 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20821 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20824 setValue : function(v,suppressEvent)
20826 if(this.inputType == 'radio'){
20827 this.setGroupValue(v, suppressEvent);
20831 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20836 setGroupValue : function(v, suppressEvent)
20838 this.startValue = this.getValue();
20840 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20841 e.dom.checked = false;
20843 if(e.dom.value == v){
20844 e.dom.checked = true;
20848 if(suppressEvent !== true){
20849 this.fireEvent('check', this, true);
20857 validate : function()
20859 if(this.getVisibilityEl().hasClass('hidden')){
20865 (this.inputType == 'radio' && this.validateRadio()) ||
20866 (this.inputType == 'checkbox' && this.validateCheckbox())
20872 this.markInvalid();
20876 validateRadio : function()
20878 if(this.getVisibilityEl().hasClass('hidden')){
20882 if(this.allowBlank){
20888 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20889 if(!e.dom.checked){
20901 validateCheckbox : function()
20904 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20905 //return (this.getValue() == this.inputValue) ? true : false;
20908 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20916 for(var i in group){
20917 if(group[i].el.isVisible(true)){
20925 for(var i in group){
20930 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20937 * Mark this field as valid
20939 markValid : function()
20943 this.fireEvent('valid', this);
20945 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20948 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20955 if(this.inputType == 'radio'){
20956 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20957 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20958 e.findParent('.form-group', false, true).addClass(_this.validClass);
20965 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20966 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20970 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20976 for(var i in group){
20977 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20978 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20983 * Mark this field as invalid
20984 * @param {String} msg The validation message
20986 markInvalid : function(msg)
20988 if(this.allowBlank){
20994 this.fireEvent('invalid', this, msg);
20996 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20999 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21003 label.markInvalid();
21006 if(this.inputType == 'radio'){
21007 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21008 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21009 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21016 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21017 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21021 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21027 for(var i in group){
21028 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21029 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21034 clearInvalid : function()
21036 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21038 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21040 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21042 if (label && label.iconEl) {
21043 label.iconEl.removeClass(label.validClass);
21044 label.iconEl.removeClass(label.invalidClass);
21048 disable : function()
21050 if(this.inputType != 'radio'){
21051 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21058 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21059 _this.getActionEl().addClass(this.disabledClass);
21060 e.dom.disabled = true;
21064 this.disabled = true;
21065 this.fireEvent("disable", this);
21069 enable : function()
21071 if(this.inputType != 'radio'){
21072 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21079 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21080 _this.getActionEl().removeClass(this.disabledClass);
21081 e.dom.disabled = false;
21085 this.disabled = false;
21086 this.fireEvent("enable", this);
21090 setBoxLabel : function(v)
21095 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21101 Roo.apply(Roo.bootstrap.CheckBox, {
21106 * register a CheckBox Group
21107 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21109 register : function(checkbox)
21111 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21112 this.groups[checkbox.groupId] = {};
21115 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21119 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21123 * fetch a CheckBox Group based on the group ID
21124 * @param {string} the group ID
21125 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21127 get: function(groupId) {
21128 if (typeof(this.groups[groupId]) == 'undefined') {
21132 return this.groups[groupId] ;
21145 * @class Roo.bootstrap.Radio
21146 * @extends Roo.bootstrap.Component
21147 * Bootstrap Radio class
21148 * @cfg {String} boxLabel - the label associated
21149 * @cfg {String} value - the value of radio
21152 * Create a new Radio
21153 * @param {Object} config The config object
21155 Roo.bootstrap.Radio = function(config){
21156 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21160 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21166 getAutoCreate : function()
21170 cls : 'form-group radio',
21175 html : this.boxLabel
21183 initEvents : function()
21185 this.parent().register(this);
21187 this.el.on('click', this.onClick, this);
21191 onClick : function(e)
21193 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21194 this.setChecked(true);
21198 setChecked : function(state, suppressEvent)
21200 this.parent().setValue(this.value, suppressEvent);
21204 setBoxLabel : function(v)
21209 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21224 * @class Roo.bootstrap.SecurePass
21225 * @extends Roo.bootstrap.Input
21226 * Bootstrap SecurePass class
21230 * Create a new SecurePass
21231 * @param {Object} config The config object
21234 Roo.bootstrap.SecurePass = function (config) {
21235 // these go here, so the translation tool can replace them..
21237 PwdEmpty: "Please type a password, and then retype it to confirm.",
21238 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21239 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21240 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21241 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21242 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21243 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21244 TooWeak: "Your password is Too Weak."
21246 this.meterLabel = "Password strength:";
21247 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21248 this.meterClass = [
21249 "roo-password-meter-tooweak",
21250 "roo-password-meter-weak",
21251 "roo-password-meter-medium",
21252 "roo-password-meter-strong",
21253 "roo-password-meter-grey"
21258 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21261 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21263 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21265 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21266 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21267 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21268 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21269 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21270 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21271 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21281 * @cfg {String/Object} Label for the strength meter (defaults to
21282 * 'Password strength:')
21287 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21288 * ['Weak', 'Medium', 'Strong'])
21291 pwdStrengths: false,
21304 initEvents: function ()
21306 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21308 if (this.el.is('input[type=password]') && Roo.isSafari) {
21309 this.el.on('keydown', this.SafariOnKeyDown, this);
21312 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21315 onRender: function (ct, position)
21317 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21318 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21319 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21321 this.trigger.createChild({
21326 cls: 'roo-password-meter-grey col-xs-12',
21329 //width: this.meterWidth + 'px'
21333 cls: 'roo-password-meter-text'
21339 if (this.hideTrigger) {
21340 this.trigger.setDisplayed(false);
21342 this.setSize(this.width || '', this.height || '');
21345 onDestroy: function ()
21347 if (this.trigger) {
21348 this.trigger.removeAllListeners();
21349 this.trigger.remove();
21352 this.wrap.remove();
21354 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21357 checkStrength: function ()
21359 var pwd = this.inputEl().getValue();
21360 if (pwd == this._lastPwd) {
21365 if (this.ClientSideStrongPassword(pwd)) {
21367 } else if (this.ClientSideMediumPassword(pwd)) {
21369 } else if (this.ClientSideWeakPassword(pwd)) {
21375 Roo.log('strength1: ' + strength);
21377 //var pm = this.trigger.child('div/div/div').dom;
21378 var pm = this.trigger.child('div/div');
21379 pm.removeClass(this.meterClass);
21380 pm.addClass(this.meterClass[strength]);
21383 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21385 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21387 this._lastPwd = pwd;
21391 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21393 this._lastPwd = '';
21395 var pm = this.trigger.child('div/div');
21396 pm.removeClass(this.meterClass);
21397 pm.addClass('roo-password-meter-grey');
21400 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21403 this.inputEl().dom.type='password';
21406 validateValue: function (value)
21409 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21412 if (value.length == 0) {
21413 if (this.allowBlank) {
21414 this.clearInvalid();
21418 this.markInvalid(this.errors.PwdEmpty);
21419 this.errorMsg = this.errors.PwdEmpty;
21427 if ('[\x21-\x7e]*'.match(value)) {
21428 this.markInvalid(this.errors.PwdBadChar);
21429 this.errorMsg = this.errors.PwdBadChar;
21432 if (value.length < 6) {
21433 this.markInvalid(this.errors.PwdShort);
21434 this.errorMsg = this.errors.PwdShort;
21437 if (value.length > 16) {
21438 this.markInvalid(this.errors.PwdLong);
21439 this.errorMsg = this.errors.PwdLong;
21443 if (this.ClientSideStrongPassword(value)) {
21445 } else if (this.ClientSideMediumPassword(value)) {
21447 } else if (this.ClientSideWeakPassword(value)) {
21454 if (strength < 2) {
21455 //this.markInvalid(this.errors.TooWeak);
21456 this.errorMsg = this.errors.TooWeak;
21461 console.log('strength2: ' + strength);
21463 //var pm = this.trigger.child('div/div/div').dom;
21465 var pm = this.trigger.child('div/div');
21466 pm.removeClass(this.meterClass);
21467 pm.addClass(this.meterClass[strength]);
21469 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21471 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21473 this.errorMsg = '';
21477 CharacterSetChecks: function (type)
21480 this.fResult = false;
21483 isctype: function (character, type)
21486 case this.kCapitalLetter:
21487 if (character >= 'A' && character <= 'Z') {
21492 case this.kSmallLetter:
21493 if (character >= 'a' && character <= 'z') {
21499 if (character >= '0' && character <= '9') {
21504 case this.kPunctuation:
21505 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21516 IsLongEnough: function (pwd, size)
21518 return !(pwd == null || isNaN(size) || pwd.length < size);
21521 SpansEnoughCharacterSets: function (word, nb)
21523 if (!this.IsLongEnough(word, nb))
21528 var characterSetChecks = new Array(
21529 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21530 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21533 for (var index = 0; index < word.length; ++index) {
21534 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21535 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21536 characterSetChecks[nCharSet].fResult = true;
21543 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21544 if (characterSetChecks[nCharSet].fResult) {
21549 if (nCharSets < nb) {
21555 ClientSideStrongPassword: function (pwd)
21557 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21560 ClientSideMediumPassword: function (pwd)
21562 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21565 ClientSideWeakPassword: function (pwd)
21567 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21570 })//<script type="text/javascript">
21573 * Based Ext JS Library 1.1.1
21574 * Copyright(c) 2006-2007, Ext JS, LLC.
21580 * @class Roo.HtmlEditorCore
21581 * @extends Roo.Component
21582 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21584 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21587 Roo.HtmlEditorCore = function(config){
21590 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21595 * @event initialize
21596 * Fires when the editor is fully initialized (including the iframe)
21597 * @param {Roo.HtmlEditorCore} this
21602 * Fires when the editor is first receives the focus. Any insertion must wait
21603 * until after this event.
21604 * @param {Roo.HtmlEditorCore} this
21608 * @event beforesync
21609 * Fires before the textarea is updated with content from the editor iframe. Return false
21610 * to cancel the sync.
21611 * @param {Roo.HtmlEditorCore} this
21612 * @param {String} html
21616 * @event beforepush
21617 * Fires before the iframe editor is updated with content from the textarea. Return false
21618 * to cancel the push.
21619 * @param {Roo.HtmlEditorCore} this
21620 * @param {String} html
21625 * Fires when the textarea is updated with content from the editor iframe.
21626 * @param {Roo.HtmlEditorCore} this
21627 * @param {String} html
21632 * Fires when the iframe editor is updated with content from the textarea.
21633 * @param {Roo.HtmlEditorCore} this
21634 * @param {String} html
21639 * @event editorevent
21640 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21641 * @param {Roo.HtmlEditorCore} this
21647 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21649 // defaults : white / black...
21650 this.applyBlacklists();
21657 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21661 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21667 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21672 * @cfg {Number} height (in pixels)
21676 * @cfg {Number} width (in pixels)
21681 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21684 stylesheets: false,
21689 // private properties
21690 validationEvent : false,
21692 initialized : false,
21694 sourceEditMode : false,
21695 onFocus : Roo.emptyFn,
21697 hideMode:'offsets',
21701 // blacklist + whitelisted elements..
21708 * Protected method that will not generally be called directly. It
21709 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21710 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21712 getDocMarkup : function(){
21716 // inherit styels from page...??
21717 if (this.stylesheets === false) {
21719 Roo.get(document.head).select('style').each(function(node) {
21720 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21723 Roo.get(document.head).select('link').each(function(node) {
21724 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21727 } else if (!this.stylesheets.length) {
21729 st = '<style type="text/css">' +
21730 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21733 st = '<style type="text/css">' +
21738 st += '<style type="text/css">' +
21739 'IMG { cursor: pointer } ' +
21742 var cls = 'roo-htmleditor-body';
21744 if(this.bodyCls.length){
21745 cls += ' ' + this.bodyCls;
21748 return '<html><head>' + st +
21749 //<style type="text/css">' +
21750 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21752 ' </head><body class="' + cls + '"></body></html>';
21756 onRender : function(ct, position)
21759 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21760 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21763 this.el.dom.style.border = '0 none';
21764 this.el.dom.setAttribute('tabIndex', -1);
21765 this.el.addClass('x-hidden hide');
21769 if(Roo.isIE){ // fix IE 1px bogus margin
21770 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21774 this.frameId = Roo.id();
21778 var iframe = this.owner.wrap.createChild({
21780 cls: 'form-control', // bootstrap..
21782 name: this.frameId,
21783 frameBorder : 'no',
21784 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21789 this.iframe = iframe.dom;
21791 this.assignDocWin();
21793 this.doc.designMode = 'on';
21796 this.doc.write(this.getDocMarkup());
21800 var task = { // must defer to wait for browser to be ready
21802 //console.log("run task?" + this.doc.readyState);
21803 this.assignDocWin();
21804 if(this.doc.body || this.doc.readyState == 'complete'){
21806 this.doc.designMode="on";
21810 Roo.TaskMgr.stop(task);
21811 this.initEditor.defer(10, this);
21818 Roo.TaskMgr.start(task);
21823 onResize : function(w, h)
21825 Roo.log('resize: ' +w + ',' + h );
21826 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21830 if(typeof w == 'number'){
21832 this.iframe.style.width = w + 'px';
21834 if(typeof h == 'number'){
21836 this.iframe.style.height = h + 'px';
21838 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21845 * Toggles the editor between standard and source edit mode.
21846 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21848 toggleSourceEdit : function(sourceEditMode){
21850 this.sourceEditMode = sourceEditMode === true;
21852 if(this.sourceEditMode){
21854 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21857 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21858 //this.iframe.className = '';
21861 //this.setSize(this.owner.wrap.getSize());
21862 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21869 * Protected method that will not generally be called directly. If you need/want
21870 * custom HTML cleanup, this is the method you should override.
21871 * @param {String} html The HTML to be cleaned
21872 * return {String} The cleaned HTML
21874 cleanHtml : function(html){
21875 html = String(html);
21876 if(html.length > 5){
21877 if(Roo.isSafari){ // strip safari nonsense
21878 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21881 if(html == ' '){
21888 * HTML Editor -> Textarea
21889 * Protected method that will not generally be called directly. Syncs the contents
21890 * of the editor iframe with the textarea.
21892 syncValue : function(){
21893 if(this.initialized){
21894 var bd = (this.doc.body || this.doc.documentElement);
21895 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21896 var html = bd.innerHTML;
21898 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21899 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21901 html = '<div style="'+m[0]+'">' + html + '</div>';
21904 html = this.cleanHtml(html);
21905 // fix up the special chars.. normaly like back quotes in word...
21906 // however we do not want to do this with chinese..
21907 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21908 var cc = b.charCodeAt();
21910 (cc >= 0x4E00 && cc < 0xA000 ) ||
21911 (cc >= 0x3400 && cc < 0x4E00 ) ||
21912 (cc >= 0xf900 && cc < 0xfb00 )
21918 if(this.owner.fireEvent('beforesync', this, html) !== false){
21919 this.el.dom.value = html;
21920 this.owner.fireEvent('sync', this, html);
21926 * Protected method that will not generally be called directly. Pushes the value of the textarea
21927 * into the iframe editor.
21929 pushValue : function(){
21930 if(this.initialized){
21931 var v = this.el.dom.value.trim();
21933 // if(v.length < 1){
21937 if(this.owner.fireEvent('beforepush', this, v) !== false){
21938 var d = (this.doc.body || this.doc.documentElement);
21940 this.cleanUpPaste();
21941 this.el.dom.value = d.innerHTML;
21942 this.owner.fireEvent('push', this, v);
21948 deferFocus : function(){
21949 this.focus.defer(10, this);
21953 focus : function(){
21954 if(this.win && !this.sourceEditMode){
21961 assignDocWin: function()
21963 var iframe = this.iframe;
21966 this.doc = iframe.contentWindow.document;
21967 this.win = iframe.contentWindow;
21969 // if (!Roo.get(this.frameId)) {
21972 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21973 // this.win = Roo.get(this.frameId).dom.contentWindow;
21975 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21979 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21980 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21985 initEditor : function(){
21986 //console.log("INIT EDITOR");
21987 this.assignDocWin();
21991 this.doc.designMode="on";
21993 this.doc.write(this.getDocMarkup());
21996 var dbody = (this.doc.body || this.doc.documentElement);
21997 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21998 // this copies styles from the containing element into thsi one..
21999 // not sure why we need all of this..
22000 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22002 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22003 //ss['background-attachment'] = 'fixed'; // w3c
22004 dbody.bgProperties = 'fixed'; // ie
22005 //Roo.DomHelper.applyStyles(dbody, ss);
22006 Roo.EventManager.on(this.doc, {
22007 //'mousedown': this.onEditorEvent,
22008 'mouseup': this.onEditorEvent,
22009 'dblclick': this.onEditorEvent,
22010 'click': this.onEditorEvent,
22011 'keyup': this.onEditorEvent,
22016 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22018 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22019 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22021 this.initialized = true;
22023 this.owner.fireEvent('initialize', this);
22028 onDestroy : function(){
22034 //for (var i =0; i < this.toolbars.length;i++) {
22035 // // fixme - ask toolbars for heights?
22036 // this.toolbars[i].onDestroy();
22039 //this.wrap.dom.innerHTML = '';
22040 //this.wrap.remove();
22045 onFirstFocus : function(){
22047 this.assignDocWin();
22050 this.activated = true;
22053 if(Roo.isGecko){ // prevent silly gecko errors
22055 var s = this.win.getSelection();
22056 if(!s.focusNode || s.focusNode.nodeType != 3){
22057 var r = s.getRangeAt(0);
22058 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22063 this.execCmd('useCSS', true);
22064 this.execCmd('styleWithCSS', false);
22067 this.owner.fireEvent('activate', this);
22071 adjustFont: function(btn){
22072 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22073 //if(Roo.isSafari){ // safari
22076 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22077 if(Roo.isSafari){ // safari
22078 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22079 v = (v < 10) ? 10 : v;
22080 v = (v > 48) ? 48 : v;
22081 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22086 v = Math.max(1, v+adjust);
22088 this.execCmd('FontSize', v );
22091 onEditorEvent : function(e)
22093 this.owner.fireEvent('editorevent', this, e);
22094 // this.updateToolbar();
22095 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22098 insertTag : function(tg)
22100 // could be a bit smarter... -> wrap the current selected tRoo..
22101 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22103 range = this.createRange(this.getSelection());
22104 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22105 wrappingNode.appendChild(range.extractContents());
22106 range.insertNode(wrappingNode);
22113 this.execCmd("formatblock", tg);
22117 insertText : function(txt)
22121 var range = this.createRange();
22122 range.deleteContents();
22123 //alert(Sender.getAttribute('label'));
22125 range.insertNode(this.doc.createTextNode(txt));
22131 * Executes a Midas editor command on the editor document and performs necessary focus and
22132 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22133 * @param {String} cmd The Midas command
22134 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22136 relayCmd : function(cmd, value){
22138 this.execCmd(cmd, value);
22139 this.owner.fireEvent('editorevent', this);
22140 //this.updateToolbar();
22141 this.owner.deferFocus();
22145 * Executes a Midas editor command directly on the editor document.
22146 * For visual commands, you should use {@link #relayCmd} instead.
22147 * <b>This should only be called after the editor is initialized.</b>
22148 * @param {String} cmd The Midas command
22149 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22151 execCmd : function(cmd, value){
22152 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22159 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22161 * @param {String} text | dom node..
22163 insertAtCursor : function(text)
22166 if(!this.activated){
22172 var r = this.doc.selection.createRange();
22183 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22187 // from jquery ui (MIT licenced)
22189 var win = this.win;
22191 if (win.getSelection && win.getSelection().getRangeAt) {
22192 range = win.getSelection().getRangeAt(0);
22193 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22194 range.insertNode(node);
22195 } else if (win.document.selection && win.document.selection.createRange) {
22196 // no firefox support
22197 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22198 win.document.selection.createRange().pasteHTML(txt);
22200 // no firefox support
22201 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22202 this.execCmd('InsertHTML', txt);
22211 mozKeyPress : function(e){
22213 var c = e.getCharCode(), cmd;
22216 c = String.fromCharCode(c).toLowerCase();
22230 this.cleanUpPaste.defer(100, this);
22238 e.preventDefault();
22246 fixKeys : function(){ // load time branching for fastest keydown performance
22248 return function(e){
22249 var k = e.getKey(), r;
22252 r = this.doc.selection.createRange();
22255 r.pasteHTML('    ');
22262 r = this.doc.selection.createRange();
22264 var target = r.parentElement();
22265 if(!target || target.tagName.toLowerCase() != 'li'){
22267 r.pasteHTML('<br />');
22273 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22274 this.cleanUpPaste.defer(100, this);
22280 }else if(Roo.isOpera){
22281 return function(e){
22282 var k = e.getKey();
22286 this.execCmd('InsertHTML','    ');
22289 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22290 this.cleanUpPaste.defer(100, this);
22295 }else if(Roo.isSafari){
22296 return function(e){
22297 var k = e.getKey();
22301 this.execCmd('InsertText','\t');
22305 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22306 this.cleanUpPaste.defer(100, this);
22314 getAllAncestors: function()
22316 var p = this.getSelectedNode();
22319 a.push(p); // push blank onto stack..
22320 p = this.getParentElement();
22324 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22328 a.push(this.doc.body);
22332 lastSelNode : false,
22335 getSelection : function()
22337 this.assignDocWin();
22338 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22341 getSelectedNode: function()
22343 // this may only work on Gecko!!!
22345 // should we cache this!!!!
22350 var range = this.createRange(this.getSelection()).cloneRange();
22353 var parent = range.parentElement();
22355 var testRange = range.duplicate();
22356 testRange.moveToElementText(parent);
22357 if (testRange.inRange(range)) {
22360 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22363 parent = parent.parentElement;
22368 // is ancestor a text element.
22369 var ac = range.commonAncestorContainer;
22370 if (ac.nodeType == 3) {
22371 ac = ac.parentNode;
22374 var ar = ac.childNodes;
22377 var other_nodes = [];
22378 var has_other_nodes = false;
22379 for (var i=0;i<ar.length;i++) {
22380 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22383 // fullly contained node.
22385 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22390 // probably selected..
22391 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22392 other_nodes.push(ar[i]);
22396 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22401 has_other_nodes = true;
22403 if (!nodes.length && other_nodes.length) {
22404 nodes= other_nodes;
22406 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22412 createRange: function(sel)
22414 // this has strange effects when using with
22415 // top toolbar - not sure if it's a great idea.
22416 //this.editor.contentWindow.focus();
22417 if (typeof sel != "undefined") {
22419 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22421 return this.doc.createRange();
22424 return this.doc.createRange();
22427 getParentElement: function()
22430 this.assignDocWin();
22431 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22433 var range = this.createRange(sel);
22436 var p = range.commonAncestorContainer;
22437 while (p.nodeType == 3) { // text node
22448 * Range intersection.. the hard stuff...
22452 * [ -- selected range --- ]
22456 * if end is before start or hits it. fail.
22457 * if start is after end or hits it fail.
22459 * if either hits (but other is outside. - then it's not
22465 // @see http://www.thismuchiknow.co.uk/?p=64.
22466 rangeIntersectsNode : function(range, node)
22468 var nodeRange = node.ownerDocument.createRange();
22470 nodeRange.selectNode(node);
22472 nodeRange.selectNodeContents(node);
22475 var rangeStartRange = range.cloneRange();
22476 rangeStartRange.collapse(true);
22478 var rangeEndRange = range.cloneRange();
22479 rangeEndRange.collapse(false);
22481 var nodeStartRange = nodeRange.cloneRange();
22482 nodeStartRange.collapse(true);
22484 var nodeEndRange = nodeRange.cloneRange();
22485 nodeEndRange.collapse(false);
22487 return rangeStartRange.compareBoundaryPoints(
22488 Range.START_TO_START, nodeEndRange) == -1 &&
22489 rangeEndRange.compareBoundaryPoints(
22490 Range.START_TO_START, nodeStartRange) == 1;
22494 rangeCompareNode : function(range, node)
22496 var nodeRange = node.ownerDocument.createRange();
22498 nodeRange.selectNode(node);
22500 nodeRange.selectNodeContents(node);
22504 range.collapse(true);
22506 nodeRange.collapse(true);
22508 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22509 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22511 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22513 var nodeIsBefore = ss == 1;
22514 var nodeIsAfter = ee == -1;
22516 if (nodeIsBefore && nodeIsAfter) {
22519 if (!nodeIsBefore && nodeIsAfter) {
22520 return 1; //right trailed.
22523 if (nodeIsBefore && !nodeIsAfter) {
22524 return 2; // left trailed.
22530 // private? - in a new class?
22531 cleanUpPaste : function()
22533 // cleans up the whole document..
22534 Roo.log('cleanuppaste');
22536 this.cleanUpChildren(this.doc.body);
22537 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22538 if (clean != this.doc.body.innerHTML) {
22539 this.doc.body.innerHTML = clean;
22544 cleanWordChars : function(input) {// change the chars to hex code
22545 var he = Roo.HtmlEditorCore;
22547 var output = input;
22548 Roo.each(he.swapCodes, function(sw) {
22549 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22551 output = output.replace(swapper, sw[1]);
22558 cleanUpChildren : function (n)
22560 if (!n.childNodes.length) {
22563 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22564 this.cleanUpChild(n.childNodes[i]);
22571 cleanUpChild : function (node)
22574 //console.log(node);
22575 if (node.nodeName == "#text") {
22576 // clean up silly Windows -- stuff?
22579 if (node.nodeName == "#comment") {
22580 node.parentNode.removeChild(node);
22581 // clean up silly Windows -- stuff?
22584 var lcname = node.tagName.toLowerCase();
22585 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22586 // whitelist of tags..
22588 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22590 node.parentNode.removeChild(node);
22595 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22597 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22598 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22600 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22601 // remove_keep_children = true;
22604 if (remove_keep_children) {
22605 this.cleanUpChildren(node);
22606 // inserts everything just before this node...
22607 while (node.childNodes.length) {
22608 var cn = node.childNodes[0];
22609 node.removeChild(cn);
22610 node.parentNode.insertBefore(cn, node);
22612 node.parentNode.removeChild(node);
22616 if (!node.attributes || !node.attributes.length) {
22617 this.cleanUpChildren(node);
22621 function cleanAttr(n,v)
22624 if (v.match(/^\./) || v.match(/^\//)) {
22627 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22630 if (v.match(/^#/)) {
22633 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22634 node.removeAttribute(n);
22638 var cwhite = this.cwhite;
22639 var cblack = this.cblack;
22641 function cleanStyle(n,v)
22643 if (v.match(/expression/)) { //XSS?? should we even bother..
22644 node.removeAttribute(n);
22648 var parts = v.split(/;/);
22651 Roo.each(parts, function(p) {
22652 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22656 var l = p.split(':').shift().replace(/\s+/g,'');
22657 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22659 if ( cwhite.length && cblack.indexOf(l) > -1) {
22660 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22661 //node.removeAttribute(n);
22665 // only allow 'c whitelisted system attributes'
22666 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22667 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22668 //node.removeAttribute(n);
22678 if (clean.length) {
22679 node.setAttribute(n, clean.join(';'));
22681 node.removeAttribute(n);
22687 for (var i = node.attributes.length-1; i > -1 ; i--) {
22688 var a = node.attributes[i];
22691 if (a.name.toLowerCase().substr(0,2)=='on') {
22692 node.removeAttribute(a.name);
22695 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22696 node.removeAttribute(a.name);
22699 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22700 cleanAttr(a.name,a.value); // fixme..
22703 if (a.name == 'style') {
22704 cleanStyle(a.name,a.value);
22707 /// clean up MS crap..
22708 // tecnically this should be a list of valid class'es..
22711 if (a.name == 'class') {
22712 if (a.value.match(/^Mso/)) {
22713 node.className = '';
22716 if (a.value.match(/^body$/)) {
22717 node.className = '';
22728 this.cleanUpChildren(node);
22734 * Clean up MS wordisms...
22736 cleanWord : function(node)
22741 this.cleanWord(this.doc.body);
22744 if (node.nodeName == "#text") {
22745 // clean up silly Windows -- stuff?
22748 if (node.nodeName == "#comment") {
22749 node.parentNode.removeChild(node);
22750 // clean up silly Windows -- stuff?
22754 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22755 node.parentNode.removeChild(node);
22759 // remove - but keep children..
22760 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22761 while (node.childNodes.length) {
22762 var cn = node.childNodes[0];
22763 node.removeChild(cn);
22764 node.parentNode.insertBefore(cn, node);
22766 node.parentNode.removeChild(node);
22767 this.iterateChildren(node, this.cleanWord);
22771 if (node.className.length) {
22773 var cn = node.className.split(/\W+/);
22775 Roo.each(cn, function(cls) {
22776 if (cls.match(/Mso[a-zA-Z]+/)) {
22781 node.className = cna.length ? cna.join(' ') : '';
22783 node.removeAttribute("class");
22787 if (node.hasAttribute("lang")) {
22788 node.removeAttribute("lang");
22791 if (node.hasAttribute("style")) {
22793 var styles = node.getAttribute("style").split(";");
22795 Roo.each(styles, function(s) {
22796 if (!s.match(/:/)) {
22799 var kv = s.split(":");
22800 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22803 // what ever is left... we allow.
22806 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22807 if (!nstyle.length) {
22808 node.removeAttribute('style');
22811 this.iterateChildren(node, this.cleanWord);
22817 * iterateChildren of a Node, calling fn each time, using this as the scole..
22818 * @param {DomNode} node node to iterate children of.
22819 * @param {Function} fn method of this class to call on each item.
22821 iterateChildren : function(node, fn)
22823 if (!node.childNodes.length) {
22826 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22827 fn.call(this, node.childNodes[i])
22833 * cleanTableWidths.
22835 * Quite often pasting from word etc.. results in tables with column and widths.
22836 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22839 cleanTableWidths : function(node)
22844 this.cleanTableWidths(this.doc.body);
22849 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22852 Roo.log(node.tagName);
22853 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22854 this.iterateChildren(node, this.cleanTableWidths);
22857 if (node.hasAttribute('width')) {
22858 node.removeAttribute('width');
22862 if (node.hasAttribute("style")) {
22865 var styles = node.getAttribute("style").split(";");
22867 Roo.each(styles, function(s) {
22868 if (!s.match(/:/)) {
22871 var kv = s.split(":");
22872 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22875 // what ever is left... we allow.
22878 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22879 if (!nstyle.length) {
22880 node.removeAttribute('style');
22884 this.iterateChildren(node, this.cleanTableWidths);
22892 domToHTML : function(currentElement, depth, nopadtext) {
22894 depth = depth || 0;
22895 nopadtext = nopadtext || false;
22897 if (!currentElement) {
22898 return this.domToHTML(this.doc.body);
22901 //Roo.log(currentElement);
22903 var allText = false;
22904 var nodeName = currentElement.nodeName;
22905 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22907 if (nodeName == '#text') {
22909 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22914 if (nodeName != 'BODY') {
22917 // Prints the node tagName, such as <A>, <IMG>, etc
22920 for(i = 0; i < currentElement.attributes.length;i++) {
22922 var aname = currentElement.attributes.item(i).name;
22923 if (!currentElement.attributes.item(i).value.length) {
22926 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22929 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22938 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22941 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22946 // Traverse the tree
22948 var currentElementChild = currentElement.childNodes.item(i);
22949 var allText = true;
22950 var innerHTML = '';
22952 while (currentElementChild) {
22953 // Formatting code (indent the tree so it looks nice on the screen)
22954 var nopad = nopadtext;
22955 if (lastnode == 'SPAN') {
22959 if (currentElementChild.nodeName == '#text') {
22960 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22961 toadd = nopadtext ? toadd : toadd.trim();
22962 if (!nopad && toadd.length > 80) {
22963 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22965 innerHTML += toadd;
22968 currentElementChild = currentElement.childNodes.item(i);
22974 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22976 // Recursively traverse the tree structure of the child node
22977 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22978 lastnode = currentElementChild.nodeName;
22980 currentElementChild=currentElement.childNodes.item(i);
22986 // The remaining code is mostly for formatting the tree
22987 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22992 ret+= "</"+tagName+">";
22998 applyBlacklists : function()
23000 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23001 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23005 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23006 if (b.indexOf(tag) > -1) {
23009 this.white.push(tag);
23013 Roo.each(w, function(tag) {
23014 if (b.indexOf(tag) > -1) {
23017 if (this.white.indexOf(tag) > -1) {
23020 this.white.push(tag);
23025 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23026 if (w.indexOf(tag) > -1) {
23029 this.black.push(tag);
23033 Roo.each(b, function(tag) {
23034 if (w.indexOf(tag) > -1) {
23037 if (this.black.indexOf(tag) > -1) {
23040 this.black.push(tag);
23045 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23046 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23050 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23051 if (b.indexOf(tag) > -1) {
23054 this.cwhite.push(tag);
23058 Roo.each(w, function(tag) {
23059 if (b.indexOf(tag) > -1) {
23062 if (this.cwhite.indexOf(tag) > -1) {
23065 this.cwhite.push(tag);
23070 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23071 if (w.indexOf(tag) > -1) {
23074 this.cblack.push(tag);
23078 Roo.each(b, function(tag) {
23079 if (w.indexOf(tag) > -1) {
23082 if (this.cblack.indexOf(tag) > -1) {
23085 this.cblack.push(tag);
23090 setStylesheets : function(stylesheets)
23092 if(typeof(stylesheets) == 'string'){
23093 Roo.get(this.iframe.contentDocument.head).createChild({
23095 rel : 'stylesheet',
23104 Roo.each(stylesheets, function(s) {
23109 Roo.get(_this.iframe.contentDocument.head).createChild({
23111 rel : 'stylesheet',
23120 removeStylesheets : function()
23124 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23129 setStyle : function(style)
23131 Roo.get(this.iframe.contentDocument.head).createChild({
23140 // hide stuff that is not compatible
23154 * @event specialkey
23158 * @cfg {String} fieldClass @hide
23161 * @cfg {String} focusClass @hide
23164 * @cfg {String} autoCreate @hide
23167 * @cfg {String} inputType @hide
23170 * @cfg {String} invalidClass @hide
23173 * @cfg {String} invalidText @hide
23176 * @cfg {String} msgFx @hide
23179 * @cfg {String} validateOnBlur @hide
23183 Roo.HtmlEditorCore.white = [
23184 'area', 'br', 'img', 'input', 'hr', 'wbr',
23186 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23187 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23188 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23189 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23190 'table', 'ul', 'xmp',
23192 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23195 'dir', 'menu', 'ol', 'ul', 'dl',
23201 Roo.HtmlEditorCore.black = [
23202 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23204 'base', 'basefont', 'bgsound', 'blink', 'body',
23205 'frame', 'frameset', 'head', 'html', 'ilayer',
23206 'iframe', 'layer', 'link', 'meta', 'object',
23207 'script', 'style' ,'title', 'xml' // clean later..
23209 Roo.HtmlEditorCore.clean = [
23210 'script', 'style', 'title', 'xml'
23212 Roo.HtmlEditorCore.remove = [
23217 Roo.HtmlEditorCore.ablack = [
23221 Roo.HtmlEditorCore.aclean = [
23222 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23226 Roo.HtmlEditorCore.pwhite= [
23227 'http', 'https', 'mailto'
23230 // white listed style attributes.
23231 Roo.HtmlEditorCore.cwhite= [
23232 // 'text-align', /// default is to allow most things..
23238 // black listed style attributes.
23239 Roo.HtmlEditorCore.cblack= [
23240 // 'font-size' -- this can be set by the project
23244 Roo.HtmlEditorCore.swapCodes =[
23263 * @class Roo.bootstrap.HtmlEditor
23264 * @extends Roo.bootstrap.TextArea
23265 * Bootstrap HtmlEditor class
23268 * Create a new HtmlEditor
23269 * @param {Object} config The config object
23272 Roo.bootstrap.HtmlEditor = function(config){
23273 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23274 if (!this.toolbars) {
23275 this.toolbars = [];
23278 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23281 * @event initialize
23282 * Fires when the editor is fully initialized (including the iframe)
23283 * @param {HtmlEditor} this
23288 * Fires when the editor is first receives the focus. Any insertion must wait
23289 * until after this event.
23290 * @param {HtmlEditor} this
23294 * @event beforesync
23295 * Fires before the textarea is updated with content from the editor iframe. Return false
23296 * to cancel the sync.
23297 * @param {HtmlEditor} this
23298 * @param {String} html
23302 * @event beforepush
23303 * Fires before the iframe editor is updated with content from the textarea. Return false
23304 * to cancel the push.
23305 * @param {HtmlEditor} this
23306 * @param {String} html
23311 * Fires when the textarea is updated with content from the editor iframe.
23312 * @param {HtmlEditor} this
23313 * @param {String} html
23318 * Fires when the iframe editor is updated with content from the textarea.
23319 * @param {HtmlEditor} this
23320 * @param {String} html
23324 * @event editmodechange
23325 * Fires when the editor switches edit modes
23326 * @param {HtmlEditor} this
23327 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23329 editmodechange: true,
23331 * @event editorevent
23332 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23333 * @param {HtmlEditor} this
23337 * @event firstfocus
23338 * Fires when on first focus - needed by toolbars..
23339 * @param {HtmlEditor} this
23344 * Auto save the htmlEditor value as a file into Events
23345 * @param {HtmlEditor} this
23349 * @event savedpreview
23350 * preview the saved version of htmlEditor
23351 * @param {HtmlEditor} this
23358 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23362 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23367 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23372 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23377 * @cfg {Number} height (in pixels)
23381 * @cfg {Number} width (in pixels)
23386 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23389 stylesheets: false,
23394 // private properties
23395 validationEvent : false,
23397 initialized : false,
23400 onFocus : Roo.emptyFn,
23402 hideMode:'offsets',
23404 tbContainer : false,
23408 toolbarContainer :function() {
23409 return this.wrap.select('.x-html-editor-tb',true).first();
23413 * Protected method that will not generally be called directly. It
23414 * is called when the editor creates its toolbar. Override this method if you need to
23415 * add custom toolbar buttons.
23416 * @param {HtmlEditor} editor
23418 createToolbar : function(){
23419 Roo.log('renewing');
23420 Roo.log("create toolbars");
23422 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23423 this.toolbars[0].render(this.toolbarContainer());
23427 // if (!editor.toolbars || !editor.toolbars.length) {
23428 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23431 // for (var i =0 ; i < editor.toolbars.length;i++) {
23432 // editor.toolbars[i] = Roo.factory(
23433 // typeof(editor.toolbars[i]) == 'string' ?
23434 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23435 // Roo.bootstrap.HtmlEditor);
23436 // editor.toolbars[i].init(editor);
23442 onRender : function(ct, position)
23444 // Roo.log("Call onRender: " + this.xtype);
23446 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23448 this.wrap = this.inputEl().wrap({
23449 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23452 this.editorcore.onRender(ct, position);
23454 if (this.resizable) {
23455 this.resizeEl = new Roo.Resizable(this.wrap, {
23459 minHeight : this.height,
23460 height: this.height,
23461 handles : this.resizable,
23464 resize : function(r, w, h) {
23465 _t.onResize(w,h); // -something
23471 this.createToolbar(this);
23474 if(!this.width && this.resizable){
23475 this.setSize(this.wrap.getSize());
23477 if (this.resizeEl) {
23478 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23479 // should trigger onReize..
23485 onResize : function(w, h)
23487 Roo.log('resize: ' +w + ',' + h );
23488 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23492 if(this.inputEl() ){
23493 if(typeof w == 'number'){
23494 var aw = w - this.wrap.getFrameWidth('lr');
23495 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23498 if(typeof h == 'number'){
23499 var tbh = -11; // fixme it needs to tool bar size!
23500 for (var i =0; i < this.toolbars.length;i++) {
23501 // fixme - ask toolbars for heights?
23502 tbh += this.toolbars[i].el.getHeight();
23503 //if (this.toolbars[i].footer) {
23504 // tbh += this.toolbars[i].footer.el.getHeight();
23512 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23513 ah -= 5; // knock a few pixes off for look..
23514 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23518 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23519 this.editorcore.onResize(ew,eh);
23524 * Toggles the editor between standard and source edit mode.
23525 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23527 toggleSourceEdit : function(sourceEditMode)
23529 this.editorcore.toggleSourceEdit(sourceEditMode);
23531 if(this.editorcore.sourceEditMode){
23532 Roo.log('editor - showing textarea');
23535 // Roo.log(this.syncValue());
23537 this.inputEl().removeClass(['hide', 'x-hidden']);
23538 this.inputEl().dom.removeAttribute('tabIndex');
23539 this.inputEl().focus();
23541 Roo.log('editor - hiding textarea');
23543 // Roo.log(this.pushValue());
23546 this.inputEl().addClass(['hide', 'x-hidden']);
23547 this.inputEl().dom.setAttribute('tabIndex', -1);
23548 //this.deferFocus();
23551 if(this.resizable){
23552 this.setSize(this.wrap.getSize());
23555 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23558 // private (for BoxComponent)
23559 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23561 // private (for BoxComponent)
23562 getResizeEl : function(){
23566 // private (for BoxComponent)
23567 getPositionEl : function(){
23572 initEvents : function(){
23573 this.originalValue = this.getValue();
23577 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23580 // markInvalid : Roo.emptyFn,
23582 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23585 // clearInvalid : Roo.emptyFn,
23587 setValue : function(v){
23588 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23589 this.editorcore.pushValue();
23594 deferFocus : function(){
23595 this.focus.defer(10, this);
23599 focus : function(){
23600 this.editorcore.focus();
23606 onDestroy : function(){
23612 for (var i =0; i < this.toolbars.length;i++) {
23613 // fixme - ask toolbars for heights?
23614 this.toolbars[i].onDestroy();
23617 this.wrap.dom.innerHTML = '';
23618 this.wrap.remove();
23623 onFirstFocus : function(){
23624 //Roo.log("onFirstFocus");
23625 this.editorcore.onFirstFocus();
23626 for (var i =0; i < this.toolbars.length;i++) {
23627 this.toolbars[i].onFirstFocus();
23633 syncValue : function()
23635 this.editorcore.syncValue();
23638 pushValue : function()
23640 this.editorcore.pushValue();
23644 // hide stuff that is not compatible
23658 * @event specialkey
23662 * @cfg {String} fieldClass @hide
23665 * @cfg {String} focusClass @hide
23668 * @cfg {String} autoCreate @hide
23671 * @cfg {String} inputType @hide
23674 * @cfg {String} invalidClass @hide
23677 * @cfg {String} invalidText @hide
23680 * @cfg {String} msgFx @hide
23683 * @cfg {String} validateOnBlur @hide
23692 Roo.namespace('Roo.bootstrap.htmleditor');
23694 * @class Roo.bootstrap.HtmlEditorToolbar1
23699 new Roo.bootstrap.HtmlEditor({
23702 new Roo.bootstrap.HtmlEditorToolbar1({
23703 disable : { fonts: 1 , format: 1, ..., ... , ...],
23709 * @cfg {Object} disable List of elements to disable..
23710 * @cfg {Array} btns List of additional buttons.
23714 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23717 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23720 Roo.apply(this, config);
23722 // default disabled, based on 'good practice'..
23723 this.disable = this.disable || {};
23724 Roo.applyIf(this.disable, {
23727 specialElements : true
23729 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23731 this.editor = config.editor;
23732 this.editorcore = config.editor.editorcore;
23734 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23736 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23737 // dont call parent... till later.
23739 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23744 editorcore : false,
23749 "h1","h2","h3","h4","h5","h6",
23751 "abbr", "acronym", "address", "cite", "samp", "var",
23755 onRender : function(ct, position)
23757 // Roo.log("Call onRender: " + this.xtype);
23759 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23761 this.el.dom.style.marginBottom = '0';
23763 var editorcore = this.editorcore;
23764 var editor= this.editor;
23767 var btn = function(id,cmd , toggle, handler, html){
23769 var event = toggle ? 'toggle' : 'click';
23774 xns: Roo.bootstrap,
23777 enableToggle:toggle !== false,
23779 pressed : toggle ? false : null,
23782 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23783 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23789 // var cb_box = function...
23794 xns: Roo.bootstrap,
23795 glyphicon : 'font',
23799 xns: Roo.bootstrap,
23803 Roo.each(this.formats, function(f) {
23804 style.menu.items.push({
23806 xns: Roo.bootstrap,
23807 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23812 editorcore.insertTag(this.tagname);
23819 children.push(style);
23821 btn('bold',false,true);
23822 btn('italic',false,true);
23823 btn('align-left', 'justifyleft',true);
23824 btn('align-center', 'justifycenter',true);
23825 btn('align-right' , 'justifyright',true);
23826 btn('link', false, false, function(btn) {
23827 //Roo.log("create link?");
23828 var url = prompt(this.createLinkText, this.defaultLinkValue);
23829 if(url && url != 'http:/'+'/'){
23830 this.editorcore.relayCmd('createlink', url);
23833 btn('list','insertunorderedlist',true);
23834 btn('pencil', false,true, function(btn){
23836 this.toggleSourceEdit(btn.pressed);
23839 if (this.editor.btns.length > 0) {
23840 for (var i = 0; i<this.editor.btns.length; i++) {
23841 children.push(this.editor.btns[i]);
23849 xns: Roo.bootstrap,
23854 xns: Roo.bootstrap,
23859 cog.menu.items.push({
23861 xns: Roo.bootstrap,
23862 html : Clean styles,
23867 editorcore.insertTag(this.tagname);
23876 this.xtype = 'NavSimplebar';
23878 for(var i=0;i< children.length;i++) {
23880 this.buttons.add(this.addxtypeChild(children[i]));
23884 editor.on('editorevent', this.updateToolbar, this);
23886 onBtnClick : function(id)
23888 this.editorcore.relayCmd(id);
23889 this.editorcore.focus();
23893 * Protected method that will not generally be called directly. It triggers
23894 * a toolbar update by reading the markup state of the current selection in the editor.
23896 updateToolbar: function(){
23898 if(!this.editorcore.activated){
23899 this.editor.onFirstFocus(); // is this neeed?
23903 var btns = this.buttons;
23904 var doc = this.editorcore.doc;
23905 btns.get('bold').setActive(doc.queryCommandState('bold'));
23906 btns.get('italic').setActive(doc.queryCommandState('italic'));
23907 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23909 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23910 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23911 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23913 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23914 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23917 var ans = this.editorcore.getAllAncestors();
23918 if (this.formatCombo) {
23921 var store = this.formatCombo.store;
23922 this.formatCombo.setValue("");
23923 for (var i =0; i < ans.length;i++) {
23924 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23926 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23934 // hides menus... - so this cant be on a menu...
23935 Roo.bootstrap.MenuMgr.hideAll();
23937 Roo.bootstrap.MenuMgr.hideAll();
23938 //this.editorsyncValue();
23940 onFirstFocus: function() {
23941 this.buttons.each(function(item){
23945 toggleSourceEdit : function(sourceEditMode){
23948 if(sourceEditMode){
23949 Roo.log("disabling buttons");
23950 this.buttons.each( function(item){
23951 if(item.cmd != 'pencil'){
23957 Roo.log("enabling buttons");
23958 if(this.editorcore.initialized){
23959 this.buttons.each( function(item){
23965 Roo.log("calling toggole on editor");
23966 // tell the editor that it's been pressed..
23967 this.editor.toggleSourceEdit(sourceEditMode);
23977 * @class Roo.bootstrap.Table.AbstractSelectionModel
23978 * @extends Roo.util.Observable
23979 * Abstract base class for grid SelectionModels. It provides the interface that should be
23980 * implemented by descendant classes. This class should not be directly instantiated.
23983 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23984 this.locked = false;
23985 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23989 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23990 /** @ignore Called by the grid automatically. Do not call directly. */
23991 init : function(grid){
23997 * Locks the selections.
24000 this.locked = true;
24004 * Unlocks the selections.
24006 unlock : function(){
24007 this.locked = false;
24011 * Returns true if the selections are locked.
24012 * @return {Boolean}
24014 isLocked : function(){
24015 return this.locked;
24019 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24020 * @class Roo.bootstrap.Table.RowSelectionModel
24021 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24022 * It supports multiple selections and keyboard selection/navigation.
24024 * @param {Object} config
24027 Roo.bootstrap.Table.RowSelectionModel = function(config){
24028 Roo.apply(this, config);
24029 this.selections = new Roo.util.MixedCollection(false, function(o){
24034 this.lastActive = false;
24038 * @event selectionchange
24039 * Fires when the selection changes
24040 * @param {SelectionModel} this
24042 "selectionchange" : true,
24044 * @event afterselectionchange
24045 * Fires after the selection changes (eg. by key press or clicking)
24046 * @param {SelectionModel} this
24048 "afterselectionchange" : true,
24050 * @event beforerowselect
24051 * Fires when a row is selected being selected, return false to cancel.
24052 * @param {SelectionModel} this
24053 * @param {Number} rowIndex The selected index
24054 * @param {Boolean} keepExisting False if other selections will be cleared
24056 "beforerowselect" : true,
24059 * Fires when a row is selected.
24060 * @param {SelectionModel} this
24061 * @param {Number} rowIndex The selected index
24062 * @param {Roo.data.Record} r The record
24064 "rowselect" : true,
24066 * @event rowdeselect
24067 * Fires when a row is deselected.
24068 * @param {SelectionModel} this
24069 * @param {Number} rowIndex The selected index
24071 "rowdeselect" : true
24073 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24074 this.locked = false;
24077 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24079 * @cfg {Boolean} singleSelect
24080 * True to allow selection of only one row at a time (defaults to false)
24082 singleSelect : false,
24085 initEvents : function()
24088 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24089 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24090 //}else{ // allow click to work like normal
24091 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24093 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24094 this.grid.on("rowclick", this.handleMouseDown, this);
24096 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24097 "up" : function(e){
24099 this.selectPrevious(e.shiftKey);
24100 }else if(this.last !== false && this.lastActive !== false){
24101 var last = this.last;
24102 this.selectRange(this.last, this.lastActive-1);
24103 this.grid.getView().focusRow(this.lastActive);
24104 if(last !== false){
24108 this.selectFirstRow();
24110 this.fireEvent("afterselectionchange", this);
24112 "down" : function(e){
24114 this.selectNext(e.shiftKey);
24115 }else if(this.last !== false && this.lastActive !== false){
24116 var last = this.last;
24117 this.selectRange(this.last, this.lastActive+1);
24118 this.grid.getView().focusRow(this.lastActive);
24119 if(last !== false){
24123 this.selectFirstRow();
24125 this.fireEvent("afterselectionchange", this);
24129 this.grid.store.on('load', function(){
24130 this.selections.clear();
24133 var view = this.grid.view;
24134 view.on("refresh", this.onRefresh, this);
24135 view.on("rowupdated", this.onRowUpdated, this);
24136 view.on("rowremoved", this.onRemove, this);
24141 onRefresh : function()
24143 var ds = this.grid.store, i, v = this.grid.view;
24144 var s = this.selections;
24145 s.each(function(r){
24146 if((i = ds.indexOfId(r.id)) != -1){
24155 onRemove : function(v, index, r){
24156 this.selections.remove(r);
24160 onRowUpdated : function(v, index, r){
24161 if(this.isSelected(r)){
24162 v.onRowSelect(index);
24168 * @param {Array} records The records to select
24169 * @param {Boolean} keepExisting (optional) True to keep existing selections
24171 selectRecords : function(records, keepExisting)
24174 this.clearSelections();
24176 var ds = this.grid.store;
24177 for(var i = 0, len = records.length; i < len; i++){
24178 this.selectRow(ds.indexOf(records[i]), true);
24183 * Gets the number of selected rows.
24186 getCount : function(){
24187 return this.selections.length;
24191 * Selects the first row in the grid.
24193 selectFirstRow : function(){
24198 * Select the last row.
24199 * @param {Boolean} keepExisting (optional) True to keep existing selections
24201 selectLastRow : function(keepExisting){
24202 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24203 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24207 * Selects the row immediately following the last selected row.
24208 * @param {Boolean} keepExisting (optional) True to keep existing selections
24210 selectNext : function(keepExisting)
24212 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24213 this.selectRow(this.last+1, keepExisting);
24214 this.grid.getView().focusRow(this.last);
24219 * Selects the row that precedes the last selected row.
24220 * @param {Boolean} keepExisting (optional) True to keep existing selections
24222 selectPrevious : function(keepExisting){
24224 this.selectRow(this.last-1, keepExisting);
24225 this.grid.getView().focusRow(this.last);
24230 * Returns the selected records
24231 * @return {Array} Array of selected records
24233 getSelections : function(){
24234 return [].concat(this.selections.items);
24238 * Returns the first selected record.
24241 getSelected : function(){
24242 return this.selections.itemAt(0);
24247 * Clears all selections.
24249 clearSelections : function(fast)
24255 var ds = this.grid.store;
24256 var s = this.selections;
24257 s.each(function(r){
24258 this.deselectRow(ds.indexOfId(r.id));
24262 this.selections.clear();
24269 * Selects all rows.
24271 selectAll : function(){
24275 this.selections.clear();
24276 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24277 this.selectRow(i, true);
24282 * Returns True if there is a selection.
24283 * @return {Boolean}
24285 hasSelection : function(){
24286 return this.selections.length > 0;
24290 * Returns True if the specified row is selected.
24291 * @param {Number/Record} record The record or index of the record to check
24292 * @return {Boolean}
24294 isSelected : function(index){
24295 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24296 return (r && this.selections.key(r.id) ? true : false);
24300 * Returns True if the specified record id is selected.
24301 * @param {String} id The id of record to check
24302 * @return {Boolean}
24304 isIdSelected : function(id){
24305 return (this.selections.key(id) ? true : false);
24310 handleMouseDBClick : function(e, t){
24314 handleMouseDown : function(e, t)
24316 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24317 if(this.isLocked() || rowIndex < 0 ){
24320 if(e.shiftKey && this.last !== false){
24321 var last = this.last;
24322 this.selectRange(last, rowIndex, e.ctrlKey);
24323 this.last = last; // reset the last
24327 var isSelected = this.isSelected(rowIndex);
24328 //Roo.log("select row:" + rowIndex);
24330 this.deselectRow(rowIndex);
24332 this.selectRow(rowIndex, true);
24336 if(e.button !== 0 && isSelected){
24337 alert('rowIndex 2: ' + rowIndex);
24338 view.focusRow(rowIndex);
24339 }else if(e.ctrlKey && isSelected){
24340 this.deselectRow(rowIndex);
24341 }else if(!isSelected){
24342 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24343 view.focusRow(rowIndex);
24347 this.fireEvent("afterselectionchange", this);
24350 handleDragableRowClick : function(grid, rowIndex, e)
24352 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24353 this.selectRow(rowIndex, false);
24354 grid.view.focusRow(rowIndex);
24355 this.fireEvent("afterselectionchange", this);
24360 * Selects multiple rows.
24361 * @param {Array} rows Array of the indexes of the row to select
24362 * @param {Boolean} keepExisting (optional) True to keep existing selections
24364 selectRows : function(rows, keepExisting){
24366 this.clearSelections();
24368 for(var i = 0, len = rows.length; i < len; i++){
24369 this.selectRow(rows[i], true);
24374 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24375 * @param {Number} startRow The index of the first row in the range
24376 * @param {Number} endRow The index of the last row in the range
24377 * @param {Boolean} keepExisting (optional) True to retain existing selections
24379 selectRange : function(startRow, endRow, keepExisting){
24384 this.clearSelections();
24386 if(startRow <= endRow){
24387 for(var i = startRow; i <= endRow; i++){
24388 this.selectRow(i, true);
24391 for(var i = startRow; i >= endRow; i--){
24392 this.selectRow(i, true);
24398 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24399 * @param {Number} startRow The index of the first row in the range
24400 * @param {Number} endRow The index of the last row in the range
24402 deselectRange : function(startRow, endRow, preventViewNotify){
24406 for(var i = startRow; i <= endRow; i++){
24407 this.deselectRow(i, preventViewNotify);
24413 * @param {Number} row The index of the row to select
24414 * @param {Boolean} keepExisting (optional) True to keep existing selections
24416 selectRow : function(index, keepExisting, preventViewNotify)
24418 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24421 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24422 if(!keepExisting || this.singleSelect){
24423 this.clearSelections();
24426 var r = this.grid.store.getAt(index);
24427 //console.log('selectRow - record id :' + r.id);
24429 this.selections.add(r);
24430 this.last = this.lastActive = index;
24431 if(!preventViewNotify){
24432 var proxy = new Roo.Element(
24433 this.grid.getRowDom(index)
24435 proxy.addClass('bg-info info');
24437 this.fireEvent("rowselect", this, index, r);
24438 this.fireEvent("selectionchange", this);
24444 * @param {Number} row The index of the row to deselect
24446 deselectRow : function(index, preventViewNotify)
24451 if(this.last == index){
24454 if(this.lastActive == index){
24455 this.lastActive = false;
24458 var r = this.grid.store.getAt(index);
24463 this.selections.remove(r);
24464 //.console.log('deselectRow - record id :' + r.id);
24465 if(!preventViewNotify){
24467 var proxy = new Roo.Element(
24468 this.grid.getRowDom(index)
24470 proxy.removeClass('bg-info info');
24472 this.fireEvent("rowdeselect", this, index);
24473 this.fireEvent("selectionchange", this);
24477 restoreLast : function(){
24479 this.last = this._last;
24484 acceptsNav : function(row, col, cm){
24485 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24489 onEditorKey : function(field, e){
24490 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24495 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24497 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24499 }else if(k == e.ENTER && !e.ctrlKey){
24503 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24505 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24507 }else if(k == e.ESC){
24511 g.startEditing(newCell[0], newCell[1]);
24517 * Ext JS Library 1.1.1
24518 * Copyright(c) 2006-2007, Ext JS, LLC.
24520 * Originally Released Under LGPL - original licence link has changed is not relivant.
24523 * <script type="text/javascript">
24527 * @class Roo.bootstrap.PagingToolbar
24528 * @extends Roo.bootstrap.NavSimplebar
24529 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24531 * Create a new PagingToolbar
24532 * @param {Object} config The config object
24533 * @param {Roo.data.Store} store
24535 Roo.bootstrap.PagingToolbar = function(config)
24537 // old args format still supported... - xtype is prefered..
24538 // created from xtype...
24540 this.ds = config.dataSource;
24542 if (config.store && !this.ds) {
24543 this.store= Roo.factory(config.store, Roo.data);
24544 this.ds = this.store;
24545 this.ds.xmodule = this.xmodule || false;
24548 this.toolbarItems = [];
24549 if (config.items) {
24550 this.toolbarItems = config.items;
24553 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24558 this.bind(this.ds);
24561 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24565 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24567 * @cfg {Roo.data.Store} dataSource
24568 * The underlying data store providing the paged data
24571 * @cfg {String/HTMLElement/Element} container
24572 * container The id or element that will contain the toolbar
24575 * @cfg {Boolean} displayInfo
24576 * True to display the displayMsg (defaults to false)
24579 * @cfg {Number} pageSize
24580 * The number of records to display per page (defaults to 20)
24584 * @cfg {String} displayMsg
24585 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24587 displayMsg : 'Displaying {0} - {1} of {2}',
24589 * @cfg {String} emptyMsg
24590 * The message to display when no records are found (defaults to "No data to display")
24592 emptyMsg : 'No data to display',
24594 * Customizable piece of the default paging text (defaults to "Page")
24597 beforePageText : "Page",
24599 * Customizable piece of the default paging text (defaults to "of %0")
24602 afterPageText : "of {0}",
24604 * Customizable piece of the default paging text (defaults to "First Page")
24607 firstText : "First Page",
24609 * Customizable piece of the default paging text (defaults to "Previous Page")
24612 prevText : "Previous Page",
24614 * Customizable piece of the default paging text (defaults to "Next Page")
24617 nextText : "Next Page",
24619 * Customizable piece of the default paging text (defaults to "Last Page")
24622 lastText : "Last Page",
24624 * Customizable piece of the default paging text (defaults to "Refresh")
24627 refreshText : "Refresh",
24631 onRender : function(ct, position)
24633 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24634 this.navgroup.parentId = this.id;
24635 this.navgroup.onRender(this.el, null);
24636 // add the buttons to the navgroup
24638 if(this.displayInfo){
24639 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24640 this.displayEl = this.el.select('.x-paging-info', true).first();
24641 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24642 // this.displayEl = navel.el.select('span',true).first();
24648 Roo.each(_this.buttons, function(e){ // this might need to use render????
24649 Roo.factory(e).render(_this.el);
24653 Roo.each(_this.toolbarItems, function(e) {
24654 _this.navgroup.addItem(e);
24658 this.first = this.navgroup.addItem({
24659 tooltip: this.firstText,
24661 icon : 'fa fa-backward',
24663 preventDefault: true,
24664 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24667 this.prev = this.navgroup.addItem({
24668 tooltip: this.prevText,
24670 icon : 'fa fa-step-backward',
24672 preventDefault: true,
24673 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24675 //this.addSeparator();
24678 var field = this.navgroup.addItem( {
24680 cls : 'x-paging-position',
24682 html : this.beforePageText +
24683 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24684 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24687 this.field = field.el.select('input', true).first();
24688 this.field.on("keydown", this.onPagingKeydown, this);
24689 this.field.on("focus", function(){this.dom.select();});
24692 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24693 //this.field.setHeight(18);
24694 //this.addSeparator();
24695 this.next = this.navgroup.addItem({
24696 tooltip: this.nextText,
24698 html : ' <i class="fa fa-step-forward">',
24700 preventDefault: true,
24701 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24703 this.last = this.navgroup.addItem({
24704 tooltip: this.lastText,
24705 icon : 'fa fa-forward',
24708 preventDefault: true,
24709 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24711 //this.addSeparator();
24712 this.loading = this.navgroup.addItem({
24713 tooltip: this.refreshText,
24714 icon: 'fa fa-refresh',
24715 preventDefault: true,
24716 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24722 updateInfo : function(){
24723 if(this.displayEl){
24724 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24725 var msg = count == 0 ?
24729 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24731 this.displayEl.update(msg);
24736 onLoad : function(ds, r, o)
24738 this.cursor = o.params.start ? o.params.start : 0;
24740 var d = this.getPageData(),
24745 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24746 this.field.dom.value = ap;
24747 this.first.setDisabled(ap == 1);
24748 this.prev.setDisabled(ap == 1);
24749 this.next.setDisabled(ap == ps);
24750 this.last.setDisabled(ap == ps);
24751 this.loading.enable();
24756 getPageData : function(){
24757 var total = this.ds.getTotalCount();
24760 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24761 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24766 onLoadError : function(){
24767 this.loading.enable();
24771 onPagingKeydown : function(e){
24772 var k = e.getKey();
24773 var d = this.getPageData();
24775 var v = this.field.dom.value, pageNum;
24776 if(!v || isNaN(pageNum = parseInt(v, 10))){
24777 this.field.dom.value = d.activePage;
24780 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24781 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24784 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))
24786 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24787 this.field.dom.value = pageNum;
24788 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24791 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24793 var v = this.field.dom.value, pageNum;
24794 var increment = (e.shiftKey) ? 10 : 1;
24795 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24798 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24799 this.field.dom.value = d.activePage;
24802 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24804 this.field.dom.value = parseInt(v, 10) + increment;
24805 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24806 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24813 beforeLoad : function(){
24815 this.loading.disable();
24820 onClick : function(which){
24829 ds.load({params:{start: 0, limit: this.pageSize}});
24832 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24835 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24838 var total = ds.getTotalCount();
24839 var extra = total % this.pageSize;
24840 var lastStart = extra ? (total - extra) : total-this.pageSize;
24841 ds.load({params:{start: lastStart, limit: this.pageSize}});
24844 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24850 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24851 * @param {Roo.data.Store} store The data store to unbind
24853 unbind : function(ds){
24854 ds.un("beforeload", this.beforeLoad, this);
24855 ds.un("load", this.onLoad, this);
24856 ds.un("loadexception", this.onLoadError, this);
24857 ds.un("remove", this.updateInfo, this);
24858 ds.un("add", this.updateInfo, this);
24859 this.ds = undefined;
24863 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24864 * @param {Roo.data.Store} store The data store to bind
24866 bind : function(ds){
24867 ds.on("beforeload", this.beforeLoad, this);
24868 ds.on("load", this.onLoad, this);
24869 ds.on("loadexception", this.onLoadError, this);
24870 ds.on("remove", this.updateInfo, this);
24871 ds.on("add", this.updateInfo, this);
24882 * @class Roo.bootstrap.MessageBar
24883 * @extends Roo.bootstrap.Component
24884 * Bootstrap MessageBar class
24885 * @cfg {String} html contents of the MessageBar
24886 * @cfg {String} weight (info | success | warning | danger) default info
24887 * @cfg {String} beforeClass insert the bar before the given class
24888 * @cfg {Boolean} closable (true | false) default false
24889 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24892 * Create a new Element
24893 * @param {Object} config The config object
24896 Roo.bootstrap.MessageBar = function(config){
24897 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24900 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24906 beforeClass: 'bootstrap-sticky-wrap',
24908 getAutoCreate : function(){
24912 cls: 'alert alert-dismissable alert-' + this.weight,
24917 html: this.html || ''
24923 cfg.cls += ' alert-messages-fixed';
24937 onRender : function(ct, position)
24939 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24942 var cfg = Roo.apply({}, this.getAutoCreate());
24946 cfg.cls += ' ' + this.cls;
24949 cfg.style = this.style;
24951 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24953 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24956 this.el.select('>button.close').on('click', this.hide, this);
24962 if (!this.rendered) {
24968 this.fireEvent('show', this);
24974 if (!this.rendered) {
24980 this.fireEvent('hide', this);
24983 update : function()
24985 // var e = this.el.dom.firstChild;
24987 // if(this.closable){
24988 // e = e.nextSibling;
24991 // e.data = this.html || '';
24993 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25009 * @class Roo.bootstrap.Graph
25010 * @extends Roo.bootstrap.Component
25011 * Bootstrap Graph class
25015 @cfg {String} graphtype bar | vbar | pie
25016 @cfg {number} g_x coodinator | centre x (pie)
25017 @cfg {number} g_y coodinator | centre y (pie)
25018 @cfg {number} g_r radius (pie)
25019 @cfg {number} g_height height of the chart (respected by all elements in the set)
25020 @cfg {number} g_width width of the chart (respected by all elements in the set)
25021 @cfg {Object} title The title of the chart
25024 -opts (object) options for the chart
25026 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25027 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25029 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.
25030 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25032 o stretch (boolean)
25034 -opts (object) options for the pie
25037 o startAngle (number)
25038 o endAngle (number)
25042 * Create a new Input
25043 * @param {Object} config The config object
25046 Roo.bootstrap.Graph = function(config){
25047 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25053 * The img click event for the img.
25054 * @param {Roo.EventObject} e
25060 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25071 //g_colors: this.colors,
25078 getAutoCreate : function(){
25089 onRender : function(ct,position){
25092 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25094 if (typeof(Raphael) == 'undefined') {
25095 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25099 this.raphael = Raphael(this.el.dom);
25101 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25102 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25103 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25104 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25106 r.text(160, 10, "Single Series Chart").attr(txtattr);
25107 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25108 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25109 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25111 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25112 r.barchart(330, 10, 300, 220, data1);
25113 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25114 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25117 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25118 // r.barchart(30, 30, 560, 250, xdata, {
25119 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25120 // axis : "0 0 1 1",
25121 // axisxlabels : xdata
25122 // //yvalues : cols,
25125 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25127 // this.load(null,xdata,{
25128 // axis : "0 0 1 1",
25129 // axisxlabels : xdata
25134 load : function(graphtype,xdata,opts)
25136 this.raphael.clear();
25138 graphtype = this.graphtype;
25143 var r = this.raphael,
25144 fin = function () {
25145 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25147 fout = function () {
25148 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25150 pfin = function() {
25151 this.sector.stop();
25152 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25155 this.label[0].stop();
25156 this.label[0].attr({ r: 7.5 });
25157 this.label[1].attr({ "font-weight": 800 });
25160 pfout = function() {
25161 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25164 this.label[0].animate({ r: 5 }, 500, "bounce");
25165 this.label[1].attr({ "font-weight": 400 });
25171 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25174 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25177 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25178 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25180 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25187 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25192 setTitle: function(o)
25197 initEvents: function() {
25200 this.el.on('click', this.onClick, this);
25204 onClick : function(e)
25206 Roo.log('img onclick');
25207 this.fireEvent('click', this, e);
25219 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25222 * @class Roo.bootstrap.dash.NumberBox
25223 * @extends Roo.bootstrap.Component
25224 * Bootstrap NumberBox class
25225 * @cfg {String} headline Box headline
25226 * @cfg {String} content Box content
25227 * @cfg {String} icon Box icon
25228 * @cfg {String} footer Footer text
25229 * @cfg {String} fhref Footer href
25232 * Create a new NumberBox
25233 * @param {Object} config The config object
25237 Roo.bootstrap.dash.NumberBox = function(config){
25238 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25242 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25251 getAutoCreate : function(){
25255 cls : 'small-box ',
25263 cls : 'roo-headline',
25264 html : this.headline
25268 cls : 'roo-content',
25269 html : this.content
25283 cls : 'ion ' + this.icon
25292 cls : 'small-box-footer',
25293 href : this.fhref || '#',
25297 cfg.cn.push(footer);
25304 onRender : function(ct,position){
25305 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25312 setHeadline: function (value)
25314 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25317 setFooter: function (value, href)
25319 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25322 this.el.select('a.small-box-footer',true).first().attr('href', href);
25327 setContent: function (value)
25329 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25332 initEvents: function()
25346 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25349 * @class Roo.bootstrap.dash.TabBox
25350 * @extends Roo.bootstrap.Component
25351 * Bootstrap TabBox class
25352 * @cfg {String} title Title of the TabBox
25353 * @cfg {String} icon Icon of the TabBox
25354 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25355 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25358 * Create a new TabBox
25359 * @param {Object} config The config object
25363 Roo.bootstrap.dash.TabBox = function(config){
25364 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25369 * When a pane is added
25370 * @param {Roo.bootstrap.dash.TabPane} pane
25374 * @event activatepane
25375 * When a pane is activated
25376 * @param {Roo.bootstrap.dash.TabPane} pane
25378 "activatepane" : true
25386 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25391 tabScrollable : false,
25393 getChildContainer : function()
25395 return this.el.select('.tab-content', true).first();
25398 getAutoCreate : function(){
25402 cls: 'pull-left header',
25410 cls: 'fa ' + this.icon
25416 cls: 'nav nav-tabs pull-right',
25422 if(this.tabScrollable){
25429 cls: 'nav nav-tabs pull-right',
25440 cls: 'nav-tabs-custom',
25445 cls: 'tab-content no-padding',
25453 initEvents : function()
25455 //Roo.log('add add pane handler');
25456 this.on('addpane', this.onAddPane, this);
25459 * Updates the box title
25460 * @param {String} html to set the title to.
25462 setTitle : function(value)
25464 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25466 onAddPane : function(pane)
25468 this.panes.push(pane);
25469 //Roo.log('addpane');
25471 // tabs are rendere left to right..
25472 if(!this.showtabs){
25476 var ctr = this.el.select('.nav-tabs', true).first();
25479 var existing = ctr.select('.nav-tab',true);
25480 var qty = existing.getCount();;
25483 var tab = ctr.createChild({
25485 cls : 'nav-tab' + (qty ? '' : ' active'),
25493 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25496 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25498 pane.el.addClass('active');
25503 onTabClick : function(ev,un,ob,pane)
25505 //Roo.log('tab - prev default');
25506 ev.preventDefault();
25509 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25510 pane.tab.addClass('active');
25511 //Roo.log(pane.title);
25512 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25513 // technically we should have a deactivate event.. but maybe add later.
25514 // and it should not de-activate the selected tab...
25515 this.fireEvent('activatepane', pane);
25516 pane.el.addClass('active');
25517 pane.fireEvent('activate');
25522 getActivePane : function()
25525 Roo.each(this.panes, function(p) {
25526 if(p.el.hasClass('active')){
25547 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25549 * @class Roo.bootstrap.TabPane
25550 * @extends Roo.bootstrap.Component
25551 * Bootstrap TabPane class
25552 * @cfg {Boolean} active (false | true) Default false
25553 * @cfg {String} title title of panel
25557 * Create a new TabPane
25558 * @param {Object} config The config object
25561 Roo.bootstrap.dash.TabPane = function(config){
25562 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25568 * When a pane is activated
25569 * @param {Roo.bootstrap.dash.TabPane} pane
25576 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25581 // the tabBox that this is attached to.
25584 getAutoCreate : function()
25592 cfg.cls += ' active';
25597 initEvents : function()
25599 //Roo.log('trigger add pane handler');
25600 this.parent().fireEvent('addpane', this)
25604 * Updates the tab title
25605 * @param {String} html to set the title to.
25607 setTitle: function(str)
25613 this.tab.select('a', true).first().dom.innerHTML = str;
25630 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25633 * @class Roo.bootstrap.menu.Menu
25634 * @extends Roo.bootstrap.Component
25635 * Bootstrap Menu class - container for Menu
25636 * @cfg {String} html Text of the menu
25637 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25638 * @cfg {String} icon Font awesome icon
25639 * @cfg {String} pos Menu align to (top | bottom) default bottom
25643 * Create a new Menu
25644 * @param {Object} config The config object
25648 Roo.bootstrap.menu.Menu = function(config){
25649 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25653 * @event beforeshow
25654 * Fires before this menu is displayed
25655 * @param {Roo.bootstrap.menu.Menu} this
25659 * @event beforehide
25660 * Fires before this menu is hidden
25661 * @param {Roo.bootstrap.menu.Menu} this
25666 * Fires after this menu is displayed
25667 * @param {Roo.bootstrap.menu.Menu} this
25672 * Fires after this menu is hidden
25673 * @param {Roo.bootstrap.menu.Menu} this
25678 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25679 * @param {Roo.bootstrap.menu.Menu} this
25680 * @param {Roo.EventObject} e
25687 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25691 weight : 'default',
25696 getChildContainer : function() {
25697 if(this.isSubMenu){
25701 return this.el.select('ul.dropdown-menu', true).first();
25704 getAutoCreate : function()
25709 cls : 'roo-menu-text',
25717 cls : 'fa ' + this.icon
25728 cls : 'dropdown-button btn btn-' + this.weight,
25733 cls : 'dropdown-toggle btn btn-' + this.weight,
25743 cls : 'dropdown-menu'
25749 if(this.pos == 'top'){
25750 cfg.cls += ' dropup';
25753 if(this.isSubMenu){
25756 cls : 'dropdown-menu'
25763 onRender : function(ct, position)
25765 this.isSubMenu = ct.hasClass('dropdown-submenu');
25767 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25770 initEvents : function()
25772 if(this.isSubMenu){
25776 this.hidden = true;
25778 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25779 this.triggerEl.on('click', this.onTriggerPress, this);
25781 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25782 this.buttonEl.on('click', this.onClick, this);
25788 if(this.isSubMenu){
25792 return this.el.select('ul.dropdown-menu', true).first();
25795 onClick : function(e)
25797 this.fireEvent("click", this, e);
25800 onTriggerPress : function(e)
25802 if (this.isVisible()) {
25809 isVisible : function(){
25810 return !this.hidden;
25815 this.fireEvent("beforeshow", this);
25817 this.hidden = false;
25818 this.el.addClass('open');
25820 Roo.get(document).on("mouseup", this.onMouseUp, this);
25822 this.fireEvent("show", this);
25829 this.fireEvent("beforehide", this);
25831 this.hidden = true;
25832 this.el.removeClass('open');
25834 Roo.get(document).un("mouseup", this.onMouseUp);
25836 this.fireEvent("hide", this);
25839 onMouseUp : function()
25853 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25856 * @class Roo.bootstrap.menu.Item
25857 * @extends Roo.bootstrap.Component
25858 * Bootstrap MenuItem class
25859 * @cfg {Boolean} submenu (true | false) default false
25860 * @cfg {String} html text of the item
25861 * @cfg {String} href the link
25862 * @cfg {Boolean} disable (true | false) default false
25863 * @cfg {Boolean} preventDefault (true | false) default true
25864 * @cfg {String} icon Font awesome icon
25865 * @cfg {String} pos Submenu align to (left | right) default right
25869 * Create a new Item
25870 * @param {Object} config The config object
25874 Roo.bootstrap.menu.Item = function(config){
25875 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25879 * Fires when the mouse is hovering over this menu
25880 * @param {Roo.bootstrap.menu.Item} this
25881 * @param {Roo.EventObject} e
25886 * Fires when the mouse exits this menu
25887 * @param {Roo.bootstrap.menu.Item} this
25888 * @param {Roo.EventObject} e
25894 * The raw click event for the entire grid.
25895 * @param {Roo.EventObject} e
25901 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25906 preventDefault: true,
25911 getAutoCreate : function()
25916 cls : 'roo-menu-item-text',
25924 cls : 'fa ' + this.icon
25933 href : this.href || '#',
25940 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25944 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25946 if(this.pos == 'left'){
25947 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25954 initEvents : function()
25956 this.el.on('mouseover', this.onMouseOver, this);
25957 this.el.on('mouseout', this.onMouseOut, this);
25959 this.el.select('a', true).first().on('click', this.onClick, this);
25963 onClick : function(e)
25965 if(this.preventDefault){
25966 e.preventDefault();
25969 this.fireEvent("click", this, e);
25972 onMouseOver : function(e)
25974 if(this.submenu && this.pos == 'left'){
25975 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25978 this.fireEvent("mouseover", this, e);
25981 onMouseOut : function(e)
25983 this.fireEvent("mouseout", this, e);
25995 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25998 * @class Roo.bootstrap.menu.Separator
25999 * @extends Roo.bootstrap.Component
26000 * Bootstrap Separator class
26003 * Create a new Separator
26004 * @param {Object} config The config object
26008 Roo.bootstrap.menu.Separator = function(config){
26009 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26012 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26014 getAutoCreate : function(){
26035 * @class Roo.bootstrap.Tooltip
26036 * Bootstrap Tooltip class
26037 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26038 * to determine which dom element triggers the tooltip.
26040 * It needs to add support for additional attributes like tooltip-position
26043 * Create a new Toolti
26044 * @param {Object} config The config object
26047 Roo.bootstrap.Tooltip = function(config){
26048 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26050 this.alignment = Roo.bootstrap.Tooltip.alignment;
26052 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26053 this.alignment = config.alignment;
26058 Roo.apply(Roo.bootstrap.Tooltip, {
26060 * @function init initialize tooltip monitoring.
26064 currentTip : false,
26065 currentRegion : false,
26071 Roo.get(document).on('mouseover', this.enter ,this);
26072 Roo.get(document).on('mouseout', this.leave, this);
26075 this.currentTip = new Roo.bootstrap.Tooltip();
26078 enter : function(ev)
26080 var dom = ev.getTarget();
26082 //Roo.log(['enter',dom]);
26083 var el = Roo.fly(dom);
26084 if (this.currentEl) {
26086 //Roo.log(this.currentEl);
26087 //Roo.log(this.currentEl.contains(dom));
26088 if (this.currentEl == el) {
26091 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26097 if (this.currentTip.el) {
26098 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26102 if(!el || el.dom == document){
26108 // you can not look for children, as if el is the body.. then everythign is the child..
26109 if (!el.attr('tooltip')) { //
26110 if (!el.select("[tooltip]").elements.length) {
26113 // is the mouse over this child...?
26114 bindEl = el.select("[tooltip]").first();
26115 var xy = ev.getXY();
26116 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26117 //Roo.log("not in region.");
26120 //Roo.log("child element over..");
26123 this.currentEl = bindEl;
26124 this.currentTip.bind(bindEl);
26125 this.currentRegion = Roo.lib.Region.getRegion(dom);
26126 this.currentTip.enter();
26129 leave : function(ev)
26131 var dom = ev.getTarget();
26132 //Roo.log(['leave',dom]);
26133 if (!this.currentEl) {
26138 if (dom != this.currentEl.dom) {
26141 var xy = ev.getXY();
26142 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26145 // only activate leave if mouse cursor is outside... bounding box..
26150 if (this.currentTip) {
26151 this.currentTip.leave();
26153 //Roo.log('clear currentEl');
26154 this.currentEl = false;
26159 'left' : ['r-l', [-2,0], 'right'],
26160 'right' : ['l-r', [2,0], 'left'],
26161 'bottom' : ['t-b', [0,2], 'top'],
26162 'top' : [ 'b-t', [0,-2], 'bottom']
26168 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26173 delay : null, // can be { show : 300 , hide: 500}
26177 hoverState : null, //???
26179 placement : 'bottom',
26183 getAutoCreate : function(){
26190 cls : 'tooltip-arrow'
26193 cls : 'tooltip-inner'
26200 bind : function(el)
26206 enter : function () {
26208 if (this.timeout != null) {
26209 clearTimeout(this.timeout);
26212 this.hoverState = 'in';
26213 //Roo.log("enter - show");
26214 if (!this.delay || !this.delay.show) {
26219 this.timeout = setTimeout(function () {
26220 if (_t.hoverState == 'in') {
26223 }, this.delay.show);
26227 clearTimeout(this.timeout);
26229 this.hoverState = 'out';
26230 if (!this.delay || !this.delay.hide) {
26236 this.timeout = setTimeout(function () {
26237 //Roo.log("leave - timeout");
26239 if (_t.hoverState == 'out') {
26241 Roo.bootstrap.Tooltip.currentEl = false;
26246 show : function (msg)
26249 this.render(document.body);
26252 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26254 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26256 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26258 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26260 var placement = typeof this.placement == 'function' ?
26261 this.placement.call(this, this.el, on_el) :
26264 var autoToken = /\s?auto?\s?/i;
26265 var autoPlace = autoToken.test(placement);
26267 placement = placement.replace(autoToken, '') || 'top';
26271 //this.el.setXY([0,0]);
26273 //this.el.dom.style.display='block';
26275 //this.el.appendTo(on_el);
26277 var p = this.getPosition();
26278 var box = this.el.getBox();
26284 var align = this.alignment[placement];
26286 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26288 if(placement == 'top' || placement == 'bottom'){
26290 placement = 'right';
26293 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26294 placement = 'left';
26297 var scroll = Roo.select('body', true).first().getScroll();
26299 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26303 align = this.alignment[placement];
26306 this.el.alignTo(this.bindEl, align[0],align[1]);
26307 //var arrow = this.el.select('.arrow',true).first();
26308 //arrow.set(align[2],
26310 this.el.addClass(placement);
26312 this.el.addClass('in fade');
26314 this.hoverState = null;
26316 if (this.el.hasClass('fade')) {
26327 //this.el.setXY([0,0]);
26328 this.el.removeClass('in');
26344 * @class Roo.bootstrap.LocationPicker
26345 * @extends Roo.bootstrap.Component
26346 * Bootstrap LocationPicker class
26347 * @cfg {Number} latitude Position when init default 0
26348 * @cfg {Number} longitude Position when init default 0
26349 * @cfg {Number} zoom default 15
26350 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26351 * @cfg {Boolean} mapTypeControl default false
26352 * @cfg {Boolean} disableDoubleClickZoom default false
26353 * @cfg {Boolean} scrollwheel default true
26354 * @cfg {Boolean} streetViewControl default false
26355 * @cfg {Number} radius default 0
26356 * @cfg {String} locationName
26357 * @cfg {Boolean} draggable default true
26358 * @cfg {Boolean} enableAutocomplete default false
26359 * @cfg {Boolean} enableReverseGeocode default true
26360 * @cfg {String} markerTitle
26363 * Create a new LocationPicker
26364 * @param {Object} config The config object
26368 Roo.bootstrap.LocationPicker = function(config){
26370 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26375 * Fires when the picker initialized.
26376 * @param {Roo.bootstrap.LocationPicker} this
26377 * @param {Google Location} location
26381 * @event positionchanged
26382 * Fires when the picker position changed.
26383 * @param {Roo.bootstrap.LocationPicker} this
26384 * @param {Google Location} location
26386 positionchanged : true,
26389 * Fires when the map resize.
26390 * @param {Roo.bootstrap.LocationPicker} this
26395 * Fires when the map show.
26396 * @param {Roo.bootstrap.LocationPicker} this
26401 * Fires when the map hide.
26402 * @param {Roo.bootstrap.LocationPicker} this
26407 * Fires when click the map.
26408 * @param {Roo.bootstrap.LocationPicker} this
26409 * @param {Map event} e
26413 * @event mapRightClick
26414 * Fires when right click the map.
26415 * @param {Roo.bootstrap.LocationPicker} this
26416 * @param {Map event} e
26418 mapRightClick : true,
26420 * @event markerClick
26421 * Fires when click the marker.
26422 * @param {Roo.bootstrap.LocationPicker} this
26423 * @param {Map event} e
26425 markerClick : true,
26427 * @event markerRightClick
26428 * Fires when right click the marker.
26429 * @param {Roo.bootstrap.LocationPicker} this
26430 * @param {Map event} e
26432 markerRightClick : true,
26434 * @event OverlayViewDraw
26435 * Fires when OverlayView Draw
26436 * @param {Roo.bootstrap.LocationPicker} this
26438 OverlayViewDraw : true,
26440 * @event OverlayViewOnAdd
26441 * Fires when OverlayView Draw
26442 * @param {Roo.bootstrap.LocationPicker} this
26444 OverlayViewOnAdd : true,
26446 * @event OverlayViewOnRemove
26447 * Fires when OverlayView Draw
26448 * @param {Roo.bootstrap.LocationPicker} this
26450 OverlayViewOnRemove : true,
26452 * @event OverlayViewShow
26453 * Fires when OverlayView Draw
26454 * @param {Roo.bootstrap.LocationPicker} this
26455 * @param {Pixel} cpx
26457 OverlayViewShow : true,
26459 * @event OverlayViewHide
26460 * Fires when OverlayView Draw
26461 * @param {Roo.bootstrap.LocationPicker} this
26463 OverlayViewHide : true,
26465 * @event loadexception
26466 * Fires when load google lib failed.
26467 * @param {Roo.bootstrap.LocationPicker} this
26469 loadexception : true
26474 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26476 gMapContext: false,
26482 mapTypeControl: false,
26483 disableDoubleClickZoom: false,
26485 streetViewControl: false,
26489 enableAutocomplete: false,
26490 enableReverseGeocode: true,
26493 getAutoCreate: function()
26498 cls: 'roo-location-picker'
26504 initEvents: function(ct, position)
26506 if(!this.el.getWidth() || this.isApplied()){
26510 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26515 initial: function()
26517 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26518 this.fireEvent('loadexception', this);
26522 if(!this.mapTypeId){
26523 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26526 this.gMapContext = this.GMapContext();
26528 this.initOverlayView();
26530 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26534 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26535 _this.setPosition(_this.gMapContext.marker.position);
26538 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26539 _this.fireEvent('mapClick', this, event);
26543 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26544 _this.fireEvent('mapRightClick', this, event);
26548 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26549 _this.fireEvent('markerClick', this, event);
26553 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26554 _this.fireEvent('markerRightClick', this, event);
26558 this.setPosition(this.gMapContext.location);
26560 this.fireEvent('initial', this, this.gMapContext.location);
26563 initOverlayView: function()
26567 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26571 _this.fireEvent('OverlayViewDraw', _this);
26576 _this.fireEvent('OverlayViewOnAdd', _this);
26579 onRemove: function()
26581 _this.fireEvent('OverlayViewOnRemove', _this);
26584 show: function(cpx)
26586 _this.fireEvent('OverlayViewShow', _this, cpx);
26591 _this.fireEvent('OverlayViewHide', _this);
26597 fromLatLngToContainerPixel: function(event)
26599 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26602 isApplied: function()
26604 return this.getGmapContext() == false ? false : true;
26607 getGmapContext: function()
26609 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26612 GMapContext: function()
26614 var position = new google.maps.LatLng(this.latitude, this.longitude);
26616 var _map = new google.maps.Map(this.el.dom, {
26619 mapTypeId: this.mapTypeId,
26620 mapTypeControl: this.mapTypeControl,
26621 disableDoubleClickZoom: this.disableDoubleClickZoom,
26622 scrollwheel: this.scrollwheel,
26623 streetViewControl: this.streetViewControl,
26624 locationName: this.locationName,
26625 draggable: this.draggable,
26626 enableAutocomplete: this.enableAutocomplete,
26627 enableReverseGeocode: this.enableReverseGeocode
26630 var _marker = new google.maps.Marker({
26631 position: position,
26633 title: this.markerTitle,
26634 draggable: this.draggable
26641 location: position,
26642 radius: this.radius,
26643 locationName: this.locationName,
26644 addressComponents: {
26645 formatted_address: null,
26646 addressLine1: null,
26647 addressLine2: null,
26649 streetNumber: null,
26653 stateOrProvince: null
26656 domContainer: this.el.dom,
26657 geodecoder: new google.maps.Geocoder()
26661 drawCircle: function(center, radius, options)
26663 if (this.gMapContext.circle != null) {
26664 this.gMapContext.circle.setMap(null);
26668 options = Roo.apply({}, options, {
26669 strokeColor: "#0000FF",
26670 strokeOpacity: .35,
26672 fillColor: "#0000FF",
26676 options.map = this.gMapContext.map;
26677 options.radius = radius;
26678 options.center = center;
26679 this.gMapContext.circle = new google.maps.Circle(options);
26680 return this.gMapContext.circle;
26686 setPosition: function(location)
26688 this.gMapContext.location = location;
26689 this.gMapContext.marker.setPosition(location);
26690 this.gMapContext.map.panTo(location);
26691 this.drawCircle(location, this.gMapContext.radius, {});
26695 if (this.gMapContext.settings.enableReverseGeocode) {
26696 this.gMapContext.geodecoder.geocode({
26697 latLng: this.gMapContext.location
26698 }, function(results, status) {
26700 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26701 _this.gMapContext.locationName = results[0].formatted_address;
26702 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26704 _this.fireEvent('positionchanged', this, location);
26711 this.fireEvent('positionchanged', this, location);
26716 google.maps.event.trigger(this.gMapContext.map, "resize");
26718 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26720 this.fireEvent('resize', this);
26723 setPositionByLatLng: function(latitude, longitude)
26725 this.setPosition(new google.maps.LatLng(latitude, longitude));
26728 getCurrentPosition: function()
26731 latitude: this.gMapContext.location.lat(),
26732 longitude: this.gMapContext.location.lng()
26736 getAddressName: function()
26738 return this.gMapContext.locationName;
26741 getAddressComponents: function()
26743 return this.gMapContext.addressComponents;
26746 address_component_from_google_geocode: function(address_components)
26750 for (var i = 0; i < address_components.length; i++) {
26751 var component = address_components[i];
26752 if (component.types.indexOf("postal_code") >= 0) {
26753 result.postalCode = component.short_name;
26754 } else if (component.types.indexOf("street_number") >= 0) {
26755 result.streetNumber = component.short_name;
26756 } else if (component.types.indexOf("route") >= 0) {
26757 result.streetName = component.short_name;
26758 } else if (component.types.indexOf("neighborhood") >= 0) {
26759 result.city = component.short_name;
26760 } else if (component.types.indexOf("locality") >= 0) {
26761 result.city = component.short_name;
26762 } else if (component.types.indexOf("sublocality") >= 0) {
26763 result.district = component.short_name;
26764 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26765 result.stateOrProvince = component.short_name;
26766 } else if (component.types.indexOf("country") >= 0) {
26767 result.country = component.short_name;
26771 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26772 result.addressLine2 = "";
26776 setZoomLevel: function(zoom)
26778 this.gMapContext.map.setZoom(zoom);
26791 this.fireEvent('show', this);
26802 this.fireEvent('hide', this);
26807 Roo.apply(Roo.bootstrap.LocationPicker, {
26809 OverlayView : function(map, options)
26811 options = options || {};
26825 * @class Roo.bootstrap.Alert
26826 * @extends Roo.bootstrap.Component
26827 * Bootstrap Alert class
26828 * @cfg {String} title The title of alert
26829 * @cfg {String} html The content of alert
26830 * @cfg {String} weight ( success | info | warning | danger )
26831 * @cfg {String} faicon font-awesomeicon
26834 * Create a new alert
26835 * @param {Object} config The config object
26839 Roo.bootstrap.Alert = function(config){
26840 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26844 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26851 getAutoCreate : function()
26860 cls : 'roo-alert-icon'
26865 cls : 'roo-alert-title',
26870 cls : 'roo-alert-text',
26877 cfg.cn[0].cls += ' fa ' + this.faicon;
26881 cfg.cls += ' alert-' + this.weight;
26887 initEvents: function()
26889 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26892 setTitle : function(str)
26894 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26897 setText : function(str)
26899 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26902 setWeight : function(weight)
26905 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26908 this.weight = weight;
26910 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26913 setIcon : function(icon)
26916 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26919 this.faicon = icon;
26921 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26942 * @class Roo.bootstrap.UploadCropbox
26943 * @extends Roo.bootstrap.Component
26944 * Bootstrap UploadCropbox class
26945 * @cfg {String} emptyText show when image has been loaded
26946 * @cfg {String} rotateNotify show when image too small to rotate
26947 * @cfg {Number} errorTimeout default 3000
26948 * @cfg {Number} minWidth default 300
26949 * @cfg {Number} minHeight default 300
26950 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26951 * @cfg {Boolean} isDocument (true|false) default false
26952 * @cfg {String} url action url
26953 * @cfg {String} paramName default 'imageUpload'
26954 * @cfg {String} method default POST
26955 * @cfg {Boolean} loadMask (true|false) default true
26956 * @cfg {Boolean} loadingText default 'Loading...'
26959 * Create a new UploadCropbox
26960 * @param {Object} config The config object
26963 Roo.bootstrap.UploadCropbox = function(config){
26964 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26968 * @event beforeselectfile
26969 * Fire before select file
26970 * @param {Roo.bootstrap.UploadCropbox} this
26972 "beforeselectfile" : true,
26975 * Fire after initEvent
26976 * @param {Roo.bootstrap.UploadCropbox} this
26981 * Fire after initEvent
26982 * @param {Roo.bootstrap.UploadCropbox} this
26983 * @param {String} data
26988 * Fire when preparing the file data
26989 * @param {Roo.bootstrap.UploadCropbox} this
26990 * @param {Object} file
26995 * Fire when get exception
26996 * @param {Roo.bootstrap.UploadCropbox} this
26997 * @param {XMLHttpRequest} xhr
26999 "exception" : true,
27001 * @event beforeloadcanvas
27002 * Fire before load the canvas
27003 * @param {Roo.bootstrap.UploadCropbox} this
27004 * @param {String} src
27006 "beforeloadcanvas" : true,
27009 * Fire when trash image
27010 * @param {Roo.bootstrap.UploadCropbox} this
27015 * Fire when download the image
27016 * @param {Roo.bootstrap.UploadCropbox} this
27020 * @event footerbuttonclick
27021 * Fire when footerbuttonclick
27022 * @param {Roo.bootstrap.UploadCropbox} this
27023 * @param {String} type
27025 "footerbuttonclick" : true,
27029 * @param {Roo.bootstrap.UploadCropbox} this
27034 * Fire when rotate the image
27035 * @param {Roo.bootstrap.UploadCropbox} this
27036 * @param {String} pos
27041 * Fire when inspect the file
27042 * @param {Roo.bootstrap.UploadCropbox} this
27043 * @param {Object} file
27048 * Fire when xhr upload the file
27049 * @param {Roo.bootstrap.UploadCropbox} this
27050 * @param {Object} data
27055 * Fire when arrange the file data
27056 * @param {Roo.bootstrap.UploadCropbox} this
27057 * @param {Object} formData
27062 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27065 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27067 emptyText : 'Click to upload image',
27068 rotateNotify : 'Image is too small to rotate',
27069 errorTimeout : 3000,
27083 cropType : 'image/jpeg',
27085 canvasLoaded : false,
27086 isDocument : false,
27088 paramName : 'imageUpload',
27090 loadingText : 'Loading...',
27093 getAutoCreate : function()
27097 cls : 'roo-upload-cropbox',
27101 cls : 'roo-upload-cropbox-selector',
27106 cls : 'roo-upload-cropbox-body',
27107 style : 'cursor:pointer',
27111 cls : 'roo-upload-cropbox-preview'
27115 cls : 'roo-upload-cropbox-thumb'
27119 cls : 'roo-upload-cropbox-empty-notify',
27120 html : this.emptyText
27124 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27125 html : this.rotateNotify
27131 cls : 'roo-upload-cropbox-footer',
27134 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27144 onRender : function(ct, position)
27146 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27148 if (this.buttons.length) {
27150 Roo.each(this.buttons, function(bb) {
27152 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27154 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27160 this.maskEl = this.el;
27164 initEvents : function()
27166 this.urlAPI = (window.createObjectURL && window) ||
27167 (window.URL && URL.revokeObjectURL && URL) ||
27168 (window.webkitURL && webkitURL);
27170 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27171 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27173 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27174 this.selectorEl.hide();
27176 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27177 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27179 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27180 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27181 this.thumbEl.hide();
27183 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27184 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27186 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27187 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27188 this.errorEl.hide();
27190 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27191 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27192 this.footerEl.hide();
27194 this.setThumbBoxSize();
27200 this.fireEvent('initial', this);
27207 window.addEventListener("resize", function() { _this.resize(); } );
27209 this.bodyEl.on('click', this.beforeSelectFile, this);
27212 this.bodyEl.on('touchstart', this.onTouchStart, this);
27213 this.bodyEl.on('touchmove', this.onTouchMove, this);
27214 this.bodyEl.on('touchend', this.onTouchEnd, this);
27218 this.bodyEl.on('mousedown', this.onMouseDown, this);
27219 this.bodyEl.on('mousemove', this.onMouseMove, this);
27220 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27221 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27222 Roo.get(document).on('mouseup', this.onMouseUp, this);
27225 this.selectorEl.on('change', this.onFileSelected, this);
27231 this.baseScale = 1;
27233 this.baseRotate = 1;
27234 this.dragable = false;
27235 this.pinching = false;
27238 this.cropData = false;
27239 this.notifyEl.dom.innerHTML = this.emptyText;
27241 this.selectorEl.dom.value = '';
27245 resize : function()
27247 if(this.fireEvent('resize', this) != false){
27248 this.setThumbBoxPosition();
27249 this.setCanvasPosition();
27253 onFooterButtonClick : function(e, el, o, type)
27256 case 'rotate-left' :
27257 this.onRotateLeft(e);
27259 case 'rotate-right' :
27260 this.onRotateRight(e);
27263 this.beforeSelectFile(e);
27278 this.fireEvent('footerbuttonclick', this, type);
27281 beforeSelectFile : function(e)
27283 e.preventDefault();
27285 if(this.fireEvent('beforeselectfile', this) != false){
27286 this.selectorEl.dom.click();
27290 onFileSelected : function(e)
27292 e.preventDefault();
27294 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27298 var file = this.selectorEl.dom.files[0];
27300 if(this.fireEvent('inspect', this, file) != false){
27301 this.prepare(file);
27306 trash : function(e)
27308 this.fireEvent('trash', this);
27311 download : function(e)
27313 this.fireEvent('download', this);
27316 loadCanvas : function(src)
27318 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27322 this.imageEl = document.createElement('img');
27326 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27328 this.imageEl.src = src;
27332 onLoadCanvas : function()
27334 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27335 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27337 this.bodyEl.un('click', this.beforeSelectFile, this);
27339 this.notifyEl.hide();
27340 this.thumbEl.show();
27341 this.footerEl.show();
27343 this.baseRotateLevel();
27345 if(this.isDocument){
27346 this.setThumbBoxSize();
27349 this.setThumbBoxPosition();
27351 this.baseScaleLevel();
27357 this.canvasLoaded = true;
27360 this.maskEl.unmask();
27365 setCanvasPosition : function()
27367 if(!this.canvasEl){
27371 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27372 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27374 this.previewEl.setLeft(pw);
27375 this.previewEl.setTop(ph);
27379 onMouseDown : function(e)
27383 this.dragable = true;
27384 this.pinching = false;
27386 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27387 this.dragable = false;
27391 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27392 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27396 onMouseMove : function(e)
27400 if(!this.canvasLoaded){
27404 if (!this.dragable){
27408 var minX = Math.ceil(this.thumbEl.getLeft(true));
27409 var minY = Math.ceil(this.thumbEl.getTop(true));
27411 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27412 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27414 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27415 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27417 x = x - this.mouseX;
27418 y = y - this.mouseY;
27420 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27421 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27423 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27424 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27426 this.previewEl.setLeft(bgX);
27427 this.previewEl.setTop(bgY);
27429 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27430 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27433 onMouseUp : function(e)
27437 this.dragable = false;
27440 onMouseWheel : function(e)
27444 this.startScale = this.scale;
27446 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27448 if(!this.zoomable()){
27449 this.scale = this.startScale;
27458 zoomable : function()
27460 var minScale = this.thumbEl.getWidth() / this.minWidth;
27462 if(this.minWidth < this.minHeight){
27463 minScale = this.thumbEl.getHeight() / this.minHeight;
27466 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27467 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27471 (this.rotate == 0 || this.rotate == 180) &&
27473 width > this.imageEl.OriginWidth ||
27474 height > this.imageEl.OriginHeight ||
27475 (width < this.minWidth && height < this.minHeight)
27483 (this.rotate == 90 || this.rotate == 270) &&
27485 width > this.imageEl.OriginWidth ||
27486 height > this.imageEl.OriginHeight ||
27487 (width < this.minHeight && height < this.minWidth)
27494 !this.isDocument &&
27495 (this.rotate == 0 || this.rotate == 180) &&
27497 width < this.minWidth ||
27498 width > this.imageEl.OriginWidth ||
27499 height < this.minHeight ||
27500 height > this.imageEl.OriginHeight
27507 !this.isDocument &&
27508 (this.rotate == 90 || this.rotate == 270) &&
27510 width < this.minHeight ||
27511 width > this.imageEl.OriginWidth ||
27512 height < this.minWidth ||
27513 height > this.imageEl.OriginHeight
27523 onRotateLeft : function(e)
27525 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27527 var minScale = this.thumbEl.getWidth() / this.minWidth;
27529 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27530 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27532 this.startScale = this.scale;
27534 while (this.getScaleLevel() < minScale){
27536 this.scale = this.scale + 1;
27538 if(!this.zoomable()){
27543 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27544 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27549 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27556 this.scale = this.startScale;
27558 this.onRotateFail();
27563 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27565 if(this.isDocument){
27566 this.setThumbBoxSize();
27567 this.setThumbBoxPosition();
27568 this.setCanvasPosition();
27573 this.fireEvent('rotate', this, 'left');
27577 onRotateRight : function(e)
27579 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27581 var minScale = this.thumbEl.getWidth() / this.minWidth;
27583 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27584 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27586 this.startScale = this.scale;
27588 while (this.getScaleLevel() < minScale){
27590 this.scale = this.scale + 1;
27592 if(!this.zoomable()){
27597 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27598 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27603 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27610 this.scale = this.startScale;
27612 this.onRotateFail();
27617 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27619 if(this.isDocument){
27620 this.setThumbBoxSize();
27621 this.setThumbBoxPosition();
27622 this.setCanvasPosition();
27627 this.fireEvent('rotate', this, 'right');
27630 onRotateFail : function()
27632 this.errorEl.show(true);
27636 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27641 this.previewEl.dom.innerHTML = '';
27643 var canvasEl = document.createElement("canvas");
27645 var contextEl = canvasEl.getContext("2d");
27647 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27648 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27649 var center = this.imageEl.OriginWidth / 2;
27651 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27652 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27653 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27654 center = this.imageEl.OriginHeight / 2;
27657 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27659 contextEl.translate(center, center);
27660 contextEl.rotate(this.rotate * Math.PI / 180);
27662 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27664 this.canvasEl = document.createElement("canvas");
27666 this.contextEl = this.canvasEl.getContext("2d");
27668 switch (this.rotate) {
27671 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27672 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27674 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27679 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27680 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27682 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27683 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);
27687 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27692 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27693 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27695 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27696 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);
27700 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);
27705 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27706 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27708 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27709 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27713 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);
27720 this.previewEl.appendChild(this.canvasEl);
27722 this.setCanvasPosition();
27727 if(!this.canvasLoaded){
27731 var imageCanvas = document.createElement("canvas");
27733 var imageContext = imageCanvas.getContext("2d");
27735 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27736 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27738 var center = imageCanvas.width / 2;
27740 imageContext.translate(center, center);
27742 imageContext.rotate(this.rotate * Math.PI / 180);
27744 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27746 var canvas = document.createElement("canvas");
27748 var context = canvas.getContext("2d");
27750 canvas.width = this.minWidth;
27751 canvas.height = this.minHeight;
27753 switch (this.rotate) {
27756 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27757 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27759 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27760 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27762 var targetWidth = this.minWidth - 2 * x;
27763 var targetHeight = this.minHeight - 2 * y;
27767 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27768 scale = targetWidth / width;
27771 if(x > 0 && y == 0){
27772 scale = targetHeight / height;
27775 if(x > 0 && y > 0){
27776 scale = targetWidth / width;
27778 if(width < height){
27779 scale = targetHeight / height;
27783 context.scale(scale, scale);
27785 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27786 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27788 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27789 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27791 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27796 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27797 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27799 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27800 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27802 var targetWidth = this.minWidth - 2 * x;
27803 var targetHeight = this.minHeight - 2 * y;
27807 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27808 scale = targetWidth / width;
27811 if(x > 0 && y == 0){
27812 scale = targetHeight / height;
27815 if(x > 0 && y > 0){
27816 scale = targetWidth / width;
27818 if(width < height){
27819 scale = targetHeight / height;
27823 context.scale(scale, scale);
27825 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27826 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27828 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27829 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27831 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27833 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27838 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27839 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27841 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27842 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27844 var targetWidth = this.minWidth - 2 * x;
27845 var targetHeight = this.minHeight - 2 * y;
27849 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27850 scale = targetWidth / width;
27853 if(x > 0 && y == 0){
27854 scale = targetHeight / height;
27857 if(x > 0 && y > 0){
27858 scale = targetWidth / width;
27860 if(width < height){
27861 scale = targetHeight / height;
27865 context.scale(scale, scale);
27867 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27868 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27870 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27871 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27873 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27874 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27876 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27881 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27882 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27884 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27885 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27887 var targetWidth = this.minWidth - 2 * x;
27888 var targetHeight = this.minHeight - 2 * y;
27892 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27893 scale = targetWidth / width;
27896 if(x > 0 && y == 0){
27897 scale = targetHeight / height;
27900 if(x > 0 && y > 0){
27901 scale = targetWidth / width;
27903 if(width < height){
27904 scale = targetHeight / height;
27908 context.scale(scale, scale);
27910 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27911 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27913 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27914 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27916 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27918 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27925 this.cropData = canvas.toDataURL(this.cropType);
27927 if(this.fireEvent('crop', this, this.cropData) !== false){
27928 this.process(this.file, this.cropData);
27935 setThumbBoxSize : function()
27939 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27940 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27941 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27943 this.minWidth = width;
27944 this.minHeight = height;
27946 if(this.rotate == 90 || this.rotate == 270){
27947 this.minWidth = height;
27948 this.minHeight = width;
27953 width = Math.ceil(this.minWidth * height / this.minHeight);
27955 if(this.minWidth > this.minHeight){
27957 height = Math.ceil(this.minHeight * width / this.minWidth);
27960 this.thumbEl.setStyle({
27961 width : width + 'px',
27962 height : height + 'px'
27969 setThumbBoxPosition : function()
27971 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27972 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27974 this.thumbEl.setLeft(x);
27975 this.thumbEl.setTop(y);
27979 baseRotateLevel : function()
27981 this.baseRotate = 1;
27984 typeof(this.exif) != 'undefined' &&
27985 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27986 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27988 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27991 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27995 baseScaleLevel : function()
27999 if(this.isDocument){
28001 if(this.baseRotate == 6 || this.baseRotate == 8){
28003 height = this.thumbEl.getHeight();
28004 this.baseScale = height / this.imageEl.OriginWidth;
28006 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28007 width = this.thumbEl.getWidth();
28008 this.baseScale = width / this.imageEl.OriginHeight;
28014 height = this.thumbEl.getHeight();
28015 this.baseScale = height / this.imageEl.OriginHeight;
28017 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28018 width = this.thumbEl.getWidth();
28019 this.baseScale = width / this.imageEl.OriginWidth;
28025 if(this.baseRotate == 6 || this.baseRotate == 8){
28027 width = this.thumbEl.getHeight();
28028 this.baseScale = width / this.imageEl.OriginHeight;
28030 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28031 height = this.thumbEl.getWidth();
28032 this.baseScale = height / this.imageEl.OriginHeight;
28035 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28036 height = this.thumbEl.getWidth();
28037 this.baseScale = height / this.imageEl.OriginHeight;
28039 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28040 width = this.thumbEl.getHeight();
28041 this.baseScale = width / this.imageEl.OriginWidth;
28048 width = this.thumbEl.getWidth();
28049 this.baseScale = width / this.imageEl.OriginWidth;
28051 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28052 height = this.thumbEl.getHeight();
28053 this.baseScale = height / this.imageEl.OriginHeight;
28056 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28058 height = this.thumbEl.getHeight();
28059 this.baseScale = height / this.imageEl.OriginHeight;
28061 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28062 width = this.thumbEl.getWidth();
28063 this.baseScale = width / this.imageEl.OriginWidth;
28071 getScaleLevel : function()
28073 return this.baseScale * Math.pow(1.1, this.scale);
28076 onTouchStart : function(e)
28078 if(!this.canvasLoaded){
28079 this.beforeSelectFile(e);
28083 var touches = e.browserEvent.touches;
28089 if(touches.length == 1){
28090 this.onMouseDown(e);
28094 if(touches.length != 2){
28100 for(var i = 0, finger; finger = touches[i]; i++){
28101 coords.push(finger.pageX, finger.pageY);
28104 var x = Math.pow(coords[0] - coords[2], 2);
28105 var y = Math.pow(coords[1] - coords[3], 2);
28107 this.startDistance = Math.sqrt(x + y);
28109 this.startScale = this.scale;
28111 this.pinching = true;
28112 this.dragable = false;
28116 onTouchMove : function(e)
28118 if(!this.pinching && !this.dragable){
28122 var touches = e.browserEvent.touches;
28129 this.onMouseMove(e);
28135 for(var i = 0, finger; finger = touches[i]; i++){
28136 coords.push(finger.pageX, finger.pageY);
28139 var x = Math.pow(coords[0] - coords[2], 2);
28140 var y = Math.pow(coords[1] - coords[3], 2);
28142 this.endDistance = Math.sqrt(x + y);
28144 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28146 if(!this.zoomable()){
28147 this.scale = this.startScale;
28155 onTouchEnd : function(e)
28157 this.pinching = false;
28158 this.dragable = false;
28162 process : function(file, crop)
28165 this.maskEl.mask(this.loadingText);
28168 this.xhr = new XMLHttpRequest();
28170 file.xhr = this.xhr;
28172 this.xhr.open(this.method, this.url, true);
28175 "Accept": "application/json",
28176 "Cache-Control": "no-cache",
28177 "X-Requested-With": "XMLHttpRequest"
28180 for (var headerName in headers) {
28181 var headerValue = headers[headerName];
28183 this.xhr.setRequestHeader(headerName, headerValue);
28189 this.xhr.onload = function()
28191 _this.xhrOnLoad(_this.xhr);
28194 this.xhr.onerror = function()
28196 _this.xhrOnError(_this.xhr);
28199 var formData = new FormData();
28201 formData.append('returnHTML', 'NO');
28204 formData.append('crop', crop);
28207 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28208 formData.append(this.paramName, file, file.name);
28211 if(typeof(file.filename) != 'undefined'){
28212 formData.append('filename', file.filename);
28215 if(typeof(file.mimetype) != 'undefined'){
28216 formData.append('mimetype', file.mimetype);
28219 if(this.fireEvent('arrange', this, formData) != false){
28220 this.xhr.send(formData);
28224 xhrOnLoad : function(xhr)
28227 this.maskEl.unmask();
28230 if (xhr.readyState !== 4) {
28231 this.fireEvent('exception', this, xhr);
28235 var response = Roo.decode(xhr.responseText);
28237 if(!response.success){
28238 this.fireEvent('exception', this, xhr);
28242 var response = Roo.decode(xhr.responseText);
28244 this.fireEvent('upload', this, response);
28248 xhrOnError : function()
28251 this.maskEl.unmask();
28254 Roo.log('xhr on error');
28256 var response = Roo.decode(xhr.responseText);
28262 prepare : function(file)
28265 this.maskEl.mask(this.loadingText);
28271 if(typeof(file) === 'string'){
28272 this.loadCanvas(file);
28276 if(!file || !this.urlAPI){
28281 this.cropType = file.type;
28285 if(this.fireEvent('prepare', this, this.file) != false){
28287 var reader = new FileReader();
28289 reader.onload = function (e) {
28290 if (e.target.error) {
28291 Roo.log(e.target.error);
28295 var buffer = e.target.result,
28296 dataView = new DataView(buffer),
28298 maxOffset = dataView.byteLength - 4,
28302 if (dataView.getUint16(0) === 0xffd8) {
28303 while (offset < maxOffset) {
28304 markerBytes = dataView.getUint16(offset);
28306 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28307 markerLength = dataView.getUint16(offset + 2) + 2;
28308 if (offset + markerLength > dataView.byteLength) {
28309 Roo.log('Invalid meta data: Invalid segment size.');
28313 if(markerBytes == 0xffe1){
28314 _this.parseExifData(
28321 offset += markerLength;
28331 var url = _this.urlAPI.createObjectURL(_this.file);
28333 _this.loadCanvas(url);
28338 reader.readAsArrayBuffer(this.file);
28344 parseExifData : function(dataView, offset, length)
28346 var tiffOffset = offset + 10,
28350 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28351 // No Exif data, might be XMP data instead
28355 // Check for the ASCII code for "Exif" (0x45786966):
28356 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28357 // No Exif data, might be XMP data instead
28360 if (tiffOffset + 8 > dataView.byteLength) {
28361 Roo.log('Invalid Exif data: Invalid segment size.');
28364 // Check for the two null bytes:
28365 if (dataView.getUint16(offset + 8) !== 0x0000) {
28366 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28369 // Check the byte alignment:
28370 switch (dataView.getUint16(tiffOffset)) {
28372 littleEndian = true;
28375 littleEndian = false;
28378 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28381 // Check for the TIFF tag marker (0x002A):
28382 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28383 Roo.log('Invalid Exif data: Missing TIFF marker.');
28386 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28387 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28389 this.parseExifTags(
28392 tiffOffset + dirOffset,
28397 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28402 if (dirOffset + 6 > dataView.byteLength) {
28403 Roo.log('Invalid Exif data: Invalid directory offset.');
28406 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28407 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28408 if (dirEndOffset + 4 > dataView.byteLength) {
28409 Roo.log('Invalid Exif data: Invalid directory size.');
28412 for (i = 0; i < tagsNumber; i += 1) {
28416 dirOffset + 2 + 12 * i, // tag offset
28420 // Return the offset to the next directory:
28421 return dataView.getUint32(dirEndOffset, littleEndian);
28424 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28426 var tag = dataView.getUint16(offset, littleEndian);
28428 this.exif[tag] = this.getExifValue(
28432 dataView.getUint16(offset + 2, littleEndian), // tag type
28433 dataView.getUint32(offset + 4, littleEndian), // tag length
28438 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28440 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28449 Roo.log('Invalid Exif data: Invalid tag type.');
28453 tagSize = tagType.size * length;
28454 // Determine if the value is contained in the dataOffset bytes,
28455 // or if the value at the dataOffset is a pointer to the actual data:
28456 dataOffset = tagSize > 4 ?
28457 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28458 if (dataOffset + tagSize > dataView.byteLength) {
28459 Roo.log('Invalid Exif data: Invalid data offset.');
28462 if (length === 1) {
28463 return tagType.getValue(dataView, dataOffset, littleEndian);
28466 for (i = 0; i < length; i += 1) {
28467 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28470 if (tagType.ascii) {
28472 // Concatenate the chars:
28473 for (i = 0; i < values.length; i += 1) {
28475 // Ignore the terminating NULL byte(s):
28476 if (c === '\u0000') {
28488 Roo.apply(Roo.bootstrap.UploadCropbox, {
28490 'Orientation': 0x0112
28494 1: 0, //'top-left',
28496 3: 180, //'bottom-right',
28497 // 4: 'bottom-left',
28499 6: 90, //'right-top',
28500 // 7: 'right-bottom',
28501 8: 270 //'left-bottom'
28505 // byte, 8-bit unsigned int:
28507 getValue: function (dataView, dataOffset) {
28508 return dataView.getUint8(dataOffset);
28512 // ascii, 8-bit byte:
28514 getValue: function (dataView, dataOffset) {
28515 return String.fromCharCode(dataView.getUint8(dataOffset));
28520 // short, 16 bit int:
28522 getValue: function (dataView, dataOffset, littleEndian) {
28523 return dataView.getUint16(dataOffset, littleEndian);
28527 // long, 32 bit int:
28529 getValue: function (dataView, dataOffset, littleEndian) {
28530 return dataView.getUint32(dataOffset, littleEndian);
28534 // rational = two long values, first is numerator, second is denominator:
28536 getValue: function (dataView, dataOffset, littleEndian) {
28537 return dataView.getUint32(dataOffset, littleEndian) /
28538 dataView.getUint32(dataOffset + 4, littleEndian);
28542 // slong, 32 bit signed int:
28544 getValue: function (dataView, dataOffset, littleEndian) {
28545 return dataView.getInt32(dataOffset, littleEndian);
28549 // srational, two slongs, first is numerator, second is denominator:
28551 getValue: function (dataView, dataOffset, littleEndian) {
28552 return dataView.getInt32(dataOffset, littleEndian) /
28553 dataView.getInt32(dataOffset + 4, littleEndian);
28563 cls : 'btn-group roo-upload-cropbox-rotate-left',
28564 action : 'rotate-left',
28568 cls : 'btn btn-default',
28569 html : '<i class="fa fa-undo"></i>'
28575 cls : 'btn-group roo-upload-cropbox-picture',
28576 action : 'picture',
28580 cls : 'btn btn-default',
28581 html : '<i class="fa fa-picture-o"></i>'
28587 cls : 'btn-group roo-upload-cropbox-rotate-right',
28588 action : 'rotate-right',
28592 cls : 'btn btn-default',
28593 html : '<i class="fa fa-repeat"></i>'
28601 cls : 'btn-group roo-upload-cropbox-rotate-left',
28602 action : 'rotate-left',
28606 cls : 'btn btn-default',
28607 html : '<i class="fa fa-undo"></i>'
28613 cls : 'btn-group roo-upload-cropbox-download',
28614 action : 'download',
28618 cls : 'btn btn-default',
28619 html : '<i class="fa fa-download"></i>'
28625 cls : 'btn-group roo-upload-cropbox-crop',
28630 cls : 'btn btn-default',
28631 html : '<i class="fa fa-crop"></i>'
28637 cls : 'btn-group roo-upload-cropbox-trash',
28642 cls : 'btn btn-default',
28643 html : '<i class="fa fa-trash"></i>'
28649 cls : 'btn-group roo-upload-cropbox-rotate-right',
28650 action : 'rotate-right',
28654 cls : 'btn btn-default',
28655 html : '<i class="fa fa-repeat"></i>'
28663 cls : 'btn-group roo-upload-cropbox-rotate-left',
28664 action : 'rotate-left',
28668 cls : 'btn btn-default',
28669 html : '<i class="fa fa-undo"></i>'
28675 cls : 'btn-group roo-upload-cropbox-rotate-right',
28676 action : 'rotate-right',
28680 cls : 'btn btn-default',
28681 html : '<i class="fa fa-repeat"></i>'
28694 * @class Roo.bootstrap.DocumentManager
28695 * @extends Roo.bootstrap.Component
28696 * Bootstrap DocumentManager class
28697 * @cfg {String} paramName default 'imageUpload'
28698 * @cfg {String} toolTipName default 'filename'
28699 * @cfg {String} method default POST
28700 * @cfg {String} url action url
28701 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28702 * @cfg {Boolean} multiple multiple upload default true
28703 * @cfg {Number} thumbSize default 300
28704 * @cfg {String} fieldLabel
28705 * @cfg {Number} labelWidth default 4
28706 * @cfg {String} labelAlign (left|top) default left
28707 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28708 * @cfg {Number} labellg set the width of label (1-12)
28709 * @cfg {Number} labelmd set the width of label (1-12)
28710 * @cfg {Number} labelsm set the width of label (1-12)
28711 * @cfg {Number} labelxs set the width of label (1-12)
28714 * Create a new DocumentManager
28715 * @param {Object} config The config object
28718 Roo.bootstrap.DocumentManager = function(config){
28719 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28722 this.delegates = [];
28727 * Fire when initial the DocumentManager
28728 * @param {Roo.bootstrap.DocumentManager} this
28733 * inspect selected file
28734 * @param {Roo.bootstrap.DocumentManager} this
28735 * @param {File} file
28740 * Fire when xhr load exception
28741 * @param {Roo.bootstrap.DocumentManager} this
28742 * @param {XMLHttpRequest} xhr
28744 "exception" : true,
28746 * @event afterupload
28747 * Fire when xhr load exception
28748 * @param {Roo.bootstrap.DocumentManager} this
28749 * @param {XMLHttpRequest} xhr
28751 "afterupload" : true,
28754 * prepare the form data
28755 * @param {Roo.bootstrap.DocumentManager} this
28756 * @param {Object} formData
28761 * Fire when remove the file
28762 * @param {Roo.bootstrap.DocumentManager} this
28763 * @param {Object} file
28768 * Fire after refresh the file
28769 * @param {Roo.bootstrap.DocumentManager} this
28774 * Fire after click the image
28775 * @param {Roo.bootstrap.DocumentManager} this
28776 * @param {Object} file
28781 * Fire when upload a image and editable set to true
28782 * @param {Roo.bootstrap.DocumentManager} this
28783 * @param {Object} file
28787 * @event beforeselectfile
28788 * Fire before select file
28789 * @param {Roo.bootstrap.DocumentManager} this
28791 "beforeselectfile" : true,
28794 * Fire before process file
28795 * @param {Roo.bootstrap.DocumentManager} this
28796 * @param {Object} file
28800 * @event previewrendered
28801 * Fire when preview rendered
28802 * @param {Roo.bootstrap.DocumentManager} this
28803 * @param {Object} file
28805 "previewrendered" : true,
28808 "previewResize" : true
28813 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28822 paramName : 'imageUpload',
28823 toolTipName : 'filename',
28826 labelAlign : 'left',
28836 getAutoCreate : function()
28838 var managerWidget = {
28840 cls : 'roo-document-manager',
28844 cls : 'roo-document-manager-selector',
28849 cls : 'roo-document-manager-uploader',
28853 cls : 'roo-document-manager-upload-btn',
28854 html : '<i class="fa fa-plus"></i>'
28865 cls : 'column col-md-12',
28870 if(this.fieldLabel.length){
28875 cls : 'column col-md-12',
28876 html : this.fieldLabel
28880 cls : 'column col-md-12',
28885 if(this.labelAlign == 'left'){
28890 html : this.fieldLabel
28899 if(this.labelWidth > 12){
28900 content[0].style = "width: " + this.labelWidth + 'px';
28903 if(this.labelWidth < 13 && this.labelmd == 0){
28904 this.labelmd = this.labelWidth;
28907 if(this.labellg > 0){
28908 content[0].cls += ' col-lg-' + this.labellg;
28909 content[1].cls += ' col-lg-' + (12 - this.labellg);
28912 if(this.labelmd > 0){
28913 content[0].cls += ' col-md-' + this.labelmd;
28914 content[1].cls += ' col-md-' + (12 - this.labelmd);
28917 if(this.labelsm > 0){
28918 content[0].cls += ' col-sm-' + this.labelsm;
28919 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28922 if(this.labelxs > 0){
28923 content[0].cls += ' col-xs-' + this.labelxs;
28924 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28932 cls : 'row clearfix',
28940 initEvents : function()
28942 this.managerEl = this.el.select('.roo-document-manager', true).first();
28943 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28945 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28946 this.selectorEl.hide();
28949 this.selectorEl.attr('multiple', 'multiple');
28952 this.selectorEl.on('change', this.onFileSelected, this);
28954 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28955 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28957 this.uploader.on('click', this.onUploaderClick, this);
28959 this.renderProgressDialog();
28963 window.addEventListener("resize", function() { _this.refresh(); } );
28965 this.fireEvent('initial', this);
28968 renderProgressDialog : function()
28972 this.progressDialog = new Roo.bootstrap.Modal({
28973 cls : 'roo-document-manager-progress-dialog',
28974 allow_close : false,
28984 btnclick : function() {
28985 _this.uploadCancel();
28991 this.progressDialog.render(Roo.get(document.body));
28993 this.progress = new Roo.bootstrap.Progress({
28994 cls : 'roo-document-manager-progress',
28999 this.progress.render(this.progressDialog.getChildContainer());
29001 this.progressBar = new Roo.bootstrap.ProgressBar({
29002 cls : 'roo-document-manager-progress-bar',
29005 aria_valuemax : 12,
29009 this.progressBar.render(this.progress.getChildContainer());
29012 onUploaderClick : function(e)
29014 e.preventDefault();
29016 if(this.fireEvent('beforeselectfile', this) != false){
29017 this.selectorEl.dom.click();
29022 onFileSelected : function(e)
29024 e.preventDefault();
29026 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29030 Roo.each(this.selectorEl.dom.files, function(file){
29031 if(this.fireEvent('inspect', this, file) != false){
29032 this.files.push(file);
29042 this.selectorEl.dom.value = '';
29044 if(!this.files || !this.files.length){
29048 if(this.boxes > 0 && this.files.length > this.boxes){
29049 this.files = this.files.slice(0, this.boxes);
29052 this.uploader.show();
29054 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29055 this.uploader.hide();
29064 Roo.each(this.files, function(file){
29066 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29067 var f = this.renderPreview(file);
29072 if(file.type.indexOf('image') != -1){
29073 this.delegates.push(
29075 _this.process(file);
29076 }).createDelegate(this)
29084 _this.process(file);
29085 }).createDelegate(this)
29090 this.files = files;
29092 this.delegates = this.delegates.concat(docs);
29094 if(!this.delegates.length){
29099 this.progressBar.aria_valuemax = this.delegates.length;
29106 arrange : function()
29108 if(!this.delegates.length){
29109 this.progressDialog.hide();
29114 var delegate = this.delegates.shift();
29116 this.progressDialog.show();
29118 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29120 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29125 refresh : function()
29127 this.uploader.show();
29129 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29130 this.uploader.hide();
29133 Roo.isTouch ? this.closable(false) : this.closable(true);
29135 this.fireEvent('refresh', this);
29138 onRemove : function(e, el, o)
29140 e.preventDefault();
29142 this.fireEvent('remove', this, o);
29146 remove : function(o)
29150 Roo.each(this.files, function(file){
29151 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29160 this.files = files;
29167 Roo.each(this.files, function(file){
29172 file.target.remove();
29181 onClick : function(e, el, o)
29183 e.preventDefault();
29185 this.fireEvent('click', this, o);
29189 closable : function(closable)
29191 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29193 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29205 xhrOnLoad : function(xhr)
29207 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29211 if (xhr.readyState !== 4) {
29213 this.fireEvent('exception', this, xhr);
29217 var response = Roo.decode(xhr.responseText);
29219 if(!response.success){
29221 this.fireEvent('exception', this, xhr);
29225 var file = this.renderPreview(response.data);
29227 this.files.push(file);
29231 this.fireEvent('afterupload', this, xhr);
29235 xhrOnError : function(xhr)
29237 Roo.log('xhr on error');
29239 var response = Roo.decode(xhr.responseText);
29246 process : function(file)
29248 if(this.fireEvent('process', this, file) !== false){
29249 if(this.editable && file.type.indexOf('image') != -1){
29250 this.fireEvent('edit', this, file);
29254 this.uploadStart(file, false);
29261 uploadStart : function(file, crop)
29263 this.xhr = new XMLHttpRequest();
29265 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29270 file.xhr = this.xhr;
29272 this.managerEl.createChild({
29274 cls : 'roo-document-manager-loading',
29278 tooltip : file.name,
29279 cls : 'roo-document-manager-thumb',
29280 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29286 this.xhr.open(this.method, this.url, true);
29289 "Accept": "application/json",
29290 "Cache-Control": "no-cache",
29291 "X-Requested-With": "XMLHttpRequest"
29294 for (var headerName in headers) {
29295 var headerValue = headers[headerName];
29297 this.xhr.setRequestHeader(headerName, headerValue);
29303 this.xhr.onload = function()
29305 _this.xhrOnLoad(_this.xhr);
29308 this.xhr.onerror = function()
29310 _this.xhrOnError(_this.xhr);
29313 var formData = new FormData();
29315 formData.append('returnHTML', 'NO');
29318 formData.append('crop', crop);
29321 formData.append(this.paramName, file, file.name);
29328 if(this.fireEvent('prepare', this, formData, options) != false){
29330 if(options.manually){
29334 this.xhr.send(formData);
29338 this.uploadCancel();
29341 uploadCancel : function()
29347 this.delegates = [];
29349 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29356 renderPreview : function(file)
29358 if(typeof(file.target) != 'undefined' && file.target){
29362 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29364 var previewEl = this.managerEl.createChild({
29366 cls : 'roo-document-manager-preview',
29370 tooltip : file[this.toolTipName],
29371 cls : 'roo-document-manager-thumb',
29372 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29377 html : '<i class="fa fa-times-circle"></i>'
29382 var close = previewEl.select('button.close', true).first();
29384 close.on('click', this.onRemove, this, file);
29386 file.target = previewEl;
29388 var image = previewEl.select('img', true).first();
29392 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29394 image.on('click', this.onClick, this, file);
29396 this.fireEvent('previewrendered', this, file);
29402 onPreviewLoad : function(file, image)
29404 if(typeof(file.target) == 'undefined' || !file.target){
29408 var width = image.dom.naturalWidth || image.dom.width;
29409 var height = image.dom.naturalHeight || image.dom.height;
29411 if(!this.previewResize) {
29415 if(width > height){
29416 file.target.addClass('wide');
29420 file.target.addClass('tall');
29425 uploadFromSource : function(file, crop)
29427 this.xhr = new XMLHttpRequest();
29429 this.managerEl.createChild({
29431 cls : 'roo-document-manager-loading',
29435 tooltip : file.name,
29436 cls : 'roo-document-manager-thumb',
29437 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29443 this.xhr.open(this.method, this.url, true);
29446 "Accept": "application/json",
29447 "Cache-Control": "no-cache",
29448 "X-Requested-With": "XMLHttpRequest"
29451 for (var headerName in headers) {
29452 var headerValue = headers[headerName];
29454 this.xhr.setRequestHeader(headerName, headerValue);
29460 this.xhr.onload = function()
29462 _this.xhrOnLoad(_this.xhr);
29465 this.xhr.onerror = function()
29467 _this.xhrOnError(_this.xhr);
29470 var formData = new FormData();
29472 formData.append('returnHTML', 'NO');
29474 formData.append('crop', crop);
29476 if(typeof(file.filename) != 'undefined'){
29477 formData.append('filename', file.filename);
29480 if(typeof(file.mimetype) != 'undefined'){
29481 formData.append('mimetype', file.mimetype);
29486 if(this.fireEvent('prepare', this, formData) != false){
29487 this.xhr.send(formData);
29497 * @class Roo.bootstrap.DocumentViewer
29498 * @extends Roo.bootstrap.Component
29499 * Bootstrap DocumentViewer class
29500 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29501 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29504 * Create a new DocumentViewer
29505 * @param {Object} config The config object
29508 Roo.bootstrap.DocumentViewer = function(config){
29509 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29514 * Fire after initEvent
29515 * @param {Roo.bootstrap.DocumentViewer} this
29521 * @param {Roo.bootstrap.DocumentViewer} this
29526 * Fire after download button
29527 * @param {Roo.bootstrap.DocumentViewer} this
29532 * Fire after trash button
29533 * @param {Roo.bootstrap.DocumentViewer} this
29540 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29542 showDownload : true,
29546 getAutoCreate : function()
29550 cls : 'roo-document-viewer',
29554 cls : 'roo-document-viewer-body',
29558 cls : 'roo-document-viewer-thumb',
29562 cls : 'roo-document-viewer-image'
29570 cls : 'roo-document-viewer-footer',
29573 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29577 cls : 'btn-group roo-document-viewer-download',
29581 cls : 'btn btn-default',
29582 html : '<i class="fa fa-download"></i>'
29588 cls : 'btn-group roo-document-viewer-trash',
29592 cls : 'btn btn-default',
29593 html : '<i class="fa fa-trash"></i>'
29606 initEvents : function()
29608 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29609 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29611 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29612 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29614 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29615 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29617 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29618 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29620 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29621 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29623 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29624 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29626 this.bodyEl.on('click', this.onClick, this);
29627 this.downloadBtn.on('click', this.onDownload, this);
29628 this.trashBtn.on('click', this.onTrash, this);
29630 this.downloadBtn.hide();
29631 this.trashBtn.hide();
29633 if(this.showDownload){
29634 this.downloadBtn.show();
29637 if(this.showTrash){
29638 this.trashBtn.show();
29641 if(!this.showDownload && !this.showTrash) {
29642 this.footerEl.hide();
29647 initial : function()
29649 this.fireEvent('initial', this);
29653 onClick : function(e)
29655 e.preventDefault();
29657 this.fireEvent('click', this);
29660 onDownload : function(e)
29662 e.preventDefault();
29664 this.fireEvent('download', this);
29667 onTrash : function(e)
29669 e.preventDefault();
29671 this.fireEvent('trash', this);
29683 * @class Roo.bootstrap.NavProgressBar
29684 * @extends Roo.bootstrap.Component
29685 * Bootstrap NavProgressBar class
29688 * Create a new nav progress bar
29689 * @param {Object} config The config object
29692 Roo.bootstrap.NavProgressBar = function(config){
29693 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29695 this.bullets = this.bullets || [];
29697 // Roo.bootstrap.NavProgressBar.register(this);
29701 * Fires when the active item changes
29702 * @param {Roo.bootstrap.NavProgressBar} this
29703 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29704 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29711 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29716 getAutoCreate : function()
29718 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29722 cls : 'roo-navigation-bar-group',
29726 cls : 'roo-navigation-top-bar'
29730 cls : 'roo-navigation-bullets-bar',
29734 cls : 'roo-navigation-bar'
29741 cls : 'roo-navigation-bottom-bar'
29751 initEvents: function()
29756 onRender : function(ct, position)
29758 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29760 if(this.bullets.length){
29761 Roo.each(this.bullets, function(b){
29770 addItem : function(cfg)
29772 var item = new Roo.bootstrap.NavProgressItem(cfg);
29774 item.parentId = this.id;
29775 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29778 var top = new Roo.bootstrap.Element({
29780 cls : 'roo-navigation-bar-text'
29783 var bottom = new Roo.bootstrap.Element({
29785 cls : 'roo-navigation-bar-text'
29788 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29789 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29791 var topText = new Roo.bootstrap.Element({
29793 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29796 var bottomText = new Roo.bootstrap.Element({
29798 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29801 topText.onRender(top.el, null);
29802 bottomText.onRender(bottom.el, null);
29805 item.bottomEl = bottom;
29808 this.barItems.push(item);
29813 getActive : function()
29815 var active = false;
29817 Roo.each(this.barItems, function(v){
29819 if (!v.isActive()) {
29831 setActiveItem : function(item)
29835 Roo.each(this.barItems, function(v){
29836 if (v.rid == item.rid) {
29840 if (v.isActive()) {
29841 v.setActive(false);
29846 item.setActive(true);
29848 this.fireEvent('changed', this, item, prev);
29851 getBarItem: function(rid)
29855 Roo.each(this.barItems, function(e) {
29856 if (e.rid != rid) {
29867 indexOfItem : function(item)
29871 Roo.each(this.barItems, function(v, i){
29873 if (v.rid != item.rid) {
29884 setActiveNext : function()
29886 var i = this.indexOfItem(this.getActive());
29888 if (i > this.barItems.length) {
29892 this.setActiveItem(this.barItems[i+1]);
29895 setActivePrev : function()
29897 var i = this.indexOfItem(this.getActive());
29903 this.setActiveItem(this.barItems[i-1]);
29906 format : function()
29908 if(!this.barItems.length){
29912 var width = 100 / this.barItems.length;
29914 Roo.each(this.barItems, function(i){
29915 i.el.setStyle('width', width + '%');
29916 i.topEl.el.setStyle('width', width + '%');
29917 i.bottomEl.el.setStyle('width', width + '%');
29926 * Nav Progress Item
29931 * @class Roo.bootstrap.NavProgressItem
29932 * @extends Roo.bootstrap.Component
29933 * Bootstrap NavProgressItem class
29934 * @cfg {String} rid the reference id
29935 * @cfg {Boolean} active (true|false) Is item active default false
29936 * @cfg {Boolean} disabled (true|false) Is item active default false
29937 * @cfg {String} html
29938 * @cfg {String} position (top|bottom) text position default bottom
29939 * @cfg {String} icon show icon instead of number
29942 * Create a new NavProgressItem
29943 * @param {Object} config The config object
29945 Roo.bootstrap.NavProgressItem = function(config){
29946 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29951 * The raw click event for the entire grid.
29952 * @param {Roo.bootstrap.NavProgressItem} this
29953 * @param {Roo.EventObject} e
29960 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29966 position : 'bottom',
29969 getAutoCreate : function()
29971 var iconCls = 'roo-navigation-bar-item-icon';
29973 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29977 cls: 'roo-navigation-bar-item',
29987 cfg.cls += ' active';
29990 cfg.cls += ' disabled';
29996 disable : function()
29998 this.setDisabled(true);
30001 enable : function()
30003 this.setDisabled(false);
30006 initEvents: function()
30008 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30010 this.iconEl.on('click', this.onClick, this);
30013 onClick : function(e)
30015 e.preventDefault();
30021 if(this.fireEvent('click', this, e) === false){
30025 this.parent().setActiveItem(this);
30028 isActive: function ()
30030 return this.active;
30033 setActive : function(state)
30035 if(this.active == state){
30039 this.active = state;
30042 this.el.addClass('active');
30046 this.el.removeClass('active');
30051 setDisabled : function(state)
30053 if(this.disabled == state){
30057 this.disabled = state;
30060 this.el.addClass('disabled');
30064 this.el.removeClass('disabled');
30067 tooltipEl : function()
30069 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30082 * @class Roo.bootstrap.FieldLabel
30083 * @extends Roo.bootstrap.Component
30084 * Bootstrap FieldLabel class
30085 * @cfg {String} html contents of the element
30086 * @cfg {String} tag tag of the element default label
30087 * @cfg {String} cls class of the element
30088 * @cfg {String} target label target
30089 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30090 * @cfg {String} invalidClass default "text-warning"
30091 * @cfg {String} validClass default "text-success"
30092 * @cfg {String} iconTooltip default "This field is required"
30093 * @cfg {String} indicatorpos (left|right) default left
30096 * Create a new FieldLabel
30097 * @param {Object} config The config object
30100 Roo.bootstrap.FieldLabel = function(config){
30101 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30106 * Fires after the field has been marked as invalid.
30107 * @param {Roo.form.FieldLabel} this
30108 * @param {String} msg The validation message
30113 * Fires after the field has been validated with no errors.
30114 * @param {Roo.form.FieldLabel} this
30120 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30127 invalidClass : 'has-warning',
30128 validClass : 'has-success',
30129 iconTooltip : 'This field is required',
30130 indicatorpos : 'left',
30132 getAutoCreate : function(){
30135 if (!this.allowBlank) {
30141 cls : 'roo-bootstrap-field-label ' + this.cls,
30146 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30147 tooltip : this.iconTooltip
30156 if(this.indicatorpos == 'right'){
30159 cls : 'roo-bootstrap-field-label ' + this.cls,
30168 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30169 tooltip : this.iconTooltip
30178 initEvents: function()
30180 Roo.bootstrap.Element.superclass.initEvents.call(this);
30182 this.indicator = this.indicatorEl();
30184 if(this.indicator){
30185 this.indicator.removeClass('visible');
30186 this.indicator.addClass('invisible');
30189 Roo.bootstrap.FieldLabel.register(this);
30192 indicatorEl : function()
30194 var indicator = this.el.select('i.roo-required-indicator',true).first();
30205 * Mark this field as valid
30207 markValid : function()
30209 if(this.indicator){
30210 this.indicator.removeClass('visible');
30211 this.indicator.addClass('invisible');
30214 this.el.removeClass(this.invalidClass);
30216 this.el.addClass(this.validClass);
30218 this.fireEvent('valid', this);
30222 * Mark this field as invalid
30223 * @param {String} msg The validation message
30225 markInvalid : function(msg)
30227 if(this.indicator){
30228 this.indicator.removeClass('invisible');
30229 this.indicator.addClass('visible');
30232 this.el.removeClass(this.validClass);
30234 this.el.addClass(this.invalidClass);
30236 this.fireEvent('invalid', this, msg);
30242 Roo.apply(Roo.bootstrap.FieldLabel, {
30247 * register a FieldLabel Group
30248 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30250 register : function(label)
30252 if(this.groups.hasOwnProperty(label.target)){
30256 this.groups[label.target] = label;
30260 * fetch a FieldLabel Group based on the target
30261 * @param {string} target
30262 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30264 get: function(target) {
30265 if (typeof(this.groups[target]) == 'undefined') {
30269 return this.groups[target] ;
30278 * page DateSplitField.
30284 * @class Roo.bootstrap.DateSplitField
30285 * @extends Roo.bootstrap.Component
30286 * Bootstrap DateSplitField class
30287 * @cfg {string} fieldLabel - the label associated
30288 * @cfg {Number} labelWidth set the width of label (0-12)
30289 * @cfg {String} labelAlign (top|left)
30290 * @cfg {Boolean} dayAllowBlank (true|false) default false
30291 * @cfg {Boolean} monthAllowBlank (true|false) default false
30292 * @cfg {Boolean} yearAllowBlank (true|false) default false
30293 * @cfg {string} dayPlaceholder
30294 * @cfg {string} monthPlaceholder
30295 * @cfg {string} yearPlaceholder
30296 * @cfg {string} dayFormat default 'd'
30297 * @cfg {string} monthFormat default 'm'
30298 * @cfg {string} yearFormat default 'Y'
30299 * @cfg {Number} labellg set the width of label (1-12)
30300 * @cfg {Number} labelmd set the width of label (1-12)
30301 * @cfg {Number} labelsm set the width of label (1-12)
30302 * @cfg {Number} labelxs set the width of label (1-12)
30306 * Create a new DateSplitField
30307 * @param {Object} config The config object
30310 Roo.bootstrap.DateSplitField = function(config){
30311 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30317 * getting the data of years
30318 * @param {Roo.bootstrap.DateSplitField} this
30319 * @param {Object} years
30324 * getting the data of days
30325 * @param {Roo.bootstrap.DateSplitField} this
30326 * @param {Object} days
30331 * Fires after the field has been marked as invalid.
30332 * @param {Roo.form.Field} this
30333 * @param {String} msg The validation message
30338 * Fires after the field has been validated with no errors.
30339 * @param {Roo.form.Field} this
30345 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30348 labelAlign : 'top',
30350 dayAllowBlank : false,
30351 monthAllowBlank : false,
30352 yearAllowBlank : false,
30353 dayPlaceholder : '',
30354 monthPlaceholder : '',
30355 yearPlaceholder : '',
30359 isFormField : true,
30365 getAutoCreate : function()
30369 cls : 'row roo-date-split-field-group',
30374 cls : 'form-hidden-field roo-date-split-field-group-value',
30380 var labelCls = 'col-md-12';
30381 var contentCls = 'col-md-4';
30383 if(this.fieldLabel){
30387 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30391 html : this.fieldLabel
30396 if(this.labelAlign == 'left'){
30398 if(this.labelWidth > 12){
30399 label.style = "width: " + this.labelWidth + 'px';
30402 if(this.labelWidth < 13 && this.labelmd == 0){
30403 this.labelmd = this.labelWidth;
30406 if(this.labellg > 0){
30407 labelCls = ' col-lg-' + this.labellg;
30408 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30411 if(this.labelmd > 0){
30412 labelCls = ' col-md-' + this.labelmd;
30413 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30416 if(this.labelsm > 0){
30417 labelCls = ' col-sm-' + this.labelsm;
30418 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30421 if(this.labelxs > 0){
30422 labelCls = ' col-xs-' + this.labelxs;
30423 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30427 label.cls += ' ' + labelCls;
30429 cfg.cn.push(label);
30432 Roo.each(['day', 'month', 'year'], function(t){
30435 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30442 inputEl: function ()
30444 return this.el.select('.roo-date-split-field-group-value', true).first();
30447 onRender : function(ct, position)
30451 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30453 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30455 this.dayField = new Roo.bootstrap.ComboBox({
30456 allowBlank : this.dayAllowBlank,
30457 alwaysQuery : true,
30458 displayField : 'value',
30461 forceSelection : true,
30463 placeholder : this.dayPlaceholder,
30464 selectOnFocus : true,
30465 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30466 triggerAction : 'all',
30468 valueField : 'value',
30469 store : new Roo.data.SimpleStore({
30470 data : (function() {
30472 _this.fireEvent('days', _this, days);
30475 fields : [ 'value' ]
30478 select : function (_self, record, index)
30480 _this.setValue(_this.getValue());
30485 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30487 this.monthField = new Roo.bootstrap.MonthField({
30488 after : '<i class=\"fa fa-calendar\"></i>',
30489 allowBlank : this.monthAllowBlank,
30490 placeholder : this.monthPlaceholder,
30493 render : function (_self)
30495 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30496 e.preventDefault();
30500 select : function (_self, oldvalue, newvalue)
30502 _this.setValue(_this.getValue());
30507 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30509 this.yearField = new Roo.bootstrap.ComboBox({
30510 allowBlank : this.yearAllowBlank,
30511 alwaysQuery : true,
30512 displayField : 'value',
30515 forceSelection : true,
30517 placeholder : this.yearPlaceholder,
30518 selectOnFocus : true,
30519 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30520 triggerAction : 'all',
30522 valueField : 'value',
30523 store : new Roo.data.SimpleStore({
30524 data : (function() {
30526 _this.fireEvent('years', _this, years);
30529 fields : [ 'value' ]
30532 select : function (_self, record, index)
30534 _this.setValue(_this.getValue());
30539 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30542 setValue : function(v, format)
30544 this.inputEl.dom.value = v;
30546 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30548 var d = Date.parseDate(v, f);
30555 this.setDay(d.format(this.dayFormat));
30556 this.setMonth(d.format(this.monthFormat));
30557 this.setYear(d.format(this.yearFormat));
30564 setDay : function(v)
30566 this.dayField.setValue(v);
30567 this.inputEl.dom.value = this.getValue();
30572 setMonth : function(v)
30574 this.monthField.setValue(v, true);
30575 this.inputEl.dom.value = this.getValue();
30580 setYear : function(v)
30582 this.yearField.setValue(v);
30583 this.inputEl.dom.value = this.getValue();
30588 getDay : function()
30590 return this.dayField.getValue();
30593 getMonth : function()
30595 return this.monthField.getValue();
30598 getYear : function()
30600 return this.yearField.getValue();
30603 getValue : function()
30605 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30607 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30617 this.inputEl.dom.value = '';
30622 validate : function()
30624 var d = this.dayField.validate();
30625 var m = this.monthField.validate();
30626 var y = this.yearField.validate();
30631 (!this.dayAllowBlank && !d) ||
30632 (!this.monthAllowBlank && !m) ||
30633 (!this.yearAllowBlank && !y)
30638 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30647 this.markInvalid();
30652 markValid : function()
30655 var label = this.el.select('label', true).first();
30656 var icon = this.el.select('i.fa-star', true).first();
30662 this.fireEvent('valid', this);
30666 * Mark this field as invalid
30667 * @param {String} msg The validation message
30669 markInvalid : function(msg)
30672 var label = this.el.select('label', true).first();
30673 var icon = this.el.select('i.fa-star', true).first();
30675 if(label && !icon){
30676 this.el.select('.roo-date-split-field-label', true).createChild({
30678 cls : 'text-danger fa fa-lg fa-star',
30679 tooltip : 'This field is required',
30680 style : 'margin-right:5px;'
30684 this.fireEvent('invalid', this, msg);
30687 clearInvalid : function()
30689 var label = this.el.select('label', true).first();
30690 var icon = this.el.select('i.fa-star', true).first();
30696 this.fireEvent('valid', this);
30699 getName: function()
30709 * http://masonry.desandro.com
30711 * The idea is to render all the bricks based on vertical width...
30713 * The original code extends 'outlayer' - we might need to use that....
30719 * @class Roo.bootstrap.LayoutMasonry
30720 * @extends Roo.bootstrap.Component
30721 * Bootstrap Layout Masonry class
30724 * Create a new Element
30725 * @param {Object} config The config object
30728 Roo.bootstrap.LayoutMasonry = function(config){
30730 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30734 Roo.bootstrap.LayoutMasonry.register(this);
30740 * Fire after layout the items
30741 * @param {Roo.bootstrap.LayoutMasonry} this
30742 * @param {Roo.EventObject} e
30749 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30752 * @cfg {Boolean} isLayoutInstant = no animation?
30754 isLayoutInstant : false, // needed?
30757 * @cfg {Number} boxWidth width of the columns
30762 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30767 * @cfg {Number} padWidth padding below box..
30772 * @cfg {Number} gutter gutter width..
30777 * @cfg {Number} maxCols maximum number of columns
30783 * @cfg {Boolean} isAutoInitial defalut true
30785 isAutoInitial : true,
30790 * @cfg {Boolean} isHorizontal defalut false
30792 isHorizontal : false,
30794 currentSize : null,
30800 bricks: null, //CompositeElement
30804 _isLayoutInited : false,
30806 // isAlternative : false, // only use for vertical layout...
30809 * @cfg {Number} alternativePadWidth padding below box..
30811 alternativePadWidth : 50,
30813 selectedBrick : [],
30815 getAutoCreate : function(){
30817 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30821 cls: 'blog-masonary-wrapper ' + this.cls,
30823 cls : 'mas-boxes masonary'
30830 getChildContainer: function( )
30832 if (this.boxesEl) {
30833 return this.boxesEl;
30836 this.boxesEl = this.el.select('.mas-boxes').first();
30838 return this.boxesEl;
30842 initEvents : function()
30846 if(this.isAutoInitial){
30847 Roo.log('hook children rendered');
30848 this.on('childrenrendered', function() {
30849 Roo.log('children rendered');
30855 initial : function()
30857 this.selectedBrick = [];
30859 this.currentSize = this.el.getBox(true);
30861 Roo.EventManager.onWindowResize(this.resize, this);
30863 if(!this.isAutoInitial){
30871 //this.layout.defer(500,this);
30875 resize : function()
30877 var cs = this.el.getBox(true);
30880 this.currentSize.width == cs.width &&
30881 this.currentSize.x == cs.x &&
30882 this.currentSize.height == cs.height &&
30883 this.currentSize.y == cs.y
30885 Roo.log("no change in with or X or Y");
30889 this.currentSize = cs;
30895 layout : function()
30897 this._resetLayout();
30899 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30901 this.layoutItems( isInstant );
30903 this._isLayoutInited = true;
30905 this.fireEvent('layout', this);
30909 _resetLayout : function()
30911 if(this.isHorizontal){
30912 this.horizontalMeasureColumns();
30916 this.verticalMeasureColumns();
30920 verticalMeasureColumns : function()
30922 this.getContainerWidth();
30924 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30925 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30929 var boxWidth = this.boxWidth + this.padWidth;
30931 if(this.containerWidth < this.boxWidth){
30932 boxWidth = this.containerWidth
30935 var containerWidth = this.containerWidth;
30937 var cols = Math.floor(containerWidth / boxWidth);
30939 this.cols = Math.max( cols, 1 );
30941 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30943 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30945 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30947 this.colWidth = boxWidth + avail - this.padWidth;
30949 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30950 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30953 horizontalMeasureColumns : function()
30955 this.getContainerWidth();
30957 var boxWidth = this.boxWidth;
30959 if(this.containerWidth < boxWidth){
30960 boxWidth = this.containerWidth;
30963 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30965 this.el.setHeight(boxWidth);
30969 getContainerWidth : function()
30971 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30974 layoutItems : function( isInstant )
30976 Roo.log(this.bricks);
30978 var items = Roo.apply([], this.bricks);
30980 if(this.isHorizontal){
30981 this._horizontalLayoutItems( items , isInstant );
30985 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30986 // this._verticalAlternativeLayoutItems( items , isInstant );
30990 this._verticalLayoutItems( items , isInstant );
30994 _verticalLayoutItems : function ( items , isInstant)
30996 if ( !items || !items.length ) {
31001 ['xs', 'xs', 'xs', 'tall'],
31002 ['xs', 'xs', 'tall'],
31003 ['xs', 'xs', 'sm'],
31004 ['xs', 'xs', 'xs'],
31010 ['sm', 'xs', 'xs'],
31014 ['tall', 'xs', 'xs', 'xs'],
31015 ['tall', 'xs', 'xs'],
31027 Roo.each(items, function(item, k){
31029 switch (item.size) {
31030 // these layouts take up a full box,
31041 boxes.push([item]);
31064 var filterPattern = function(box, length)
31072 var pattern = box.slice(0, length);
31076 Roo.each(pattern, function(i){
31077 format.push(i.size);
31080 Roo.each(standard, function(s){
31082 if(String(s) != String(format)){
31091 if(!match && length == 1){
31096 filterPattern(box, length - 1);
31100 queue.push(pattern);
31102 box = box.slice(length, box.length);
31104 filterPattern(box, 4);
31110 Roo.each(boxes, function(box, k){
31116 if(box.length == 1){
31121 filterPattern(box, 4);
31125 this._processVerticalLayoutQueue( queue, isInstant );
31129 // _verticalAlternativeLayoutItems : function( items , isInstant )
31131 // if ( !items || !items.length ) {
31135 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31139 _horizontalLayoutItems : function ( items , isInstant)
31141 if ( !items || !items.length || items.length < 3) {
31147 var eItems = items.slice(0, 3);
31149 items = items.slice(3, items.length);
31152 ['xs', 'xs', 'xs', 'wide'],
31153 ['xs', 'xs', 'wide'],
31154 ['xs', 'xs', 'sm'],
31155 ['xs', 'xs', 'xs'],
31161 ['sm', 'xs', 'xs'],
31165 ['wide', 'xs', 'xs', 'xs'],
31166 ['wide', 'xs', 'xs'],
31179 Roo.each(items, function(item, k){
31181 switch (item.size) {
31192 boxes.push([item]);
31216 var filterPattern = function(box, length)
31224 var pattern = box.slice(0, length);
31228 Roo.each(pattern, function(i){
31229 format.push(i.size);
31232 Roo.each(standard, function(s){
31234 if(String(s) != String(format)){
31243 if(!match && length == 1){
31248 filterPattern(box, length - 1);
31252 queue.push(pattern);
31254 box = box.slice(length, box.length);
31256 filterPattern(box, 4);
31262 Roo.each(boxes, function(box, k){
31268 if(box.length == 1){
31273 filterPattern(box, 4);
31280 var pos = this.el.getBox(true);
31284 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31286 var hit_end = false;
31288 Roo.each(queue, function(box){
31292 Roo.each(box, function(b){
31294 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31304 Roo.each(box, function(b){
31306 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31309 mx = Math.max(mx, b.x);
31313 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31317 Roo.each(box, function(b){
31319 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31333 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31336 /** Sets position of item in DOM
31337 * @param {Element} item
31338 * @param {Number} x - horizontal position
31339 * @param {Number} y - vertical position
31340 * @param {Boolean} isInstant - disables transitions
31342 _processVerticalLayoutQueue : function( queue, isInstant )
31344 var pos = this.el.getBox(true);
31349 for (var i = 0; i < this.cols; i++){
31353 Roo.each(queue, function(box, k){
31355 var col = k % this.cols;
31357 Roo.each(box, function(b,kk){
31359 b.el.position('absolute');
31361 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31362 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31364 if(b.size == 'md-left' || b.size == 'md-right'){
31365 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31366 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31369 b.el.setWidth(width);
31370 b.el.setHeight(height);
31372 b.el.select('iframe',true).setSize(width,height);
31376 for (var i = 0; i < this.cols; i++){
31378 if(maxY[i] < maxY[col]){
31383 col = Math.min(col, i);
31387 x = pos.x + col * (this.colWidth + this.padWidth);
31391 var positions = [];
31393 switch (box.length){
31395 positions = this.getVerticalOneBoxColPositions(x, y, box);
31398 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31401 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31404 positions = this.getVerticalFourBoxColPositions(x, y, box);
31410 Roo.each(box, function(b,kk){
31412 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31414 var sz = b.el.getSize();
31416 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31424 for (var i = 0; i < this.cols; i++){
31425 mY = Math.max(mY, maxY[i]);
31428 this.el.setHeight(mY - pos.y);
31432 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31434 // var pos = this.el.getBox(true);
31437 // var maxX = pos.right;
31439 // var maxHeight = 0;
31441 // Roo.each(items, function(item, k){
31445 // item.el.position('absolute');
31447 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31449 // item.el.setWidth(width);
31451 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31453 // item.el.setHeight(height);
31456 // item.el.setXY([x, y], isInstant ? false : true);
31458 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31461 // y = y + height + this.alternativePadWidth;
31463 // maxHeight = maxHeight + height + this.alternativePadWidth;
31467 // this.el.setHeight(maxHeight);
31471 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31473 var pos = this.el.getBox(true);
31478 var maxX = pos.right;
31480 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31482 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31484 Roo.each(queue, function(box, k){
31486 Roo.each(box, function(b, kk){
31488 b.el.position('absolute');
31490 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31491 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31493 if(b.size == 'md-left' || b.size == 'md-right'){
31494 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31495 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31498 b.el.setWidth(width);
31499 b.el.setHeight(height);
31507 var positions = [];
31509 switch (box.length){
31511 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31514 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31517 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31520 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31526 Roo.each(box, function(b,kk){
31528 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31530 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31538 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31540 Roo.each(eItems, function(b,k){
31542 b.size = (k == 0) ? 'sm' : 'xs';
31543 b.x = (k == 0) ? 2 : 1;
31544 b.y = (k == 0) ? 2 : 1;
31546 b.el.position('absolute');
31548 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31550 b.el.setWidth(width);
31552 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31554 b.el.setHeight(height);
31558 var positions = [];
31561 x : maxX - this.unitWidth * 2 - this.gutter,
31566 x : maxX - this.unitWidth,
31567 y : minY + (this.unitWidth + this.gutter) * 2
31571 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31575 Roo.each(eItems, function(b,k){
31577 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31583 getVerticalOneBoxColPositions : function(x, y, box)
31587 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31589 if(box[0].size == 'md-left'){
31593 if(box[0].size == 'md-right'){
31598 x : x + (this.unitWidth + this.gutter) * rand,
31605 getVerticalTwoBoxColPositions : function(x, y, box)
31609 if(box[0].size == 'xs'){
31613 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31617 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31631 x : x + (this.unitWidth + this.gutter) * 2,
31632 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31639 getVerticalThreeBoxColPositions : function(x, y, box)
31643 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31651 x : x + (this.unitWidth + this.gutter) * 1,
31656 x : x + (this.unitWidth + this.gutter) * 2,
31664 if(box[0].size == 'xs' && box[1].size == 'xs'){
31673 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31677 x : x + (this.unitWidth + this.gutter) * 1,
31691 x : x + (this.unitWidth + this.gutter) * 2,
31696 x : x + (this.unitWidth + this.gutter) * 2,
31697 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31704 getVerticalFourBoxColPositions : function(x, y, box)
31708 if(box[0].size == 'xs'){
31717 y : y + (this.unitHeight + this.gutter) * 1
31722 y : y + (this.unitHeight + this.gutter) * 2
31726 x : x + (this.unitWidth + this.gutter) * 1,
31740 x : x + (this.unitWidth + this.gutter) * 2,
31745 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31746 y : y + (this.unitHeight + this.gutter) * 1
31750 x : x + (this.unitWidth + this.gutter) * 2,
31751 y : y + (this.unitWidth + this.gutter) * 2
31758 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31762 if(box[0].size == 'md-left'){
31764 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31771 if(box[0].size == 'md-right'){
31773 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31774 y : minY + (this.unitWidth + this.gutter) * 1
31780 var rand = Math.floor(Math.random() * (4 - box[0].y));
31783 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31784 y : minY + (this.unitWidth + this.gutter) * rand
31791 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31795 if(box[0].size == 'xs'){
31798 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31803 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31804 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31812 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31817 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31818 y : minY + (this.unitWidth + this.gutter) * 2
31825 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31829 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31832 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31837 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31838 y : minY + (this.unitWidth + this.gutter) * 1
31842 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31843 y : minY + (this.unitWidth + this.gutter) * 2
31850 if(box[0].size == 'xs' && box[1].size == 'xs'){
31853 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31858 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31863 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31864 y : minY + (this.unitWidth + this.gutter) * 1
31872 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31877 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31878 y : minY + (this.unitWidth + this.gutter) * 2
31882 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31883 y : minY + (this.unitWidth + this.gutter) * 2
31890 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31894 if(box[0].size == 'xs'){
31897 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31902 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31907 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),
31912 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31913 y : minY + (this.unitWidth + this.gutter) * 1
31921 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31926 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31927 y : minY + (this.unitWidth + this.gutter) * 2
31931 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31932 y : minY + (this.unitWidth + this.gutter) * 2
31936 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),
31937 y : minY + (this.unitWidth + this.gutter) * 2
31945 * remove a Masonry Brick
31946 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31948 removeBrick : function(brick_id)
31954 for (var i = 0; i<this.bricks.length; i++) {
31955 if (this.bricks[i].id == brick_id) {
31956 this.bricks.splice(i,1);
31957 this.el.dom.removeChild(Roo.get(brick_id).dom);
31964 * adds a Masonry Brick
31965 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31967 addBrick : function(cfg)
31969 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31970 //this.register(cn);
31971 cn.parentId = this.id;
31972 cn.onRender(this.el, null);
31977 * register a Masonry Brick
31978 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31981 register : function(brick)
31983 this.bricks.push(brick);
31984 brick.masonryId = this.id;
31988 * clear all the Masonry Brick
31990 clearAll : function()
31993 //this.getChildContainer().dom.innerHTML = "";
31994 this.el.dom.innerHTML = '';
31997 getSelected : function()
31999 if (!this.selectedBrick) {
32003 return this.selectedBrick;
32007 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32011 * register a Masonry Layout
32012 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32015 register : function(layout)
32017 this.groups[layout.id] = layout;
32020 * fetch a Masonry Layout based on the masonry layout ID
32021 * @param {string} the masonry layout to add
32022 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32025 get: function(layout_id) {
32026 if (typeof(this.groups[layout_id]) == 'undefined') {
32029 return this.groups[layout_id] ;
32041 * http://masonry.desandro.com
32043 * The idea is to render all the bricks based on vertical width...
32045 * The original code extends 'outlayer' - we might need to use that....
32051 * @class Roo.bootstrap.LayoutMasonryAuto
32052 * @extends Roo.bootstrap.Component
32053 * Bootstrap Layout Masonry class
32056 * Create a new Element
32057 * @param {Object} config The config object
32060 Roo.bootstrap.LayoutMasonryAuto = function(config){
32061 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32064 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32067 * @cfg {Boolean} isFitWidth - resize the width..
32069 isFitWidth : false, // options..
32071 * @cfg {Boolean} isOriginLeft = left align?
32073 isOriginLeft : true,
32075 * @cfg {Boolean} isOriginTop = top align?
32077 isOriginTop : false,
32079 * @cfg {Boolean} isLayoutInstant = no animation?
32081 isLayoutInstant : false, // needed?
32083 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32085 isResizingContainer : true,
32087 * @cfg {Number} columnWidth width of the columns
32093 * @cfg {Number} maxCols maximum number of columns
32098 * @cfg {Number} padHeight padding below box..
32104 * @cfg {Boolean} isAutoInitial defalut true
32107 isAutoInitial : true,
32113 initialColumnWidth : 0,
32114 currentSize : null,
32116 colYs : null, // array.
32123 bricks: null, //CompositeElement
32124 cols : 0, // array?
32125 // element : null, // wrapped now this.el
32126 _isLayoutInited : null,
32129 getAutoCreate : function(){
32133 cls: 'blog-masonary-wrapper ' + this.cls,
32135 cls : 'mas-boxes masonary'
32142 getChildContainer: function( )
32144 if (this.boxesEl) {
32145 return this.boxesEl;
32148 this.boxesEl = this.el.select('.mas-boxes').first();
32150 return this.boxesEl;
32154 initEvents : function()
32158 if(this.isAutoInitial){
32159 Roo.log('hook children rendered');
32160 this.on('childrenrendered', function() {
32161 Roo.log('children rendered');
32168 initial : function()
32170 this.reloadItems();
32172 this.currentSize = this.el.getBox(true);
32174 /// was window resize... - let's see if this works..
32175 Roo.EventManager.onWindowResize(this.resize, this);
32177 if(!this.isAutoInitial){
32182 this.layout.defer(500,this);
32185 reloadItems: function()
32187 this.bricks = this.el.select('.masonry-brick', true);
32189 this.bricks.each(function(b) {
32190 //Roo.log(b.getSize());
32191 if (!b.attr('originalwidth')) {
32192 b.attr('originalwidth', b.getSize().width);
32197 Roo.log(this.bricks.elements.length);
32200 resize : function()
32203 var cs = this.el.getBox(true);
32205 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32206 Roo.log("no change in with or X");
32209 this.currentSize = cs;
32213 layout : function()
32216 this._resetLayout();
32217 //this._manageStamps();
32219 // don't animate first layout
32220 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32221 this.layoutItems( isInstant );
32223 // flag for initalized
32224 this._isLayoutInited = true;
32227 layoutItems : function( isInstant )
32229 //var items = this._getItemsForLayout( this.items );
32230 // original code supports filtering layout items.. we just ignore it..
32232 this._layoutItems( this.bricks , isInstant );
32234 this._postLayout();
32236 _layoutItems : function ( items , isInstant)
32238 //this.fireEvent( 'layout', this, items );
32241 if ( !items || !items.elements.length ) {
32242 // no items, emit event with empty array
32247 items.each(function(item) {
32248 Roo.log("layout item");
32250 // get x/y object from method
32251 var position = this._getItemLayoutPosition( item );
32253 position.item = item;
32254 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32255 queue.push( position );
32258 this._processLayoutQueue( queue );
32260 /** Sets position of item in DOM
32261 * @param {Element} item
32262 * @param {Number} x - horizontal position
32263 * @param {Number} y - vertical position
32264 * @param {Boolean} isInstant - disables transitions
32266 _processLayoutQueue : function( queue )
32268 for ( var i=0, len = queue.length; i < len; i++ ) {
32269 var obj = queue[i];
32270 obj.item.position('absolute');
32271 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32277 * Any logic you want to do after each layout,
32278 * i.e. size the container
32280 _postLayout : function()
32282 this.resizeContainer();
32285 resizeContainer : function()
32287 if ( !this.isResizingContainer ) {
32290 var size = this._getContainerSize();
32292 this.el.setSize(size.width,size.height);
32293 this.boxesEl.setSize(size.width,size.height);
32299 _resetLayout : function()
32301 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32302 this.colWidth = this.el.getWidth();
32303 //this.gutter = this.el.getWidth();
32305 this.measureColumns();
32311 this.colYs.push( 0 );
32317 measureColumns : function()
32319 this.getContainerWidth();
32320 // if columnWidth is 0, default to outerWidth of first item
32321 if ( !this.columnWidth ) {
32322 var firstItem = this.bricks.first();
32323 Roo.log(firstItem);
32324 this.columnWidth = this.containerWidth;
32325 if (firstItem && firstItem.attr('originalwidth') ) {
32326 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32328 // columnWidth fall back to item of first element
32329 Roo.log("set column width?");
32330 this.initialColumnWidth = this.columnWidth ;
32332 // if first elem has no width, default to size of container
32337 if (this.initialColumnWidth) {
32338 this.columnWidth = this.initialColumnWidth;
32343 // column width is fixed at the top - however if container width get's smaller we should
32346 // this bit calcs how man columns..
32348 var columnWidth = this.columnWidth += this.gutter;
32350 // calculate columns
32351 var containerWidth = this.containerWidth + this.gutter;
32353 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32354 // fix rounding errors, typically with gutters
32355 var excess = columnWidth - containerWidth % columnWidth;
32358 // if overshoot is less than a pixel, round up, otherwise floor it
32359 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32360 cols = Math[ mathMethod ]( cols );
32361 this.cols = Math.max( cols, 1 );
32362 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32364 // padding positioning..
32365 var totalColWidth = this.cols * this.columnWidth;
32366 var padavail = this.containerWidth - totalColWidth;
32367 // so for 2 columns - we need 3 'pads'
32369 var padNeeded = (1+this.cols) * this.padWidth;
32371 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32373 this.columnWidth += padExtra
32374 //this.padWidth = Math.floor(padavail / ( this.cols));
32376 // adjust colum width so that padding is fixed??
32378 // we have 3 columns ... total = width * 3
32379 // we have X left over... that should be used by
32381 //if (this.expandC) {
32389 getContainerWidth : function()
32391 /* // container is parent if fit width
32392 var container = this.isFitWidth ? this.element.parentNode : this.element;
32393 // check that this.size and size are there
32394 // IE8 triggers resize on body size change, so they might not be
32396 var size = getSize( container ); //FIXME
32397 this.containerWidth = size && size.innerWidth; //FIXME
32400 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32404 _getItemLayoutPosition : function( item ) // what is item?
32406 // we resize the item to our columnWidth..
32408 item.setWidth(this.columnWidth);
32409 item.autoBoxAdjust = false;
32411 var sz = item.getSize();
32413 // how many columns does this brick span
32414 var remainder = this.containerWidth % this.columnWidth;
32416 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32417 // round if off by 1 pixel, otherwise use ceil
32418 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32419 colSpan = Math.min( colSpan, this.cols );
32421 // normally this should be '1' as we dont' currently allow multi width columns..
32423 var colGroup = this._getColGroup( colSpan );
32424 // get the minimum Y value from the columns
32425 var minimumY = Math.min.apply( Math, colGroup );
32426 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32428 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32430 // position the brick
32432 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32433 y: this.currentSize.y + minimumY + this.padHeight
32437 // apply setHeight to necessary columns
32438 var setHeight = minimumY + sz.height + this.padHeight;
32439 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32441 var setSpan = this.cols + 1 - colGroup.length;
32442 for ( var i = 0; i < setSpan; i++ ) {
32443 this.colYs[ shortColIndex + i ] = setHeight ;
32450 * @param {Number} colSpan - number of columns the element spans
32451 * @returns {Array} colGroup
32453 _getColGroup : function( colSpan )
32455 if ( colSpan < 2 ) {
32456 // if brick spans only one column, use all the column Ys
32461 // how many different places could this brick fit horizontally
32462 var groupCount = this.cols + 1 - colSpan;
32463 // for each group potential horizontal position
32464 for ( var i = 0; i < groupCount; i++ ) {
32465 // make an array of colY values for that one group
32466 var groupColYs = this.colYs.slice( i, i + colSpan );
32467 // and get the max value of the array
32468 colGroup[i] = Math.max.apply( Math, groupColYs );
32473 _manageStamp : function( stamp )
32475 var stampSize = stamp.getSize();
32476 var offset = stamp.getBox();
32477 // get the columns that this stamp affects
32478 var firstX = this.isOriginLeft ? offset.x : offset.right;
32479 var lastX = firstX + stampSize.width;
32480 var firstCol = Math.floor( firstX / this.columnWidth );
32481 firstCol = Math.max( 0, firstCol );
32483 var lastCol = Math.floor( lastX / this.columnWidth );
32484 // lastCol should not go over if multiple of columnWidth #425
32485 lastCol -= lastX % this.columnWidth ? 0 : 1;
32486 lastCol = Math.min( this.cols - 1, lastCol );
32488 // set colYs to bottom of the stamp
32489 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32492 for ( var i = firstCol; i <= lastCol; i++ ) {
32493 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32498 _getContainerSize : function()
32500 this.maxY = Math.max.apply( Math, this.colYs );
32505 if ( this.isFitWidth ) {
32506 size.width = this._getContainerFitWidth();
32512 _getContainerFitWidth : function()
32514 var unusedCols = 0;
32515 // count unused columns
32518 if ( this.colYs[i] !== 0 ) {
32523 // fit container to columns that have been used
32524 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32527 needsResizeLayout : function()
32529 var previousWidth = this.containerWidth;
32530 this.getContainerWidth();
32531 return previousWidth !== this.containerWidth;
32546 * @class Roo.bootstrap.MasonryBrick
32547 * @extends Roo.bootstrap.Component
32548 * Bootstrap MasonryBrick class
32551 * Create a new MasonryBrick
32552 * @param {Object} config The config object
32555 Roo.bootstrap.MasonryBrick = function(config){
32557 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32559 Roo.bootstrap.MasonryBrick.register(this);
32565 * When a MasonryBrick is clcik
32566 * @param {Roo.bootstrap.MasonryBrick} this
32567 * @param {Roo.EventObject} e
32573 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32576 * @cfg {String} title
32580 * @cfg {String} html
32584 * @cfg {String} bgimage
32588 * @cfg {String} videourl
32592 * @cfg {String} cls
32596 * @cfg {String} href
32600 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32605 * @cfg {String} placetitle (center|bottom)
32610 * @cfg {Boolean} isFitContainer defalut true
32612 isFitContainer : true,
32615 * @cfg {Boolean} preventDefault defalut false
32617 preventDefault : false,
32620 * @cfg {Boolean} inverse defalut false
32622 maskInverse : false,
32624 getAutoCreate : function()
32626 if(!this.isFitContainer){
32627 return this.getSplitAutoCreate();
32630 var cls = 'masonry-brick masonry-brick-full';
32632 if(this.href.length){
32633 cls += ' masonry-brick-link';
32636 if(this.bgimage.length){
32637 cls += ' masonry-brick-image';
32640 if(this.maskInverse){
32641 cls += ' mask-inverse';
32644 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32645 cls += ' enable-mask';
32649 cls += ' masonry-' + this.size + '-brick';
32652 if(this.placetitle.length){
32654 switch (this.placetitle) {
32656 cls += ' masonry-center-title';
32659 cls += ' masonry-bottom-title';
32666 if(!this.html.length && !this.bgimage.length){
32667 cls += ' masonry-center-title';
32670 if(!this.html.length && this.bgimage.length){
32671 cls += ' masonry-bottom-title';
32676 cls += ' ' + this.cls;
32680 tag: (this.href.length) ? 'a' : 'div',
32685 cls: 'masonry-brick-mask'
32689 cls: 'masonry-brick-paragraph',
32695 if(this.href.length){
32696 cfg.href = this.href;
32699 var cn = cfg.cn[1].cn;
32701 if(this.title.length){
32704 cls: 'masonry-brick-title',
32709 if(this.html.length){
32712 cls: 'masonry-brick-text',
32717 if (!this.title.length && !this.html.length) {
32718 cfg.cn[1].cls += ' hide';
32721 if(this.bgimage.length){
32724 cls: 'masonry-brick-image-view',
32729 if(this.videourl.length){
32730 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32731 // youtube support only?
32734 cls: 'masonry-brick-image-view',
32737 allowfullscreen : true
32745 getSplitAutoCreate : function()
32747 var cls = 'masonry-brick masonry-brick-split';
32749 if(this.href.length){
32750 cls += ' masonry-brick-link';
32753 if(this.bgimage.length){
32754 cls += ' masonry-brick-image';
32758 cls += ' masonry-' + this.size + '-brick';
32761 switch (this.placetitle) {
32763 cls += ' masonry-center-title';
32766 cls += ' masonry-bottom-title';
32769 if(!this.bgimage.length){
32770 cls += ' masonry-center-title';
32773 if(this.bgimage.length){
32774 cls += ' masonry-bottom-title';
32780 cls += ' ' + this.cls;
32784 tag: (this.href.length) ? 'a' : 'div',
32789 cls: 'masonry-brick-split-head',
32793 cls: 'masonry-brick-paragraph',
32800 cls: 'masonry-brick-split-body',
32806 if(this.href.length){
32807 cfg.href = this.href;
32810 if(this.title.length){
32811 cfg.cn[0].cn[0].cn.push({
32813 cls: 'masonry-brick-title',
32818 if(this.html.length){
32819 cfg.cn[1].cn.push({
32821 cls: 'masonry-brick-text',
32826 if(this.bgimage.length){
32827 cfg.cn[0].cn.push({
32829 cls: 'masonry-brick-image-view',
32834 if(this.videourl.length){
32835 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32836 // youtube support only?
32837 cfg.cn[0].cn.cn.push({
32839 cls: 'masonry-brick-image-view',
32842 allowfullscreen : true
32849 initEvents: function()
32851 switch (this.size) {
32884 this.el.on('touchstart', this.onTouchStart, this);
32885 this.el.on('touchmove', this.onTouchMove, this);
32886 this.el.on('touchend', this.onTouchEnd, this);
32887 this.el.on('contextmenu', this.onContextMenu, this);
32889 this.el.on('mouseenter' ,this.enter, this);
32890 this.el.on('mouseleave', this.leave, this);
32891 this.el.on('click', this.onClick, this);
32894 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32895 this.parent().bricks.push(this);
32900 onClick: function(e, el)
32902 var time = this.endTimer - this.startTimer;
32903 // Roo.log(e.preventDefault());
32906 e.preventDefault();
32911 if(!this.preventDefault){
32915 e.preventDefault();
32917 if (this.activeClass != '') {
32918 this.selectBrick();
32921 this.fireEvent('click', this, e);
32924 enter: function(e, el)
32926 e.preventDefault();
32928 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32932 if(this.bgimage.length && this.html.length){
32933 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32937 leave: function(e, el)
32939 e.preventDefault();
32941 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32945 if(this.bgimage.length && this.html.length){
32946 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32950 onTouchStart: function(e, el)
32952 // e.preventDefault();
32954 this.touchmoved = false;
32956 if(!this.isFitContainer){
32960 if(!this.bgimage.length || !this.html.length){
32964 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32966 this.timer = new Date().getTime();
32970 onTouchMove: function(e, el)
32972 this.touchmoved = true;
32975 onContextMenu : function(e,el)
32977 e.preventDefault();
32978 e.stopPropagation();
32982 onTouchEnd: function(e, el)
32984 // e.preventDefault();
32986 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32993 if(!this.bgimage.length || !this.html.length){
32995 if(this.href.length){
32996 window.location.href = this.href;
33002 if(!this.isFitContainer){
33006 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33008 window.location.href = this.href;
33011 //selection on single brick only
33012 selectBrick : function() {
33014 if (!this.parentId) {
33018 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33019 var index = m.selectedBrick.indexOf(this.id);
33022 m.selectedBrick.splice(index,1);
33023 this.el.removeClass(this.activeClass);
33027 for(var i = 0; i < m.selectedBrick.length; i++) {
33028 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33029 b.el.removeClass(b.activeClass);
33032 m.selectedBrick = [];
33034 m.selectedBrick.push(this.id);
33035 this.el.addClass(this.activeClass);
33039 isSelected : function(){
33040 return this.el.hasClass(this.activeClass);
33045 Roo.apply(Roo.bootstrap.MasonryBrick, {
33048 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33050 * register a Masonry Brick
33051 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33054 register : function(brick)
33056 //this.groups[brick.id] = brick;
33057 this.groups.add(brick.id, brick);
33060 * fetch a masonry brick based on the masonry brick ID
33061 * @param {string} the masonry brick to add
33062 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33065 get: function(brick_id)
33067 // if (typeof(this.groups[brick_id]) == 'undefined') {
33070 // return this.groups[brick_id] ;
33072 if(this.groups.key(brick_id)) {
33073 return this.groups.key(brick_id);
33091 * @class Roo.bootstrap.Brick
33092 * @extends Roo.bootstrap.Component
33093 * Bootstrap Brick class
33096 * Create a new Brick
33097 * @param {Object} config The config object
33100 Roo.bootstrap.Brick = function(config){
33101 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33107 * When a Brick is click
33108 * @param {Roo.bootstrap.Brick} this
33109 * @param {Roo.EventObject} e
33115 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33118 * @cfg {String} title
33122 * @cfg {String} html
33126 * @cfg {String} bgimage
33130 * @cfg {String} cls
33134 * @cfg {String} href
33138 * @cfg {String} video
33142 * @cfg {Boolean} square
33146 getAutoCreate : function()
33148 var cls = 'roo-brick';
33150 if(this.href.length){
33151 cls += ' roo-brick-link';
33154 if(this.bgimage.length){
33155 cls += ' roo-brick-image';
33158 if(!this.html.length && !this.bgimage.length){
33159 cls += ' roo-brick-center-title';
33162 if(!this.html.length && this.bgimage.length){
33163 cls += ' roo-brick-bottom-title';
33167 cls += ' ' + this.cls;
33171 tag: (this.href.length) ? 'a' : 'div',
33176 cls: 'roo-brick-paragraph',
33182 if(this.href.length){
33183 cfg.href = this.href;
33186 var cn = cfg.cn[0].cn;
33188 if(this.title.length){
33191 cls: 'roo-brick-title',
33196 if(this.html.length){
33199 cls: 'roo-brick-text',
33206 if(this.bgimage.length){
33209 cls: 'roo-brick-image-view',
33217 initEvents: function()
33219 if(this.title.length || this.html.length){
33220 this.el.on('mouseenter' ,this.enter, this);
33221 this.el.on('mouseleave', this.leave, this);
33224 Roo.EventManager.onWindowResize(this.resize, this);
33226 if(this.bgimage.length){
33227 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33228 this.imageEl.on('load', this.onImageLoad, this);
33235 onImageLoad : function()
33240 resize : function()
33242 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33244 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33246 if(this.bgimage.length){
33247 var image = this.el.select('.roo-brick-image-view', true).first();
33249 image.setWidth(paragraph.getWidth());
33252 image.setHeight(paragraph.getWidth());
33255 this.el.setHeight(image.getHeight());
33256 paragraph.setHeight(image.getHeight());
33262 enter: function(e, el)
33264 e.preventDefault();
33266 if(this.bgimage.length){
33267 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33268 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33272 leave: function(e, el)
33274 e.preventDefault();
33276 if(this.bgimage.length){
33277 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33278 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33293 * @class Roo.bootstrap.NumberField
33294 * @extends Roo.bootstrap.Input
33295 * Bootstrap NumberField class
33301 * Create a new NumberField
33302 * @param {Object} config The config object
33305 Roo.bootstrap.NumberField = function(config){
33306 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33309 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33312 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33314 allowDecimals : true,
33316 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33318 decimalSeparator : ".",
33320 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33322 decimalPrecision : 2,
33324 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33326 allowNegative : true,
33329 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33333 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33335 minValue : Number.NEGATIVE_INFINITY,
33337 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33339 maxValue : Number.MAX_VALUE,
33341 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33343 minText : "The minimum value for this field is {0}",
33345 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33347 maxText : "The maximum value for this field is {0}",
33349 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33350 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33352 nanText : "{0} is not a valid number",
33354 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33356 thousandsDelimiter : false,
33358 * @cfg {String} valueAlign alignment of value
33360 valueAlign : "left",
33362 getAutoCreate : function()
33364 var hiddenInput = {
33368 cls: 'hidden-number-input'
33372 hiddenInput.name = this.name;
33377 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33379 this.name = hiddenInput.name;
33381 if(cfg.cn.length > 0) {
33382 cfg.cn.push(hiddenInput);
33389 initEvents : function()
33391 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33393 var allowed = "0123456789";
33395 if(this.allowDecimals){
33396 allowed += this.decimalSeparator;
33399 if(this.allowNegative){
33403 if(this.thousandsDelimiter) {
33407 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33409 var keyPress = function(e){
33411 var k = e.getKey();
33413 var c = e.getCharCode();
33416 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33417 allowed.indexOf(String.fromCharCode(c)) === -1
33423 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33427 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33432 this.el.on("keypress", keyPress, this);
33435 validateValue : function(value)
33438 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33442 var num = this.parseValue(value);
33445 this.markInvalid(String.format(this.nanText, value));
33449 if(num < this.minValue){
33450 this.markInvalid(String.format(this.minText, this.minValue));
33454 if(num > this.maxValue){
33455 this.markInvalid(String.format(this.maxText, this.maxValue));
33462 getValue : function()
33464 var v = this.hiddenEl().getValue();
33466 return this.fixPrecision(this.parseValue(v));
33469 parseValue : function(value)
33471 if(this.thousandsDelimiter) {
33473 r = new RegExp(",", "g");
33474 value = value.replace(r, "");
33477 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33478 return isNaN(value) ? '' : value;
33481 fixPrecision : function(value)
33483 if(this.thousandsDelimiter) {
33485 r = new RegExp(",", "g");
33486 value = value.replace(r, "");
33489 var nan = isNaN(value);
33491 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33492 return nan ? '' : value;
33494 return parseFloat(value).toFixed(this.decimalPrecision);
33497 setValue : function(v)
33499 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33505 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33507 this.inputEl().dom.value = (v == '') ? '' :
33508 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33510 if(!this.allowZero && v === '0') {
33511 this.hiddenEl().dom.value = '';
33512 this.inputEl().dom.value = '';
33519 decimalPrecisionFcn : function(v)
33521 return Math.floor(v);
33524 beforeBlur : function()
33526 var v = this.parseValue(this.getRawValue());
33528 if(v || v === 0 || v === ''){
33533 hiddenEl : function()
33535 return this.el.select('input.hidden-number-input',true).first();
33547 * @class Roo.bootstrap.DocumentSlider
33548 * @extends Roo.bootstrap.Component
33549 * Bootstrap DocumentSlider class
33552 * Create a new DocumentViewer
33553 * @param {Object} config The config object
33556 Roo.bootstrap.DocumentSlider = function(config){
33557 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33564 * Fire after initEvent
33565 * @param {Roo.bootstrap.DocumentSlider} this
33570 * Fire after update
33571 * @param {Roo.bootstrap.DocumentSlider} this
33577 * @param {Roo.bootstrap.DocumentSlider} this
33583 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33589 getAutoCreate : function()
33593 cls : 'roo-document-slider',
33597 cls : 'roo-document-slider-header',
33601 cls : 'roo-document-slider-header-title'
33607 cls : 'roo-document-slider-body',
33611 cls : 'roo-document-slider-prev',
33615 cls : 'fa fa-chevron-left'
33621 cls : 'roo-document-slider-thumb',
33625 cls : 'roo-document-slider-image'
33631 cls : 'roo-document-slider-next',
33635 cls : 'fa fa-chevron-right'
33647 initEvents : function()
33649 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33650 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33652 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33653 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33655 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33656 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33658 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33659 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33661 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33662 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33664 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33665 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33667 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33668 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33670 this.thumbEl.on('click', this.onClick, this);
33672 this.prevIndicator.on('click', this.prev, this);
33674 this.nextIndicator.on('click', this.next, this);
33678 initial : function()
33680 if(this.files.length){
33681 this.indicator = 1;
33685 this.fireEvent('initial', this);
33688 update : function()
33690 this.imageEl.attr('src', this.files[this.indicator - 1]);
33692 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33694 this.prevIndicator.show();
33696 if(this.indicator == 1){
33697 this.prevIndicator.hide();
33700 this.nextIndicator.show();
33702 if(this.indicator == this.files.length){
33703 this.nextIndicator.hide();
33706 this.thumbEl.scrollTo('top');
33708 this.fireEvent('update', this);
33711 onClick : function(e)
33713 e.preventDefault();
33715 this.fireEvent('click', this);
33720 e.preventDefault();
33722 this.indicator = Math.max(1, this.indicator - 1);
33729 e.preventDefault();
33731 this.indicator = Math.min(this.files.length, this.indicator + 1);
33745 * @class Roo.bootstrap.RadioSet
33746 * @extends Roo.bootstrap.Input
33747 * Bootstrap RadioSet class
33748 * @cfg {String} indicatorpos (left|right) default left
33749 * @cfg {Boolean} inline (true|false) inline the element (default true)
33750 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33752 * Create a new RadioSet
33753 * @param {Object} config The config object
33756 Roo.bootstrap.RadioSet = function(config){
33758 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33762 Roo.bootstrap.RadioSet.register(this);
33767 * Fires when the element is checked or unchecked.
33768 * @param {Roo.bootstrap.RadioSet} this This radio
33769 * @param {Roo.bootstrap.Radio} item The checked item
33774 * Fires when the element is click.
33775 * @param {Roo.bootstrap.RadioSet} this This radio set
33776 * @param {Roo.bootstrap.Radio} item The checked item
33777 * @param {Roo.EventObject} e The event object
33784 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33792 indicatorpos : 'left',
33794 getAutoCreate : function()
33798 cls : 'roo-radio-set-label',
33802 html : this.fieldLabel
33807 if(this.indicatorpos == 'left'){
33810 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33811 tooltip : 'This field is required'
33816 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33817 tooltip : 'This field is required'
33823 cls : 'roo-radio-set-items'
33826 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33828 if (align === 'left' && this.fieldLabel.length) {
33831 cls : "roo-radio-set-right",
33837 if(this.labelWidth > 12){
33838 label.style = "width: " + this.labelWidth + 'px';
33841 if(this.labelWidth < 13 && this.labelmd == 0){
33842 this.labelmd = this.labelWidth;
33845 if(this.labellg > 0){
33846 label.cls += ' col-lg-' + this.labellg;
33847 items.cls += ' col-lg-' + (12 - this.labellg);
33850 if(this.labelmd > 0){
33851 label.cls += ' col-md-' + this.labelmd;
33852 items.cls += ' col-md-' + (12 - this.labelmd);
33855 if(this.labelsm > 0){
33856 label.cls += ' col-sm-' + this.labelsm;
33857 items.cls += ' col-sm-' + (12 - this.labelsm);
33860 if(this.labelxs > 0){
33861 label.cls += ' col-xs-' + this.labelxs;
33862 items.cls += ' col-xs-' + (12 - this.labelxs);
33868 cls : 'roo-radio-set',
33872 cls : 'roo-radio-set-input',
33875 value : this.value ? this.value : ''
33882 if(this.weight.length){
33883 cfg.cls += ' roo-radio-' + this.weight;
33887 cfg.cls += ' roo-radio-set-inline';
33891 ['xs','sm','md','lg'].map(function(size){
33892 if (settings[size]) {
33893 cfg.cls += ' col-' + size + '-' + settings[size];
33901 initEvents : function()
33903 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33904 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33906 if(!this.fieldLabel.length){
33907 this.labelEl.hide();
33910 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33911 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33913 this.indicator = this.indicatorEl();
33915 if(this.indicator){
33916 this.indicator.addClass('invisible');
33919 this.originalValue = this.getValue();
33923 inputEl: function ()
33925 return this.el.select('.roo-radio-set-input', true).first();
33928 getChildContainer : function()
33930 return this.itemsEl;
33933 register : function(item)
33935 this.radioes.push(item);
33939 validate : function()
33941 if(this.getVisibilityEl().hasClass('hidden')){
33947 Roo.each(this.radioes, function(i){
33956 if(this.allowBlank) {
33960 if(this.disabled || valid){
33965 this.markInvalid();
33970 markValid : function()
33972 if(this.labelEl.isVisible(true)){
33973 this.indicatorEl().removeClass('visible');
33974 this.indicatorEl().addClass('invisible');
33977 this.el.removeClass([this.invalidClass, this.validClass]);
33978 this.el.addClass(this.validClass);
33980 this.fireEvent('valid', this);
33983 markInvalid : function(msg)
33985 if(this.allowBlank || this.disabled){
33989 if(this.labelEl.isVisible(true)){
33990 this.indicatorEl().removeClass('invisible');
33991 this.indicatorEl().addClass('visible');
33994 this.el.removeClass([this.invalidClass, this.validClass]);
33995 this.el.addClass(this.invalidClass);
33997 this.fireEvent('invalid', this, msg);
34001 setValue : function(v, suppressEvent)
34003 if(this.value === v){
34010 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34013 Roo.each(this.radioes, function(i){
34015 i.el.removeClass('checked');
34018 Roo.each(this.radioes, function(i){
34020 if(i.value === v || i.value.toString() === v.toString()){
34022 i.el.addClass('checked');
34024 if(suppressEvent !== true){
34025 this.fireEvent('check', this, i);
34036 clearInvalid : function(){
34038 if(!this.el || this.preventMark){
34042 this.el.removeClass([this.invalidClass]);
34044 this.fireEvent('valid', this);
34049 Roo.apply(Roo.bootstrap.RadioSet, {
34053 register : function(set)
34055 this.groups[set.name] = set;
34058 get: function(name)
34060 if (typeof(this.groups[name]) == 'undefined') {
34064 return this.groups[name] ;
34070 * Ext JS Library 1.1.1
34071 * Copyright(c) 2006-2007, Ext JS, LLC.
34073 * Originally Released Under LGPL - original licence link has changed is not relivant.
34076 * <script type="text/javascript">
34081 * @class Roo.bootstrap.SplitBar
34082 * @extends Roo.util.Observable
34083 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34087 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34088 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34089 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34090 split.minSize = 100;
34091 split.maxSize = 600;
34092 split.animate = true;
34093 split.on('moved', splitterMoved);
34096 * Create a new SplitBar
34097 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34098 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34099 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34100 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34101 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34102 position of the SplitBar).
34104 Roo.bootstrap.SplitBar = function(cfg){
34109 // dragElement : elm
34110 // resizingElement: el,
34112 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34113 // placement : Roo.bootstrap.SplitBar.LEFT ,
34114 // existingProxy ???
34117 this.el = Roo.get(cfg.dragElement, true);
34118 this.el.dom.unselectable = "on";
34120 this.resizingEl = Roo.get(cfg.resizingElement, true);
34124 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34125 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34128 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34131 * The minimum size of the resizing element. (Defaults to 0)
34137 * The maximum size of the resizing element. (Defaults to 2000)
34140 this.maxSize = 2000;
34143 * Whether to animate the transition to the new size
34146 this.animate = false;
34149 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34152 this.useShim = false;
34157 if(!cfg.existingProxy){
34159 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34161 this.proxy = Roo.get(cfg.existingProxy).dom;
34164 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34167 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34170 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34173 this.dragSpecs = {};
34176 * @private The adapter to use to positon and resize elements
34178 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34179 this.adapter.init(this);
34181 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34183 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34184 this.el.addClass("roo-splitbar-h");
34187 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34188 this.el.addClass("roo-splitbar-v");
34194 * Fires when the splitter is moved (alias for {@link #event-moved})
34195 * @param {Roo.bootstrap.SplitBar} this
34196 * @param {Number} newSize the new width or height
34201 * Fires when the splitter is moved
34202 * @param {Roo.bootstrap.SplitBar} this
34203 * @param {Number} newSize the new width or height
34207 * @event beforeresize
34208 * Fires before the splitter is dragged
34209 * @param {Roo.bootstrap.SplitBar} this
34211 "beforeresize" : true,
34213 "beforeapply" : true
34216 Roo.util.Observable.call(this);
34219 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34220 onStartProxyDrag : function(x, y){
34221 this.fireEvent("beforeresize", this);
34223 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34225 o.enableDisplayMode("block");
34226 // all splitbars share the same overlay
34227 Roo.bootstrap.SplitBar.prototype.overlay = o;
34229 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34230 this.overlay.show();
34231 Roo.get(this.proxy).setDisplayed("block");
34232 var size = this.adapter.getElementSize(this);
34233 this.activeMinSize = this.getMinimumSize();;
34234 this.activeMaxSize = this.getMaximumSize();;
34235 var c1 = size - this.activeMinSize;
34236 var c2 = Math.max(this.activeMaxSize - size, 0);
34237 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34238 this.dd.resetConstraints();
34239 this.dd.setXConstraint(
34240 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34241 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34243 this.dd.setYConstraint(0, 0);
34245 this.dd.resetConstraints();
34246 this.dd.setXConstraint(0, 0);
34247 this.dd.setYConstraint(
34248 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34249 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34252 this.dragSpecs.startSize = size;
34253 this.dragSpecs.startPoint = [x, y];
34254 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34258 * @private Called after the drag operation by the DDProxy
34260 onEndProxyDrag : function(e){
34261 Roo.get(this.proxy).setDisplayed(false);
34262 var endPoint = Roo.lib.Event.getXY(e);
34264 this.overlay.hide();
34267 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34268 newSize = this.dragSpecs.startSize +
34269 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34270 endPoint[0] - this.dragSpecs.startPoint[0] :
34271 this.dragSpecs.startPoint[0] - endPoint[0]
34274 newSize = this.dragSpecs.startSize +
34275 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34276 endPoint[1] - this.dragSpecs.startPoint[1] :
34277 this.dragSpecs.startPoint[1] - endPoint[1]
34280 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34281 if(newSize != this.dragSpecs.startSize){
34282 if(this.fireEvent('beforeapply', this, newSize) !== false){
34283 this.adapter.setElementSize(this, newSize);
34284 this.fireEvent("moved", this, newSize);
34285 this.fireEvent("resize", this, newSize);
34291 * Get the adapter this SplitBar uses
34292 * @return The adapter object
34294 getAdapter : function(){
34295 return this.adapter;
34299 * Set the adapter this SplitBar uses
34300 * @param {Object} adapter A SplitBar adapter object
34302 setAdapter : function(adapter){
34303 this.adapter = adapter;
34304 this.adapter.init(this);
34308 * Gets the minimum size for the resizing element
34309 * @return {Number} The minimum size
34311 getMinimumSize : function(){
34312 return this.minSize;
34316 * Sets the minimum size for the resizing element
34317 * @param {Number} minSize The minimum size
34319 setMinimumSize : function(minSize){
34320 this.minSize = minSize;
34324 * Gets the maximum size for the resizing element
34325 * @return {Number} The maximum size
34327 getMaximumSize : function(){
34328 return this.maxSize;
34332 * Sets the maximum size for the resizing element
34333 * @param {Number} maxSize The maximum size
34335 setMaximumSize : function(maxSize){
34336 this.maxSize = maxSize;
34340 * Sets the initialize size for the resizing element
34341 * @param {Number} size The initial size
34343 setCurrentSize : function(size){
34344 var oldAnimate = this.animate;
34345 this.animate = false;
34346 this.adapter.setElementSize(this, size);
34347 this.animate = oldAnimate;
34351 * Destroy this splitbar.
34352 * @param {Boolean} removeEl True to remove the element
34354 destroy : function(removeEl){
34356 this.shim.remove();
34359 this.proxy.parentNode.removeChild(this.proxy);
34367 * @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.
34369 Roo.bootstrap.SplitBar.createProxy = function(dir){
34370 var proxy = new Roo.Element(document.createElement("div"));
34371 proxy.unselectable();
34372 var cls = 'roo-splitbar-proxy';
34373 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34374 document.body.appendChild(proxy.dom);
34379 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34380 * Default Adapter. It assumes the splitter and resizing element are not positioned
34381 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34383 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34386 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34387 // do nothing for now
34388 init : function(s){
34392 * Called before drag operations to get the current size of the resizing element.
34393 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34395 getElementSize : function(s){
34396 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34397 return s.resizingEl.getWidth();
34399 return s.resizingEl.getHeight();
34404 * Called after drag operations to set the size of the resizing element.
34405 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34406 * @param {Number} newSize The new size to set
34407 * @param {Function} onComplete A function to be invoked when resizing is complete
34409 setElementSize : function(s, newSize, onComplete){
34410 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34412 s.resizingEl.setWidth(newSize);
34414 onComplete(s, newSize);
34417 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34422 s.resizingEl.setHeight(newSize);
34424 onComplete(s, newSize);
34427 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34434 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34435 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34436 * Adapter that moves the splitter element to align with the resized sizing element.
34437 * Used with an absolute positioned SplitBar.
34438 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34439 * document.body, make sure you assign an id to the body element.
34441 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34442 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34443 this.container = Roo.get(container);
34446 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34447 init : function(s){
34448 this.basic.init(s);
34451 getElementSize : function(s){
34452 return this.basic.getElementSize(s);
34455 setElementSize : function(s, newSize, onComplete){
34456 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34459 moveSplitter : function(s){
34460 var yes = Roo.bootstrap.SplitBar;
34461 switch(s.placement){
34463 s.el.setX(s.resizingEl.getRight());
34466 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34469 s.el.setY(s.resizingEl.getBottom());
34472 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34479 * Orientation constant - Create a vertical SplitBar
34483 Roo.bootstrap.SplitBar.VERTICAL = 1;
34486 * Orientation constant - Create a horizontal SplitBar
34490 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34493 * Placement constant - The resizing element is to the left of the splitter element
34497 Roo.bootstrap.SplitBar.LEFT = 1;
34500 * Placement constant - The resizing element is to the right of the splitter element
34504 Roo.bootstrap.SplitBar.RIGHT = 2;
34507 * Placement constant - The resizing element is positioned above the splitter element
34511 Roo.bootstrap.SplitBar.TOP = 3;
34514 * Placement constant - The resizing element is positioned under splitter element
34518 Roo.bootstrap.SplitBar.BOTTOM = 4;
34519 Roo.namespace("Roo.bootstrap.layout");/*
34521 * Ext JS Library 1.1.1
34522 * Copyright(c) 2006-2007, Ext JS, LLC.
34524 * Originally Released Under LGPL - original licence link has changed is not relivant.
34527 * <script type="text/javascript">
34531 * @class Roo.bootstrap.layout.Manager
34532 * @extends Roo.bootstrap.Component
34533 * Base class for layout managers.
34535 Roo.bootstrap.layout.Manager = function(config)
34537 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34543 /** false to disable window resize monitoring @type Boolean */
34544 this.monitorWindowResize = true;
34549 * Fires when a layout is performed.
34550 * @param {Roo.LayoutManager} this
34554 * @event regionresized
34555 * Fires when the user resizes a region.
34556 * @param {Roo.LayoutRegion} region The resized region
34557 * @param {Number} newSize The new size (width for east/west, height for north/south)
34559 "regionresized" : true,
34561 * @event regioncollapsed
34562 * Fires when a region is collapsed.
34563 * @param {Roo.LayoutRegion} region The collapsed region
34565 "regioncollapsed" : true,
34567 * @event regionexpanded
34568 * Fires when a region is expanded.
34569 * @param {Roo.LayoutRegion} region The expanded region
34571 "regionexpanded" : true
34573 this.updating = false;
34576 this.el = Roo.get(config.el);
34582 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34587 monitorWindowResize : true,
34593 onRender : function(ct, position)
34596 this.el = Roo.get(ct);
34599 //this.fireEvent('render',this);
34603 initEvents: function()
34607 // ie scrollbar fix
34608 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34609 document.body.scroll = "no";
34610 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34611 this.el.position('relative');
34613 this.id = this.el.id;
34614 this.el.addClass("roo-layout-container");
34615 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34616 if(this.el.dom != document.body ) {
34617 this.el.on('resize', this.layout,this);
34618 this.el.on('show', this.layout,this);
34624 * Returns true if this layout is currently being updated
34625 * @return {Boolean}
34627 isUpdating : function(){
34628 return this.updating;
34632 * Suspend the LayoutManager from doing auto-layouts while
34633 * making multiple add or remove calls
34635 beginUpdate : function(){
34636 this.updating = true;
34640 * Restore auto-layouts and optionally disable the manager from performing a layout
34641 * @param {Boolean} noLayout true to disable a layout update
34643 endUpdate : function(noLayout){
34644 this.updating = false;
34650 layout: function(){
34654 onRegionResized : function(region, newSize){
34655 this.fireEvent("regionresized", region, newSize);
34659 onRegionCollapsed : function(region){
34660 this.fireEvent("regioncollapsed", region);
34663 onRegionExpanded : function(region){
34664 this.fireEvent("regionexpanded", region);
34668 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34669 * performs box-model adjustments.
34670 * @return {Object} The size as an object {width: (the width), height: (the height)}
34672 getViewSize : function()
34675 if(this.el.dom != document.body){
34676 size = this.el.getSize();
34678 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34680 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34681 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34686 * Returns the Element this layout is bound to.
34687 * @return {Roo.Element}
34689 getEl : function(){
34694 * Returns the specified region.
34695 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34696 * @return {Roo.LayoutRegion}
34698 getRegion : function(target){
34699 return this.regions[target.toLowerCase()];
34702 onWindowResize : function(){
34703 if(this.monitorWindowResize){
34710 * Ext JS Library 1.1.1
34711 * Copyright(c) 2006-2007, Ext JS, LLC.
34713 * Originally Released Under LGPL - original licence link has changed is not relivant.
34716 * <script type="text/javascript">
34719 * @class Roo.bootstrap.layout.Border
34720 * @extends Roo.bootstrap.layout.Manager
34721 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34722 * please see: examples/bootstrap/nested.html<br><br>
34724 <b>The container the layout is rendered into can be either the body element or any other element.
34725 If it is not the body element, the container needs to either be an absolute positioned element,
34726 or you will need to add "position:relative" to the css of the container. You will also need to specify
34727 the container size if it is not the body element.</b>
34730 * Create a new Border
34731 * @param {Object} config Configuration options
34733 Roo.bootstrap.layout.Border = function(config){
34734 config = config || {};
34735 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34739 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34740 if(config[region]){
34741 config[region].region = region;
34742 this.addRegion(config[region]);
34748 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34750 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34752 * Creates and adds a new region if it doesn't already exist.
34753 * @param {String} target The target region key (north, south, east, west or center).
34754 * @param {Object} config The regions config object
34755 * @return {BorderLayoutRegion} The new region
34757 addRegion : function(config)
34759 if(!this.regions[config.region]){
34760 var r = this.factory(config);
34761 this.bindRegion(r);
34763 return this.regions[config.region];
34767 bindRegion : function(r){
34768 this.regions[r.config.region] = r;
34770 r.on("visibilitychange", this.layout, this);
34771 r.on("paneladded", this.layout, this);
34772 r.on("panelremoved", this.layout, this);
34773 r.on("invalidated", this.layout, this);
34774 r.on("resized", this.onRegionResized, this);
34775 r.on("collapsed", this.onRegionCollapsed, this);
34776 r.on("expanded", this.onRegionExpanded, this);
34780 * Performs a layout update.
34782 layout : function()
34784 if(this.updating) {
34788 // render all the rebions if they have not been done alreayd?
34789 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34790 if(this.regions[region] && !this.regions[region].bodyEl){
34791 this.regions[region].onRender(this.el)
34795 var size = this.getViewSize();
34796 var w = size.width;
34797 var h = size.height;
34802 //var x = 0, y = 0;
34804 var rs = this.regions;
34805 var north = rs["north"];
34806 var south = rs["south"];
34807 var west = rs["west"];
34808 var east = rs["east"];
34809 var center = rs["center"];
34810 //if(this.hideOnLayout){ // not supported anymore
34811 //c.el.setStyle("display", "none");
34813 if(north && north.isVisible()){
34814 var b = north.getBox();
34815 var m = north.getMargins();
34816 b.width = w - (m.left+m.right);
34819 centerY = b.height + b.y + m.bottom;
34820 centerH -= centerY;
34821 north.updateBox(this.safeBox(b));
34823 if(south && south.isVisible()){
34824 var b = south.getBox();
34825 var m = south.getMargins();
34826 b.width = w - (m.left+m.right);
34828 var totalHeight = (b.height + m.top + m.bottom);
34829 b.y = h - totalHeight + m.top;
34830 centerH -= totalHeight;
34831 south.updateBox(this.safeBox(b));
34833 if(west && west.isVisible()){
34834 var b = west.getBox();
34835 var m = west.getMargins();
34836 b.height = centerH - (m.top+m.bottom);
34838 b.y = centerY + m.top;
34839 var totalWidth = (b.width + m.left + m.right);
34840 centerX += totalWidth;
34841 centerW -= totalWidth;
34842 west.updateBox(this.safeBox(b));
34844 if(east && east.isVisible()){
34845 var b = east.getBox();
34846 var m = east.getMargins();
34847 b.height = centerH - (m.top+m.bottom);
34848 var totalWidth = (b.width + m.left + m.right);
34849 b.x = w - totalWidth + m.left;
34850 b.y = centerY + m.top;
34851 centerW -= totalWidth;
34852 east.updateBox(this.safeBox(b));
34855 var m = center.getMargins();
34857 x: centerX + m.left,
34858 y: centerY + m.top,
34859 width: centerW - (m.left+m.right),
34860 height: centerH - (m.top+m.bottom)
34862 //if(this.hideOnLayout){
34863 //center.el.setStyle("display", "block");
34865 center.updateBox(this.safeBox(centerBox));
34868 this.fireEvent("layout", this);
34872 safeBox : function(box){
34873 box.width = Math.max(0, box.width);
34874 box.height = Math.max(0, box.height);
34879 * Adds a ContentPanel (or subclass) to this layout.
34880 * @param {String} target The target region key (north, south, east, west or center).
34881 * @param {Roo.ContentPanel} panel The panel to add
34882 * @return {Roo.ContentPanel} The added panel
34884 add : function(target, panel){
34886 target = target.toLowerCase();
34887 return this.regions[target].add(panel);
34891 * Remove a ContentPanel (or subclass) to this layout.
34892 * @param {String} target The target region key (north, south, east, west or center).
34893 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34894 * @return {Roo.ContentPanel} The removed panel
34896 remove : function(target, panel){
34897 target = target.toLowerCase();
34898 return this.regions[target].remove(panel);
34902 * Searches all regions for a panel with the specified id
34903 * @param {String} panelId
34904 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34906 findPanel : function(panelId){
34907 var rs = this.regions;
34908 for(var target in rs){
34909 if(typeof rs[target] != "function"){
34910 var p = rs[target].getPanel(panelId);
34920 * Searches all regions for a panel with the specified id and activates (shows) it.
34921 * @param {String/ContentPanel} panelId The panels id or the panel itself
34922 * @return {Roo.ContentPanel} The shown panel or null
34924 showPanel : function(panelId) {
34925 var rs = this.regions;
34926 for(var target in rs){
34927 var r = rs[target];
34928 if(typeof r != "function"){
34929 if(r.hasPanel(panelId)){
34930 return r.showPanel(panelId);
34938 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34939 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34942 restoreState : function(provider){
34944 provider = Roo.state.Manager;
34946 var sm = new Roo.LayoutStateManager();
34947 sm.init(this, provider);
34953 * Adds a xtype elements to the layout.
34957 xtype : 'ContentPanel',
34964 xtype : 'NestedLayoutPanel',
34970 items : [ ... list of content panels or nested layout panels.. ]
34974 * @param {Object} cfg Xtype definition of item to add.
34976 addxtype : function(cfg)
34978 // basically accepts a pannel...
34979 // can accept a layout region..!?!?
34980 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34983 // theory? children can only be panels??
34985 //if (!cfg.xtype.match(/Panel$/)) {
34990 if (typeof(cfg.region) == 'undefined') {
34991 Roo.log("Failed to add Panel, region was not set");
34995 var region = cfg.region;
35001 xitems = cfg.items;
35008 case 'Content': // ContentPanel (el, cfg)
35009 case 'Scroll': // ContentPanel (el, cfg)
35011 cfg.autoCreate = true;
35012 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35014 // var el = this.el.createChild();
35015 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35018 this.add(region, ret);
35022 case 'TreePanel': // our new panel!
35023 cfg.el = this.el.createChild();
35024 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35025 this.add(region, ret);
35030 // create a new Layout (which is a Border Layout...
35032 var clayout = cfg.layout;
35033 clayout.el = this.el.createChild();
35034 clayout.items = clayout.items || [];
35038 // replace this exitems with the clayout ones..
35039 xitems = clayout.items;
35041 // force background off if it's in center...
35042 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35043 cfg.background = false;
35045 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35048 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35049 //console.log('adding nested layout panel ' + cfg.toSource());
35050 this.add(region, ret);
35051 nb = {}; /// find first...
35056 // needs grid and region
35058 //var el = this.getRegion(region).el.createChild();
35060 *var el = this.el.createChild();
35061 // create the grid first...
35062 cfg.grid.container = el;
35063 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35066 if (region == 'center' && this.active ) {
35067 cfg.background = false;
35070 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35072 this.add(region, ret);
35074 if (cfg.background) {
35075 // render grid on panel activation (if panel background)
35076 ret.on('activate', function(gp) {
35077 if (!gp.grid.rendered) {
35078 // gp.grid.render(el);
35082 // cfg.grid.render(el);
35088 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35089 // it was the old xcomponent building that caused this before.
35090 // espeically if border is the top element in the tree.
35100 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35102 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35103 this.add(region, ret);
35107 throw "Can not add '" + cfg.xtype + "' to Border";
35113 this.beginUpdate();
35117 Roo.each(xitems, function(i) {
35118 region = nb && i.region ? i.region : false;
35120 var add = ret.addxtype(i);
35123 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35124 if (!i.background) {
35125 abn[region] = nb[region] ;
35132 // make the last non-background panel active..
35133 //if (nb) { Roo.log(abn); }
35136 for(var r in abn) {
35137 region = this.getRegion(r);
35139 // tried using nb[r], but it does not work..
35141 region.showPanel(abn[r]);
35152 factory : function(cfg)
35155 var validRegions = Roo.bootstrap.layout.Border.regions;
35157 var target = cfg.region;
35160 var r = Roo.bootstrap.layout;
35164 return new r.North(cfg);
35166 return new r.South(cfg);
35168 return new r.East(cfg);
35170 return new r.West(cfg);
35172 return new r.Center(cfg);
35174 throw 'Layout region "'+target+'" not supported.';
35181 * Ext JS Library 1.1.1
35182 * Copyright(c) 2006-2007, Ext JS, LLC.
35184 * Originally Released Under LGPL - original licence link has changed is not relivant.
35187 * <script type="text/javascript">
35191 * @class Roo.bootstrap.layout.Basic
35192 * @extends Roo.util.Observable
35193 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35194 * and does not have a titlebar, tabs or any other features. All it does is size and position
35195 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35196 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35197 * @cfg {string} region the region that it inhabits..
35198 * @cfg {bool} skipConfig skip config?
35202 Roo.bootstrap.layout.Basic = function(config){
35204 this.mgr = config.mgr;
35206 this.position = config.region;
35208 var skipConfig = config.skipConfig;
35212 * @scope Roo.BasicLayoutRegion
35216 * @event beforeremove
35217 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35218 * @param {Roo.LayoutRegion} this
35219 * @param {Roo.ContentPanel} panel The panel
35220 * @param {Object} e The cancel event object
35222 "beforeremove" : true,
35224 * @event invalidated
35225 * Fires when the layout for this region is changed.
35226 * @param {Roo.LayoutRegion} this
35228 "invalidated" : true,
35230 * @event visibilitychange
35231 * Fires when this region is shown or hidden
35232 * @param {Roo.LayoutRegion} this
35233 * @param {Boolean} visibility true or false
35235 "visibilitychange" : true,
35237 * @event paneladded
35238 * Fires when a panel is added.
35239 * @param {Roo.LayoutRegion} this
35240 * @param {Roo.ContentPanel} panel The panel
35242 "paneladded" : true,
35244 * @event panelremoved
35245 * Fires when a panel is removed.
35246 * @param {Roo.LayoutRegion} this
35247 * @param {Roo.ContentPanel} panel The panel
35249 "panelremoved" : true,
35251 * @event beforecollapse
35252 * Fires when this region before collapse.
35253 * @param {Roo.LayoutRegion} this
35255 "beforecollapse" : true,
35258 * Fires when this region is collapsed.
35259 * @param {Roo.LayoutRegion} this
35261 "collapsed" : true,
35264 * Fires when this region is expanded.
35265 * @param {Roo.LayoutRegion} this
35270 * Fires when this region is slid into view.
35271 * @param {Roo.LayoutRegion} this
35273 "slideshow" : true,
35276 * Fires when this region slides out of view.
35277 * @param {Roo.LayoutRegion} this
35279 "slidehide" : true,
35281 * @event panelactivated
35282 * Fires when a panel is activated.
35283 * @param {Roo.LayoutRegion} this
35284 * @param {Roo.ContentPanel} panel The activated panel
35286 "panelactivated" : true,
35289 * Fires when the user resizes this region.
35290 * @param {Roo.LayoutRegion} this
35291 * @param {Number} newSize The new size (width for east/west, height for north/south)
35295 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35296 this.panels = new Roo.util.MixedCollection();
35297 this.panels.getKey = this.getPanelId.createDelegate(this);
35299 this.activePanel = null;
35300 // ensure listeners are added...
35302 if (config.listeners || config.events) {
35303 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35304 listeners : config.listeners || {},
35305 events : config.events || {}
35309 if(skipConfig !== true){
35310 this.applyConfig(config);
35314 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35316 getPanelId : function(p){
35320 applyConfig : function(config){
35321 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35322 this.config = config;
35327 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35328 * the width, for horizontal (north, south) the height.
35329 * @param {Number} newSize The new width or height
35331 resizeTo : function(newSize){
35332 var el = this.el ? this.el :
35333 (this.activePanel ? this.activePanel.getEl() : null);
35335 switch(this.position){
35338 el.setWidth(newSize);
35339 this.fireEvent("resized", this, newSize);
35343 el.setHeight(newSize);
35344 this.fireEvent("resized", this, newSize);
35350 getBox : function(){
35351 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35354 getMargins : function(){
35355 return this.margins;
35358 updateBox : function(box){
35360 var el = this.activePanel.getEl();
35361 el.dom.style.left = box.x + "px";
35362 el.dom.style.top = box.y + "px";
35363 this.activePanel.setSize(box.width, box.height);
35367 * Returns the container element for this region.
35368 * @return {Roo.Element}
35370 getEl : function(){
35371 return this.activePanel;
35375 * Returns true if this region is currently visible.
35376 * @return {Boolean}
35378 isVisible : function(){
35379 return this.activePanel ? true : false;
35382 setActivePanel : function(panel){
35383 panel = this.getPanel(panel);
35384 if(this.activePanel && this.activePanel != panel){
35385 this.activePanel.setActiveState(false);
35386 this.activePanel.getEl().setLeftTop(-10000,-10000);
35388 this.activePanel = panel;
35389 panel.setActiveState(true);
35391 panel.setSize(this.box.width, this.box.height);
35393 this.fireEvent("panelactivated", this, panel);
35394 this.fireEvent("invalidated");
35398 * Show the specified panel.
35399 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35400 * @return {Roo.ContentPanel} The shown panel or null
35402 showPanel : function(panel){
35403 panel = this.getPanel(panel);
35405 this.setActivePanel(panel);
35411 * Get the active panel for this region.
35412 * @return {Roo.ContentPanel} The active panel or null
35414 getActivePanel : function(){
35415 return this.activePanel;
35419 * Add the passed ContentPanel(s)
35420 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35421 * @return {Roo.ContentPanel} The panel added (if only one was added)
35423 add : function(panel){
35424 if(arguments.length > 1){
35425 for(var i = 0, len = arguments.length; i < len; i++) {
35426 this.add(arguments[i]);
35430 if(this.hasPanel(panel)){
35431 this.showPanel(panel);
35434 var el = panel.getEl();
35435 if(el.dom.parentNode != this.mgr.el.dom){
35436 this.mgr.el.dom.appendChild(el.dom);
35438 if(panel.setRegion){
35439 panel.setRegion(this);
35441 this.panels.add(panel);
35442 el.setStyle("position", "absolute");
35443 if(!panel.background){
35444 this.setActivePanel(panel);
35445 if(this.config.initialSize && this.panels.getCount()==1){
35446 this.resizeTo(this.config.initialSize);
35449 this.fireEvent("paneladded", this, panel);
35454 * Returns true if the panel is in this region.
35455 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35456 * @return {Boolean}
35458 hasPanel : function(panel){
35459 if(typeof panel == "object"){ // must be panel obj
35460 panel = panel.getId();
35462 return this.getPanel(panel) ? true : false;
35466 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35467 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35468 * @param {Boolean} preservePanel Overrides the config preservePanel option
35469 * @return {Roo.ContentPanel} The panel that was removed
35471 remove : function(panel, preservePanel){
35472 panel = this.getPanel(panel);
35477 this.fireEvent("beforeremove", this, panel, e);
35478 if(e.cancel === true){
35481 var panelId = panel.getId();
35482 this.panels.removeKey(panelId);
35487 * Returns the panel specified or null if it's not in this region.
35488 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35489 * @return {Roo.ContentPanel}
35491 getPanel : function(id){
35492 if(typeof id == "object"){ // must be panel obj
35495 return this.panels.get(id);
35499 * Returns this regions position (north/south/east/west/center).
35502 getPosition: function(){
35503 return this.position;
35507 * Ext JS Library 1.1.1
35508 * Copyright(c) 2006-2007, Ext JS, LLC.
35510 * Originally Released Under LGPL - original licence link has changed is not relivant.
35513 * <script type="text/javascript">
35517 * @class Roo.bootstrap.layout.Region
35518 * @extends Roo.bootstrap.layout.Basic
35519 * This class represents a region in a layout manager.
35521 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35522 * @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})
35523 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35524 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35525 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35526 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35527 * @cfg {String} title The title for the region (overrides panel titles)
35528 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35529 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35530 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35531 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35532 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35533 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35534 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35535 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35536 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35537 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35539 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35540 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35541 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35542 * @cfg {Number} width For East/West panels
35543 * @cfg {Number} height For North/South panels
35544 * @cfg {Boolean} split To show the splitter
35545 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35547 * @cfg {string} cls Extra CSS classes to add to region
35549 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35550 * @cfg {string} region the region that it inhabits..
35553 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35554 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35556 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35557 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35558 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35560 Roo.bootstrap.layout.Region = function(config)
35562 this.applyConfig(config);
35564 var mgr = config.mgr;
35565 var pos = config.region;
35566 config.skipConfig = true;
35567 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35570 this.onRender(mgr.el);
35573 this.visible = true;
35574 this.collapsed = false;
35575 this.unrendered_panels = [];
35578 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35580 position: '', // set by wrapper (eg. north/south etc..)
35581 unrendered_panels : null, // unrendered panels.
35582 createBody : function(){
35583 /** This region's body element
35584 * @type Roo.Element */
35585 this.bodyEl = this.el.createChild({
35587 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35591 onRender: function(ctr, pos)
35593 var dh = Roo.DomHelper;
35594 /** This region's container element
35595 * @type Roo.Element */
35596 this.el = dh.append(ctr.dom, {
35598 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35600 /** This region's title element
35601 * @type Roo.Element */
35603 this.titleEl = dh.append(this.el.dom,
35606 unselectable: "on",
35607 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35609 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35610 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35613 this.titleEl.enableDisplayMode();
35614 /** This region's title text element
35615 * @type HTMLElement */
35616 this.titleTextEl = this.titleEl.dom.firstChild;
35617 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35619 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35620 this.closeBtn.enableDisplayMode();
35621 this.closeBtn.on("click", this.closeClicked, this);
35622 this.closeBtn.hide();
35624 this.createBody(this.config);
35625 if(this.config.hideWhenEmpty){
35627 this.on("paneladded", this.validateVisibility, this);
35628 this.on("panelremoved", this.validateVisibility, this);
35630 if(this.autoScroll){
35631 this.bodyEl.setStyle("overflow", "auto");
35633 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35635 //if(c.titlebar !== false){
35636 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35637 this.titleEl.hide();
35639 this.titleEl.show();
35640 if(this.config.title){
35641 this.titleTextEl.innerHTML = this.config.title;
35645 if(this.config.collapsed){
35646 this.collapse(true);
35648 if(this.config.hidden){
35652 if (this.unrendered_panels && this.unrendered_panels.length) {
35653 for (var i =0;i< this.unrendered_panels.length; i++) {
35654 this.add(this.unrendered_panels[i]);
35656 this.unrendered_panels = null;
35662 applyConfig : function(c)
35665 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35666 var dh = Roo.DomHelper;
35667 if(c.titlebar !== false){
35668 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35669 this.collapseBtn.on("click", this.collapse, this);
35670 this.collapseBtn.enableDisplayMode();
35672 if(c.showPin === true || this.showPin){
35673 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35674 this.stickBtn.enableDisplayMode();
35675 this.stickBtn.on("click", this.expand, this);
35676 this.stickBtn.hide();
35681 /** This region's collapsed element
35682 * @type Roo.Element */
35685 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35686 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35689 if(c.floatable !== false){
35690 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35691 this.collapsedEl.on("click", this.collapseClick, this);
35694 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35695 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35696 id: "message", unselectable: "on", style:{"float":"left"}});
35697 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35699 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35700 this.expandBtn.on("click", this.expand, this);
35704 if(this.collapseBtn){
35705 this.collapseBtn.setVisible(c.collapsible == true);
35708 this.cmargins = c.cmargins || this.cmargins ||
35709 (this.position == "west" || this.position == "east" ?
35710 {top: 0, left: 2, right:2, bottom: 0} :
35711 {top: 2, left: 0, right:0, bottom: 2});
35713 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35716 this.bottomTabs = c.tabPosition != "top";
35718 this.autoScroll = c.autoScroll || false;
35723 this.duration = c.duration || .30;
35724 this.slideDuration = c.slideDuration || .45;
35729 * Returns true if this region is currently visible.
35730 * @return {Boolean}
35732 isVisible : function(){
35733 return this.visible;
35737 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35738 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35740 //setCollapsedTitle : function(title){
35741 // title = title || " ";
35742 // if(this.collapsedTitleTextEl){
35743 // this.collapsedTitleTextEl.innerHTML = title;
35747 getBox : function(){
35749 // if(!this.collapsed){
35750 b = this.el.getBox(false, true);
35752 // b = this.collapsedEl.getBox(false, true);
35757 getMargins : function(){
35758 return this.margins;
35759 //return this.collapsed ? this.cmargins : this.margins;
35762 highlight : function(){
35763 this.el.addClass("x-layout-panel-dragover");
35766 unhighlight : function(){
35767 this.el.removeClass("x-layout-panel-dragover");
35770 updateBox : function(box)
35772 if (!this.bodyEl) {
35773 return; // not rendered yet..
35777 if(!this.collapsed){
35778 this.el.dom.style.left = box.x + "px";
35779 this.el.dom.style.top = box.y + "px";
35780 this.updateBody(box.width, box.height);
35782 this.collapsedEl.dom.style.left = box.x + "px";
35783 this.collapsedEl.dom.style.top = box.y + "px";
35784 this.collapsedEl.setSize(box.width, box.height);
35787 this.tabs.autoSizeTabs();
35791 updateBody : function(w, h)
35794 this.el.setWidth(w);
35795 w -= this.el.getBorderWidth("rl");
35796 if(this.config.adjustments){
35797 w += this.config.adjustments[0];
35800 if(h !== null && h > 0){
35801 this.el.setHeight(h);
35802 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35803 h -= this.el.getBorderWidth("tb");
35804 if(this.config.adjustments){
35805 h += this.config.adjustments[1];
35807 this.bodyEl.setHeight(h);
35809 h = this.tabs.syncHeight(h);
35812 if(this.panelSize){
35813 w = w !== null ? w : this.panelSize.width;
35814 h = h !== null ? h : this.panelSize.height;
35816 if(this.activePanel){
35817 var el = this.activePanel.getEl();
35818 w = w !== null ? w : el.getWidth();
35819 h = h !== null ? h : el.getHeight();
35820 this.panelSize = {width: w, height: h};
35821 this.activePanel.setSize(w, h);
35823 if(Roo.isIE && this.tabs){
35824 this.tabs.el.repaint();
35829 * Returns the container element for this region.
35830 * @return {Roo.Element}
35832 getEl : function(){
35837 * Hides this region.
35840 //if(!this.collapsed){
35841 this.el.dom.style.left = "-2000px";
35844 // this.collapsedEl.dom.style.left = "-2000px";
35845 // this.collapsedEl.hide();
35847 this.visible = false;
35848 this.fireEvent("visibilitychange", this, false);
35852 * Shows this region if it was previously hidden.
35855 //if(!this.collapsed){
35858 // this.collapsedEl.show();
35860 this.visible = true;
35861 this.fireEvent("visibilitychange", this, true);
35864 closeClicked : function(){
35865 if(this.activePanel){
35866 this.remove(this.activePanel);
35870 collapseClick : function(e){
35872 e.stopPropagation();
35875 e.stopPropagation();
35881 * Collapses this region.
35882 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35885 collapse : function(skipAnim, skipCheck = false){
35886 if(this.collapsed) {
35890 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35892 this.collapsed = true;
35894 this.split.el.hide();
35896 if(this.config.animate && skipAnim !== true){
35897 this.fireEvent("invalidated", this);
35898 this.animateCollapse();
35900 this.el.setLocation(-20000,-20000);
35902 this.collapsedEl.show();
35903 this.fireEvent("collapsed", this);
35904 this.fireEvent("invalidated", this);
35910 animateCollapse : function(){
35915 * Expands this region if it was previously collapsed.
35916 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35917 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35920 expand : function(e, skipAnim){
35922 e.stopPropagation();
35924 if(!this.collapsed || this.el.hasActiveFx()) {
35928 this.afterSlideIn();
35931 this.collapsed = false;
35932 if(this.config.animate && skipAnim !== true){
35933 this.animateExpand();
35937 this.split.el.show();
35939 this.collapsedEl.setLocation(-2000,-2000);
35940 this.collapsedEl.hide();
35941 this.fireEvent("invalidated", this);
35942 this.fireEvent("expanded", this);
35946 animateExpand : function(){
35950 initTabs : function()
35952 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35954 var ts = new Roo.bootstrap.panel.Tabs({
35955 el: this.bodyEl.dom,
35956 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35957 disableTooltips: this.config.disableTabTips,
35958 toolbar : this.config.toolbar
35961 if(this.config.hideTabs){
35962 ts.stripWrap.setDisplayed(false);
35965 ts.resizeTabs = this.config.resizeTabs === true;
35966 ts.minTabWidth = this.config.minTabWidth || 40;
35967 ts.maxTabWidth = this.config.maxTabWidth || 250;
35968 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35969 ts.monitorResize = false;
35970 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35971 ts.bodyEl.addClass('roo-layout-tabs-body');
35972 this.panels.each(this.initPanelAsTab, this);
35975 initPanelAsTab : function(panel){
35976 var ti = this.tabs.addTab(
35980 this.config.closeOnTab && panel.isClosable(),
35983 if(panel.tabTip !== undefined){
35984 ti.setTooltip(panel.tabTip);
35986 ti.on("activate", function(){
35987 this.setActivePanel(panel);
35990 if(this.config.closeOnTab){
35991 ti.on("beforeclose", function(t, e){
35993 this.remove(panel);
35997 panel.tabItem = ti;
36002 updatePanelTitle : function(panel, title)
36004 if(this.activePanel == panel){
36005 this.updateTitle(title);
36008 var ti = this.tabs.getTab(panel.getEl().id);
36010 if(panel.tabTip !== undefined){
36011 ti.setTooltip(panel.tabTip);
36016 updateTitle : function(title){
36017 if(this.titleTextEl && !this.config.title){
36018 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36022 setActivePanel : function(panel)
36024 panel = this.getPanel(panel);
36025 if(this.activePanel && this.activePanel != panel){
36026 if(this.activePanel.setActiveState(false) === false){
36030 this.activePanel = panel;
36031 panel.setActiveState(true);
36032 if(this.panelSize){
36033 panel.setSize(this.panelSize.width, this.panelSize.height);
36036 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36038 this.updateTitle(panel.getTitle());
36040 this.fireEvent("invalidated", this);
36042 this.fireEvent("panelactivated", this, panel);
36046 * Shows the specified panel.
36047 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36048 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36050 showPanel : function(panel)
36052 panel = this.getPanel(panel);
36055 var tab = this.tabs.getTab(panel.getEl().id);
36056 if(tab.isHidden()){
36057 this.tabs.unhideTab(tab.id);
36061 this.setActivePanel(panel);
36068 * Get the active panel for this region.
36069 * @return {Roo.ContentPanel} The active panel or null
36071 getActivePanel : function(){
36072 return this.activePanel;
36075 validateVisibility : function(){
36076 if(this.panels.getCount() < 1){
36077 this.updateTitle(" ");
36078 this.closeBtn.hide();
36081 if(!this.isVisible()){
36088 * Adds the passed ContentPanel(s) to this region.
36089 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36090 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36092 add : function(panel)
36094 if(arguments.length > 1){
36095 for(var i = 0, len = arguments.length; i < len; i++) {
36096 this.add(arguments[i]);
36101 // if we have not been rendered yet, then we can not really do much of this..
36102 if (!this.bodyEl) {
36103 this.unrendered_panels.push(panel);
36110 if(this.hasPanel(panel)){
36111 this.showPanel(panel);
36114 panel.setRegion(this);
36115 this.panels.add(panel);
36116 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36117 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36118 // and hide them... ???
36119 this.bodyEl.dom.appendChild(panel.getEl().dom);
36120 if(panel.background !== true){
36121 this.setActivePanel(panel);
36123 this.fireEvent("paneladded", this, panel);
36130 this.initPanelAsTab(panel);
36134 if(panel.background !== true){
36135 this.tabs.activate(panel.getEl().id);
36137 this.fireEvent("paneladded", this, panel);
36142 * Hides the tab for the specified panel.
36143 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36145 hidePanel : function(panel){
36146 if(this.tabs && (panel = this.getPanel(panel))){
36147 this.tabs.hideTab(panel.getEl().id);
36152 * Unhides the tab for a previously hidden panel.
36153 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36155 unhidePanel : function(panel){
36156 if(this.tabs && (panel = this.getPanel(panel))){
36157 this.tabs.unhideTab(panel.getEl().id);
36161 clearPanels : function(){
36162 while(this.panels.getCount() > 0){
36163 this.remove(this.panels.first());
36168 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36169 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36170 * @param {Boolean} preservePanel Overrides the config preservePanel option
36171 * @return {Roo.ContentPanel} The panel that was removed
36173 remove : function(panel, preservePanel)
36175 panel = this.getPanel(panel);
36180 this.fireEvent("beforeremove", this, panel, e);
36181 if(e.cancel === true){
36184 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36185 var panelId = panel.getId();
36186 this.panels.removeKey(panelId);
36188 document.body.appendChild(panel.getEl().dom);
36191 this.tabs.removeTab(panel.getEl().id);
36192 }else if (!preservePanel){
36193 this.bodyEl.dom.removeChild(panel.getEl().dom);
36195 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36196 var p = this.panels.first();
36197 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36198 tempEl.appendChild(p.getEl().dom);
36199 this.bodyEl.update("");
36200 this.bodyEl.dom.appendChild(p.getEl().dom);
36202 this.updateTitle(p.getTitle());
36204 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36205 this.setActivePanel(p);
36207 panel.setRegion(null);
36208 if(this.activePanel == panel){
36209 this.activePanel = null;
36211 if(this.config.autoDestroy !== false && preservePanel !== true){
36212 try{panel.destroy();}catch(e){}
36214 this.fireEvent("panelremoved", this, panel);
36219 * Returns the TabPanel component used by this region
36220 * @return {Roo.TabPanel}
36222 getTabs : function(){
36226 createTool : function(parentEl, className){
36227 var btn = Roo.DomHelper.append(parentEl, {
36229 cls: "x-layout-tools-button",
36232 cls: "roo-layout-tools-button-inner " + className,
36236 btn.addClassOnOver("roo-layout-tools-button-over");
36241 * Ext JS Library 1.1.1
36242 * Copyright(c) 2006-2007, Ext JS, LLC.
36244 * Originally Released Under LGPL - original licence link has changed is not relivant.
36247 * <script type="text/javascript">
36253 * @class Roo.SplitLayoutRegion
36254 * @extends Roo.LayoutRegion
36255 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36257 Roo.bootstrap.layout.Split = function(config){
36258 this.cursor = config.cursor;
36259 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36262 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36264 splitTip : "Drag to resize.",
36265 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36266 useSplitTips : false,
36268 applyConfig : function(config){
36269 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36272 onRender : function(ctr,pos) {
36274 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36275 if(!this.config.split){
36280 var splitEl = Roo.DomHelper.append(ctr.dom, {
36282 id: this.el.id + "-split",
36283 cls: "roo-layout-split roo-layout-split-"+this.position,
36286 /** The SplitBar for this region
36287 * @type Roo.SplitBar */
36288 // does not exist yet...
36289 Roo.log([this.position, this.orientation]);
36291 this.split = new Roo.bootstrap.SplitBar({
36292 dragElement : splitEl,
36293 resizingElement: this.el,
36294 orientation : this.orientation
36297 this.split.on("moved", this.onSplitMove, this);
36298 this.split.useShim = this.config.useShim === true;
36299 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36300 if(this.useSplitTips){
36301 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36303 //if(config.collapsible){
36304 // this.split.el.on("dblclick", this.collapse, this);
36307 if(typeof this.config.minSize != "undefined"){
36308 this.split.minSize = this.config.minSize;
36310 if(typeof this.config.maxSize != "undefined"){
36311 this.split.maxSize = this.config.maxSize;
36313 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36314 this.hideSplitter();
36319 getHMaxSize : function(){
36320 var cmax = this.config.maxSize || 10000;
36321 var center = this.mgr.getRegion("center");
36322 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36325 getVMaxSize : function(){
36326 var cmax = this.config.maxSize || 10000;
36327 var center = this.mgr.getRegion("center");
36328 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36331 onSplitMove : function(split, newSize){
36332 this.fireEvent("resized", this, newSize);
36336 * Returns the {@link Roo.SplitBar} for this region.
36337 * @return {Roo.SplitBar}
36339 getSplitBar : function(){
36344 this.hideSplitter();
36345 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36348 hideSplitter : function(){
36350 this.split.el.setLocation(-2000,-2000);
36351 this.split.el.hide();
36357 this.split.el.show();
36359 Roo.bootstrap.layout.Split.superclass.show.call(this);
36362 beforeSlide: function(){
36363 if(Roo.isGecko){// firefox overflow auto bug workaround
36364 this.bodyEl.clip();
36366 this.tabs.bodyEl.clip();
36368 if(this.activePanel){
36369 this.activePanel.getEl().clip();
36371 if(this.activePanel.beforeSlide){
36372 this.activePanel.beforeSlide();
36378 afterSlide : function(){
36379 if(Roo.isGecko){// firefox overflow auto bug workaround
36380 this.bodyEl.unclip();
36382 this.tabs.bodyEl.unclip();
36384 if(this.activePanel){
36385 this.activePanel.getEl().unclip();
36386 if(this.activePanel.afterSlide){
36387 this.activePanel.afterSlide();
36393 initAutoHide : function(){
36394 if(this.autoHide !== false){
36395 if(!this.autoHideHd){
36396 var st = new Roo.util.DelayedTask(this.slideIn, this);
36397 this.autoHideHd = {
36398 "mouseout": function(e){
36399 if(!e.within(this.el, true)){
36403 "mouseover" : function(e){
36409 this.el.on(this.autoHideHd);
36413 clearAutoHide : function(){
36414 if(this.autoHide !== false){
36415 this.el.un("mouseout", this.autoHideHd.mouseout);
36416 this.el.un("mouseover", this.autoHideHd.mouseover);
36420 clearMonitor : function(){
36421 Roo.get(document).un("click", this.slideInIf, this);
36424 // these names are backwards but not changed for compat
36425 slideOut : function(){
36426 if(this.isSlid || this.el.hasActiveFx()){
36429 this.isSlid = true;
36430 if(this.collapseBtn){
36431 this.collapseBtn.hide();
36433 this.closeBtnState = this.closeBtn.getStyle('display');
36434 this.closeBtn.hide();
36436 this.stickBtn.show();
36439 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36440 this.beforeSlide();
36441 this.el.setStyle("z-index", 10001);
36442 this.el.slideIn(this.getSlideAnchor(), {
36443 callback: function(){
36445 this.initAutoHide();
36446 Roo.get(document).on("click", this.slideInIf, this);
36447 this.fireEvent("slideshow", this);
36454 afterSlideIn : function(){
36455 this.clearAutoHide();
36456 this.isSlid = false;
36457 this.clearMonitor();
36458 this.el.setStyle("z-index", "");
36459 if(this.collapseBtn){
36460 this.collapseBtn.show();
36462 this.closeBtn.setStyle('display', this.closeBtnState);
36464 this.stickBtn.hide();
36466 this.fireEvent("slidehide", this);
36469 slideIn : function(cb){
36470 if(!this.isSlid || this.el.hasActiveFx()){
36474 this.isSlid = false;
36475 this.beforeSlide();
36476 this.el.slideOut(this.getSlideAnchor(), {
36477 callback: function(){
36478 this.el.setLeftTop(-10000, -10000);
36480 this.afterSlideIn();
36488 slideInIf : function(e){
36489 if(!e.within(this.el)){
36494 animateCollapse : function(){
36495 this.beforeSlide();
36496 this.el.setStyle("z-index", 20000);
36497 var anchor = this.getSlideAnchor();
36498 this.el.slideOut(anchor, {
36499 callback : function(){
36500 this.el.setStyle("z-index", "");
36501 this.collapsedEl.slideIn(anchor, {duration:.3});
36503 this.el.setLocation(-10000,-10000);
36505 this.fireEvent("collapsed", this);
36512 animateExpand : function(){
36513 this.beforeSlide();
36514 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36515 this.el.setStyle("z-index", 20000);
36516 this.collapsedEl.hide({
36519 this.el.slideIn(this.getSlideAnchor(), {
36520 callback : function(){
36521 this.el.setStyle("z-index", "");
36524 this.split.el.show();
36526 this.fireEvent("invalidated", this);
36527 this.fireEvent("expanded", this);
36555 getAnchor : function(){
36556 return this.anchors[this.position];
36559 getCollapseAnchor : function(){
36560 return this.canchors[this.position];
36563 getSlideAnchor : function(){
36564 return this.sanchors[this.position];
36567 getAlignAdj : function(){
36568 var cm = this.cmargins;
36569 switch(this.position){
36585 getExpandAdj : function(){
36586 var c = this.collapsedEl, cm = this.cmargins;
36587 switch(this.position){
36589 return [-(cm.right+c.getWidth()+cm.left), 0];
36592 return [cm.right+c.getWidth()+cm.left, 0];
36595 return [0, -(cm.top+cm.bottom+c.getHeight())];
36598 return [0, cm.top+cm.bottom+c.getHeight()];
36604 * Ext JS Library 1.1.1
36605 * Copyright(c) 2006-2007, Ext JS, LLC.
36607 * Originally Released Under LGPL - original licence link has changed is not relivant.
36610 * <script type="text/javascript">
36613 * These classes are private internal classes
36615 Roo.bootstrap.layout.Center = function(config){
36616 config.region = "center";
36617 Roo.bootstrap.layout.Region.call(this, config);
36618 this.visible = true;
36619 this.minWidth = config.minWidth || 20;
36620 this.minHeight = config.minHeight || 20;
36623 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36625 // center panel can't be hidden
36629 // center panel can't be hidden
36632 getMinWidth: function(){
36633 return this.minWidth;
36636 getMinHeight: function(){
36637 return this.minHeight;
36650 Roo.bootstrap.layout.North = function(config)
36652 config.region = 'north';
36653 config.cursor = 'n-resize';
36655 Roo.bootstrap.layout.Split.call(this, config);
36659 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36660 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36661 this.split.el.addClass("roo-layout-split-v");
36663 var size = config.initialSize || config.height;
36664 if(typeof size != "undefined"){
36665 this.el.setHeight(size);
36668 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36670 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36674 getBox : function(){
36675 if(this.collapsed){
36676 return this.collapsedEl.getBox();
36678 var box = this.el.getBox();
36680 box.height += this.split.el.getHeight();
36685 updateBox : function(box){
36686 if(this.split && !this.collapsed){
36687 box.height -= this.split.el.getHeight();
36688 this.split.el.setLeft(box.x);
36689 this.split.el.setTop(box.y+box.height);
36690 this.split.el.setWidth(box.width);
36692 if(this.collapsed){
36693 this.updateBody(box.width, null);
36695 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36703 Roo.bootstrap.layout.South = function(config){
36704 config.region = 'south';
36705 config.cursor = 's-resize';
36706 Roo.bootstrap.layout.Split.call(this, config);
36708 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36709 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36710 this.split.el.addClass("roo-layout-split-v");
36712 var size = config.initialSize || config.height;
36713 if(typeof size != "undefined"){
36714 this.el.setHeight(size);
36718 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36719 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36720 getBox : function(){
36721 if(this.collapsed){
36722 return this.collapsedEl.getBox();
36724 var box = this.el.getBox();
36726 var sh = this.split.el.getHeight();
36733 updateBox : function(box){
36734 if(this.split && !this.collapsed){
36735 var sh = this.split.el.getHeight();
36738 this.split.el.setLeft(box.x);
36739 this.split.el.setTop(box.y-sh);
36740 this.split.el.setWidth(box.width);
36742 if(this.collapsed){
36743 this.updateBody(box.width, null);
36745 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36749 Roo.bootstrap.layout.East = function(config){
36750 config.region = "east";
36751 config.cursor = "e-resize";
36752 Roo.bootstrap.layout.Split.call(this, config);
36754 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36755 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36756 this.split.el.addClass("roo-layout-split-h");
36758 var size = config.initialSize || config.width;
36759 if(typeof size != "undefined"){
36760 this.el.setWidth(size);
36763 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36764 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36765 getBox : function(){
36766 if(this.collapsed){
36767 return this.collapsedEl.getBox();
36769 var box = this.el.getBox();
36771 var sw = this.split.el.getWidth();
36778 updateBox : function(box){
36779 if(this.split && !this.collapsed){
36780 var sw = this.split.el.getWidth();
36782 this.split.el.setLeft(box.x);
36783 this.split.el.setTop(box.y);
36784 this.split.el.setHeight(box.height);
36787 if(this.collapsed){
36788 this.updateBody(null, box.height);
36790 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36794 Roo.bootstrap.layout.West = function(config){
36795 config.region = "west";
36796 config.cursor = "w-resize";
36798 Roo.bootstrap.layout.Split.call(this, config);
36800 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36801 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36802 this.split.el.addClass("roo-layout-split-h");
36806 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36807 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36809 onRender: function(ctr, pos)
36811 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36812 var size = this.config.initialSize || this.config.width;
36813 if(typeof size != "undefined"){
36814 this.el.setWidth(size);
36818 getBox : function(){
36819 if(this.collapsed){
36820 return this.collapsedEl.getBox();
36822 var box = this.el.getBox();
36824 box.width += this.split.el.getWidth();
36829 updateBox : function(box){
36830 if(this.split && !this.collapsed){
36831 var sw = this.split.el.getWidth();
36833 this.split.el.setLeft(box.x+box.width);
36834 this.split.el.setTop(box.y);
36835 this.split.el.setHeight(box.height);
36837 if(this.collapsed){
36838 this.updateBody(null, box.height);
36840 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36843 Roo.namespace("Roo.bootstrap.panel");/*
36845 * Ext JS Library 1.1.1
36846 * Copyright(c) 2006-2007, Ext JS, LLC.
36848 * Originally Released Under LGPL - original licence link has changed is not relivant.
36851 * <script type="text/javascript">
36854 * @class Roo.ContentPanel
36855 * @extends Roo.util.Observable
36856 * A basic ContentPanel element.
36857 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36858 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36859 * @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
36860 * @cfg {Boolean} closable True if the panel can be closed/removed
36861 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36862 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36863 * @cfg {Toolbar} toolbar A toolbar for this panel
36864 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36865 * @cfg {String} title The title for this panel
36866 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36867 * @cfg {String} url Calls {@link #setUrl} with this value
36868 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36869 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36870 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36871 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36872 * @cfg {Boolean} badges render the badges
36875 * Create a new ContentPanel.
36876 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36877 * @param {String/Object} config A string to set only the title or a config object
36878 * @param {String} content (optional) Set the HTML content for this panel
36879 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36881 Roo.bootstrap.panel.Content = function( config){
36883 this.tpl = config.tpl || false;
36885 var el = config.el;
36886 var content = config.content;
36888 if(config.autoCreate){ // xtype is available if this is called from factory
36891 this.el = Roo.get(el);
36892 if(!this.el && config && config.autoCreate){
36893 if(typeof config.autoCreate == "object"){
36894 if(!config.autoCreate.id){
36895 config.autoCreate.id = config.id||el;
36897 this.el = Roo.DomHelper.append(document.body,
36898 config.autoCreate, true);
36900 var elcfg = { tag: "div",
36901 cls: "roo-layout-inactive-content",
36905 elcfg.html = config.html;
36909 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36912 this.closable = false;
36913 this.loaded = false;
36914 this.active = false;
36917 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36919 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36921 this.wrapEl = this.el; //this.el.wrap();
36923 if (config.toolbar.items) {
36924 ti = config.toolbar.items ;
36925 delete config.toolbar.items ;
36929 this.toolbar.render(this.wrapEl, 'before');
36930 for(var i =0;i < ti.length;i++) {
36931 // Roo.log(['add child', items[i]]);
36932 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36934 this.toolbar.items = nitems;
36935 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36936 delete config.toolbar;
36940 // xtype created footer. - not sure if will work as we normally have to render first..
36941 if (this.footer && !this.footer.el && this.footer.xtype) {
36942 if (!this.wrapEl) {
36943 this.wrapEl = this.el.wrap();
36946 this.footer.container = this.wrapEl.createChild();
36948 this.footer = Roo.factory(this.footer, Roo);
36953 if(typeof config == "string"){
36954 this.title = config;
36956 Roo.apply(this, config);
36960 this.resizeEl = Roo.get(this.resizeEl, true);
36962 this.resizeEl = this.el;
36964 // handle view.xtype
36972 * Fires when this panel is activated.
36973 * @param {Roo.ContentPanel} this
36977 * @event deactivate
36978 * Fires when this panel is activated.
36979 * @param {Roo.ContentPanel} this
36981 "deactivate" : true,
36985 * Fires when this panel is resized if fitToFrame is true.
36986 * @param {Roo.ContentPanel} this
36987 * @param {Number} width The width after any component adjustments
36988 * @param {Number} height The height after any component adjustments
36994 * Fires when this tab is created
36995 * @param {Roo.ContentPanel} this
37006 if(this.autoScroll){
37007 this.resizeEl.setStyle("overflow", "auto");
37009 // fix randome scrolling
37010 //this.el.on('scroll', function() {
37011 // Roo.log('fix random scolling');
37012 // this.scrollTo('top',0);
37015 content = content || this.content;
37017 this.setContent(content);
37019 if(config && config.url){
37020 this.setUrl(this.url, this.params, this.loadOnce);
37025 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37027 if (this.view && typeof(this.view.xtype) != 'undefined') {
37028 this.view.el = this.el.appendChild(document.createElement("div"));
37029 this.view = Roo.factory(this.view);
37030 this.view.render && this.view.render(false, '');
37034 this.fireEvent('render', this);
37037 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37041 setRegion : function(region){
37042 this.region = region;
37043 this.setActiveClass(region && !this.background);
37047 setActiveClass: function(state)
37050 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37051 this.el.setStyle('position','relative');
37053 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37054 this.el.setStyle('position', 'absolute');
37059 * Returns the toolbar for this Panel if one was configured.
37060 * @return {Roo.Toolbar}
37062 getToolbar : function(){
37063 return this.toolbar;
37066 setActiveState : function(active)
37068 this.active = active;
37069 this.setActiveClass(active);
37071 if(this.fireEvent("deactivate", this) === false){
37076 this.fireEvent("activate", this);
37080 * Updates this panel's element
37081 * @param {String} content The new content
37082 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37084 setContent : function(content, loadScripts){
37085 this.el.update(content, loadScripts);
37088 ignoreResize : function(w, h){
37089 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37092 this.lastSize = {width: w, height: h};
37097 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37098 * @return {Roo.UpdateManager} The UpdateManager
37100 getUpdateManager : function(){
37101 return this.el.getUpdateManager();
37104 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37105 * @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:
37108 url: "your-url.php",
37109 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37110 callback: yourFunction,
37111 scope: yourObject, //(optional scope)
37114 text: "Loading...",
37119 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37120 * 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.
37121 * @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}
37122 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37123 * @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.
37124 * @return {Roo.ContentPanel} this
37127 var um = this.el.getUpdateManager();
37128 um.update.apply(um, arguments);
37134 * 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.
37135 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37136 * @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)
37137 * @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)
37138 * @return {Roo.UpdateManager} The UpdateManager
37140 setUrl : function(url, params, loadOnce){
37141 if(this.refreshDelegate){
37142 this.removeListener("activate", this.refreshDelegate);
37144 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37145 this.on("activate", this.refreshDelegate);
37146 return this.el.getUpdateManager();
37149 _handleRefresh : function(url, params, loadOnce){
37150 if(!loadOnce || !this.loaded){
37151 var updater = this.el.getUpdateManager();
37152 updater.update(url, params, this._setLoaded.createDelegate(this));
37156 _setLoaded : function(){
37157 this.loaded = true;
37161 * Returns this panel's id
37164 getId : function(){
37169 * Returns this panel's element - used by regiosn to add.
37170 * @return {Roo.Element}
37172 getEl : function(){
37173 return this.wrapEl || this.el;
37178 adjustForComponents : function(width, height)
37180 //Roo.log('adjustForComponents ');
37181 if(this.resizeEl != this.el){
37182 width -= this.el.getFrameWidth('lr');
37183 height -= this.el.getFrameWidth('tb');
37186 var te = this.toolbar.getEl();
37187 te.setWidth(width);
37188 height -= te.getHeight();
37191 var te = this.footer.getEl();
37192 te.setWidth(width);
37193 height -= te.getHeight();
37197 if(this.adjustments){
37198 width += this.adjustments[0];
37199 height += this.adjustments[1];
37201 return {"width": width, "height": height};
37204 setSize : function(width, height){
37205 if(this.fitToFrame && !this.ignoreResize(width, height)){
37206 if(this.fitContainer && this.resizeEl != this.el){
37207 this.el.setSize(width, height);
37209 var size = this.adjustForComponents(width, height);
37210 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37211 this.fireEvent('resize', this, size.width, size.height);
37216 * Returns this panel's title
37219 getTitle : function(){
37221 if (typeof(this.title) != 'object') {
37226 for (var k in this.title) {
37227 if (!this.title.hasOwnProperty(k)) {
37231 if (k.indexOf('-') >= 0) {
37232 var s = k.split('-');
37233 for (var i = 0; i<s.length; i++) {
37234 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37237 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37244 * Set this panel's title
37245 * @param {String} title
37247 setTitle : function(title){
37248 this.title = title;
37250 this.region.updatePanelTitle(this, title);
37255 * Returns true is this panel was configured to be closable
37256 * @return {Boolean}
37258 isClosable : function(){
37259 return this.closable;
37262 beforeSlide : function(){
37264 this.resizeEl.clip();
37267 afterSlide : function(){
37269 this.resizeEl.unclip();
37273 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37274 * Will fail silently if the {@link #setUrl} method has not been called.
37275 * This does not activate the panel, just updates its content.
37277 refresh : function(){
37278 if(this.refreshDelegate){
37279 this.loaded = false;
37280 this.refreshDelegate();
37285 * Destroys this panel
37287 destroy : function(){
37288 this.el.removeAllListeners();
37289 var tempEl = document.createElement("span");
37290 tempEl.appendChild(this.el.dom);
37291 tempEl.innerHTML = "";
37297 * form - if the content panel contains a form - this is a reference to it.
37298 * @type {Roo.form.Form}
37302 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37303 * This contains a reference to it.
37309 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37319 * @param {Object} cfg Xtype definition of item to add.
37323 getChildContainer: function () {
37324 return this.getEl();
37329 var ret = new Roo.factory(cfg);
37334 if (cfg.xtype.match(/^Form$/)) {
37337 //if (this.footer) {
37338 // el = this.footer.container.insertSibling(false, 'before');
37340 el = this.el.createChild();
37343 this.form = new Roo.form.Form(cfg);
37346 if ( this.form.allItems.length) {
37347 this.form.render(el.dom);
37351 // should only have one of theses..
37352 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37353 // views.. should not be just added - used named prop 'view''
37355 cfg.el = this.el.appendChild(document.createElement("div"));
37358 var ret = new Roo.factory(cfg);
37360 ret.render && ret.render(false, ''); // render blank..
37370 * @class Roo.bootstrap.panel.Grid
37371 * @extends Roo.bootstrap.panel.Content
37373 * Create a new GridPanel.
37374 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37375 * @param {Object} config A the config object
37381 Roo.bootstrap.panel.Grid = function(config)
37385 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37386 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37388 config.el = this.wrapper;
37389 //this.el = this.wrapper;
37391 if (config.container) {
37392 // ctor'ed from a Border/panel.grid
37395 this.wrapper.setStyle("overflow", "hidden");
37396 this.wrapper.addClass('roo-grid-container');
37401 if(config.toolbar){
37402 var tool_el = this.wrapper.createChild();
37403 this.toolbar = Roo.factory(config.toolbar);
37405 if (config.toolbar.items) {
37406 ti = config.toolbar.items ;
37407 delete config.toolbar.items ;
37411 this.toolbar.render(tool_el);
37412 for(var i =0;i < ti.length;i++) {
37413 // Roo.log(['add child', items[i]]);
37414 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37416 this.toolbar.items = nitems;
37418 delete config.toolbar;
37421 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37422 config.grid.scrollBody = true;;
37423 config.grid.monitorWindowResize = false; // turn off autosizing
37424 config.grid.autoHeight = false;
37425 config.grid.autoWidth = false;
37427 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37429 if (config.background) {
37430 // render grid on panel activation (if panel background)
37431 this.on('activate', function(gp) {
37432 if (!gp.grid.rendered) {
37433 gp.grid.render(this.wrapper);
37434 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37439 this.grid.render(this.wrapper);
37440 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37443 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37444 // ??? needed ??? config.el = this.wrapper;
37449 // xtype created footer. - not sure if will work as we normally have to render first..
37450 if (this.footer && !this.footer.el && this.footer.xtype) {
37452 var ctr = this.grid.getView().getFooterPanel(true);
37453 this.footer.dataSource = this.grid.dataSource;
37454 this.footer = Roo.factory(this.footer, Roo);
37455 this.footer.render(ctr);
37465 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37466 getId : function(){
37467 return this.grid.id;
37471 * Returns the grid for this panel
37472 * @return {Roo.bootstrap.Table}
37474 getGrid : function(){
37478 setSize : function(width, height){
37479 if(!this.ignoreResize(width, height)){
37480 var grid = this.grid;
37481 var size = this.adjustForComponents(width, height);
37482 var gridel = grid.getGridEl();
37483 gridel.setSize(size.width, size.height);
37485 var thd = grid.getGridEl().select('thead',true).first();
37486 var tbd = grid.getGridEl().select('tbody', true).first();
37488 tbd.setSize(width, height - thd.getHeight());
37497 beforeSlide : function(){
37498 this.grid.getView().scroller.clip();
37501 afterSlide : function(){
37502 this.grid.getView().scroller.unclip();
37505 destroy : function(){
37506 this.grid.destroy();
37508 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37513 * @class Roo.bootstrap.panel.Nest
37514 * @extends Roo.bootstrap.panel.Content
37516 * Create a new Panel, that can contain a layout.Border.
37519 * @param {Roo.BorderLayout} layout The layout for this panel
37520 * @param {String/Object} config A string to set only the title or a config object
37522 Roo.bootstrap.panel.Nest = function(config)
37524 // construct with only one argument..
37525 /* FIXME - implement nicer consturctors
37526 if (layout.layout) {
37528 layout = config.layout;
37529 delete config.layout;
37531 if (layout.xtype && !layout.getEl) {
37532 // then layout needs constructing..
37533 layout = Roo.factory(layout, Roo);
37537 config.el = config.layout.getEl();
37539 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37541 config.layout.monitorWindowResize = false; // turn off autosizing
37542 this.layout = config.layout;
37543 this.layout.getEl().addClass("roo-layout-nested-layout");
37550 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37552 setSize : function(width, height){
37553 if(!this.ignoreResize(width, height)){
37554 var size = this.adjustForComponents(width, height);
37555 var el = this.layout.getEl();
37556 if (size.height < 1) {
37557 el.setWidth(size.width);
37559 el.setSize(size.width, size.height);
37561 var touch = el.dom.offsetWidth;
37562 this.layout.layout();
37563 // ie requires a double layout on the first pass
37564 if(Roo.isIE && !this.initialized){
37565 this.initialized = true;
37566 this.layout.layout();
37571 // activate all subpanels if not currently active..
37573 setActiveState : function(active){
37574 this.active = active;
37575 this.setActiveClass(active);
37578 this.fireEvent("deactivate", this);
37582 this.fireEvent("activate", this);
37583 // not sure if this should happen before or after..
37584 if (!this.layout) {
37585 return; // should not happen..
37588 for (var r in this.layout.regions) {
37589 reg = this.layout.getRegion(r);
37590 if (reg.getActivePanel()) {
37591 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37592 reg.setActivePanel(reg.getActivePanel());
37595 if (!reg.panels.length) {
37598 reg.showPanel(reg.getPanel(0));
37607 * Returns the nested BorderLayout for this panel
37608 * @return {Roo.BorderLayout}
37610 getLayout : function(){
37611 return this.layout;
37615 * Adds a xtype elements to the layout of the nested panel
37619 xtype : 'ContentPanel',
37626 xtype : 'NestedLayoutPanel',
37632 items : [ ... list of content panels or nested layout panels.. ]
37636 * @param {Object} cfg Xtype definition of item to add.
37638 addxtype : function(cfg) {
37639 return this.layout.addxtype(cfg);
37644 * Ext JS Library 1.1.1
37645 * Copyright(c) 2006-2007, Ext JS, LLC.
37647 * Originally Released Under LGPL - original licence link has changed is not relivant.
37650 * <script type="text/javascript">
37653 * @class Roo.TabPanel
37654 * @extends Roo.util.Observable
37655 * A lightweight tab container.
37659 // basic tabs 1, built from existing content
37660 var tabs = new Roo.TabPanel("tabs1");
37661 tabs.addTab("script", "View Script");
37662 tabs.addTab("markup", "View Markup");
37663 tabs.activate("script");
37665 // more advanced tabs, built from javascript
37666 var jtabs = new Roo.TabPanel("jtabs");
37667 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37669 // set up the UpdateManager
37670 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37671 var updater = tab2.getUpdateManager();
37672 updater.setDefaultUrl("ajax1.htm");
37673 tab2.on('activate', updater.refresh, updater, true);
37675 // Use setUrl for Ajax loading
37676 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37677 tab3.setUrl("ajax2.htm", null, true);
37680 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37683 jtabs.activate("jtabs-1");
37686 * Create a new TabPanel.
37687 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37688 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37690 Roo.bootstrap.panel.Tabs = function(config){
37692 * The container element for this TabPanel.
37693 * @type Roo.Element
37695 this.el = Roo.get(config.el);
37698 if(typeof config == "boolean"){
37699 this.tabPosition = config ? "bottom" : "top";
37701 Roo.apply(this, config);
37705 if(this.tabPosition == "bottom"){
37706 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37707 this.el.addClass("roo-tabs-bottom");
37709 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37710 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37711 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37713 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37715 if(this.tabPosition != "bottom"){
37716 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37717 * @type Roo.Element
37719 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37720 this.el.addClass("roo-tabs-top");
37724 this.bodyEl.setStyle("position", "relative");
37726 this.active = null;
37727 this.activateDelegate = this.activate.createDelegate(this);
37732 * Fires when the active tab changes
37733 * @param {Roo.TabPanel} this
37734 * @param {Roo.TabPanelItem} activePanel The new active tab
37738 * @event beforetabchange
37739 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37740 * @param {Roo.TabPanel} this
37741 * @param {Object} e Set cancel to true on this object to cancel the tab change
37742 * @param {Roo.TabPanelItem} tab The tab being changed to
37744 "beforetabchange" : true
37747 Roo.EventManager.onWindowResize(this.onResize, this);
37748 this.cpad = this.el.getPadding("lr");
37749 this.hiddenCount = 0;
37752 // toolbar on the tabbar support...
37753 if (this.toolbar) {
37754 alert("no toolbar support yet");
37755 this.toolbar = false;
37757 var tcfg = this.toolbar;
37758 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37759 this.toolbar = new Roo.Toolbar(tcfg);
37760 if (Roo.isSafari) {
37761 var tbl = tcfg.container.child('table', true);
37762 tbl.setAttribute('width', '100%');
37770 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37773 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37775 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37777 tabPosition : "top",
37779 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37781 currentTabWidth : 0,
37783 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37787 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37791 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37793 preferredTabWidth : 175,
37795 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37797 resizeTabs : false,
37799 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37801 monitorResize : true,
37803 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37808 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37809 * @param {String} id The id of the div to use <b>or create</b>
37810 * @param {String} text The text for the tab
37811 * @param {String} content (optional) Content to put in the TabPanelItem body
37812 * @param {Boolean} closable (optional) True to create a close icon on the tab
37813 * @return {Roo.TabPanelItem} The created TabPanelItem
37815 addTab : function(id, text, content, closable, tpl)
37817 var item = new Roo.bootstrap.panel.TabItem({
37821 closable : closable,
37824 this.addTabItem(item);
37826 item.setContent(content);
37832 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37833 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37834 * @return {Roo.TabPanelItem}
37836 getTab : function(id){
37837 return this.items[id];
37841 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37842 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37844 hideTab : function(id){
37845 var t = this.items[id];
37848 this.hiddenCount++;
37849 this.autoSizeTabs();
37854 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37855 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37857 unhideTab : function(id){
37858 var t = this.items[id];
37860 t.setHidden(false);
37861 this.hiddenCount--;
37862 this.autoSizeTabs();
37867 * Adds an existing {@link Roo.TabPanelItem}.
37868 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37870 addTabItem : function(item){
37871 this.items[item.id] = item;
37872 this.items.push(item);
37873 // if(this.resizeTabs){
37874 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37875 // this.autoSizeTabs();
37877 // item.autoSize();
37882 * Removes a {@link Roo.TabPanelItem}.
37883 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37885 removeTab : function(id){
37886 var items = this.items;
37887 var tab = items[id];
37888 if(!tab) { return; }
37889 var index = items.indexOf(tab);
37890 if(this.active == tab && items.length > 1){
37891 var newTab = this.getNextAvailable(index);
37896 this.stripEl.dom.removeChild(tab.pnode.dom);
37897 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37898 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37900 items.splice(index, 1);
37901 delete this.items[tab.id];
37902 tab.fireEvent("close", tab);
37903 tab.purgeListeners();
37904 this.autoSizeTabs();
37907 getNextAvailable : function(start){
37908 var items = this.items;
37910 // look for a next tab that will slide over to
37911 // replace the one being removed
37912 while(index < items.length){
37913 var item = items[++index];
37914 if(item && !item.isHidden()){
37918 // if one isn't found select the previous tab (on the left)
37921 var item = items[--index];
37922 if(item && !item.isHidden()){
37930 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37931 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37933 disableTab : function(id){
37934 var tab = this.items[id];
37935 if(tab && this.active != tab){
37941 * Enables a {@link Roo.TabPanelItem} that is disabled.
37942 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37944 enableTab : function(id){
37945 var tab = this.items[id];
37950 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37951 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37952 * @return {Roo.TabPanelItem} The TabPanelItem.
37954 activate : function(id){
37955 var tab = this.items[id];
37959 if(tab == this.active || tab.disabled){
37963 this.fireEvent("beforetabchange", this, e, tab);
37964 if(e.cancel !== true && !tab.disabled){
37966 this.active.hide();
37968 this.active = this.items[id];
37969 this.active.show();
37970 this.fireEvent("tabchange", this, this.active);
37976 * Gets the active {@link Roo.TabPanelItem}.
37977 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37979 getActiveTab : function(){
37980 return this.active;
37984 * Updates the tab body element to fit the height of the container element
37985 * for overflow scrolling
37986 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37988 syncHeight : function(targetHeight){
37989 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37990 var bm = this.bodyEl.getMargins();
37991 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37992 this.bodyEl.setHeight(newHeight);
37996 onResize : function(){
37997 if(this.monitorResize){
37998 this.autoSizeTabs();
38003 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38005 beginUpdate : function(){
38006 this.updating = true;
38010 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38012 endUpdate : function(){
38013 this.updating = false;
38014 this.autoSizeTabs();
38018 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38020 autoSizeTabs : function(){
38021 var count = this.items.length;
38022 var vcount = count - this.hiddenCount;
38023 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38026 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38027 var availWidth = Math.floor(w / vcount);
38028 var b = this.stripBody;
38029 if(b.getWidth() > w){
38030 var tabs = this.items;
38031 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38032 if(availWidth < this.minTabWidth){
38033 /*if(!this.sleft){ // incomplete scrolling code
38034 this.createScrollButtons();
38037 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38040 if(this.currentTabWidth < this.preferredTabWidth){
38041 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38047 * Returns the number of tabs in this TabPanel.
38050 getCount : function(){
38051 return this.items.length;
38055 * Resizes all the tabs to the passed width
38056 * @param {Number} The new width
38058 setTabWidth : function(width){
38059 this.currentTabWidth = width;
38060 for(var i = 0, len = this.items.length; i < len; i++) {
38061 if(!this.items[i].isHidden()) {
38062 this.items[i].setWidth(width);
38068 * Destroys this TabPanel
38069 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38071 destroy : function(removeEl){
38072 Roo.EventManager.removeResizeListener(this.onResize, this);
38073 for(var i = 0, len = this.items.length; i < len; i++){
38074 this.items[i].purgeListeners();
38076 if(removeEl === true){
38077 this.el.update("");
38082 createStrip : function(container)
38084 var strip = document.createElement("nav");
38085 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38086 container.appendChild(strip);
38090 createStripList : function(strip)
38092 // div wrapper for retard IE
38093 // returns the "tr" element.
38094 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38095 //'<div class="x-tabs-strip-wrap">'+
38096 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38097 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38098 return strip.firstChild; //.firstChild.firstChild.firstChild;
38100 createBody : function(container)
38102 var body = document.createElement("div");
38103 Roo.id(body, "tab-body");
38104 //Roo.fly(body).addClass("x-tabs-body");
38105 Roo.fly(body).addClass("tab-content");
38106 container.appendChild(body);
38109 createItemBody :function(bodyEl, id){
38110 var body = Roo.getDom(id);
38112 body = document.createElement("div");
38115 //Roo.fly(body).addClass("x-tabs-item-body");
38116 Roo.fly(body).addClass("tab-pane");
38117 bodyEl.insertBefore(body, bodyEl.firstChild);
38121 createStripElements : function(stripEl, text, closable, tpl)
38123 var td = document.createElement("li"); // was td..
38126 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38129 stripEl.appendChild(td);
38131 td.className = "x-tabs-closable";
38132 if(!this.closeTpl){
38133 this.closeTpl = new Roo.Template(
38134 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38135 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38136 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38139 var el = this.closeTpl.overwrite(td, {"text": text});
38140 var close = el.getElementsByTagName("div")[0];
38141 var inner = el.getElementsByTagName("em")[0];
38142 return {"el": el, "close": close, "inner": inner};
38145 // not sure what this is..
38146 // if(!this.tabTpl){
38147 //this.tabTpl = new Roo.Template(
38148 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38149 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38151 // this.tabTpl = new Roo.Template(
38152 // '<a href="#">' +
38153 // '<span unselectable="on"' +
38154 // (this.disableTooltips ? '' : ' title="{text}"') +
38155 // ' >{text}</span></a>'
38161 var template = tpl || this.tabTpl || false;
38165 template = new Roo.Template(
38167 '<span unselectable="on"' +
38168 (this.disableTooltips ? '' : ' title="{text}"') +
38169 ' >{text}</span></a>'
38173 switch (typeof(template)) {
38177 template = new Roo.Template(template);
38183 var el = template.overwrite(td, {"text": text});
38185 var inner = el.getElementsByTagName("span")[0];
38187 return {"el": el, "inner": inner};
38195 * @class Roo.TabPanelItem
38196 * @extends Roo.util.Observable
38197 * Represents an individual item (tab plus body) in a TabPanel.
38198 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38199 * @param {String} id The id of this TabPanelItem
38200 * @param {String} text The text for the tab of this TabPanelItem
38201 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38203 Roo.bootstrap.panel.TabItem = function(config){
38205 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38206 * @type Roo.TabPanel
38208 this.tabPanel = config.panel;
38210 * The id for this TabPanelItem
38213 this.id = config.id;
38215 this.disabled = false;
38217 this.text = config.text;
38219 this.loaded = false;
38220 this.closable = config.closable;
38223 * The body element for this TabPanelItem.
38224 * @type Roo.Element
38226 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38227 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38228 this.bodyEl.setStyle("display", "block");
38229 this.bodyEl.setStyle("zoom", "1");
38230 //this.hideAction();
38232 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38234 this.el = Roo.get(els.el);
38235 this.inner = Roo.get(els.inner, true);
38236 this.textEl = Roo.get(this.el.dom.firstChild, true);
38237 this.pnode = Roo.get(els.el.parentNode, true);
38238 // this.el.on("mousedown", this.onTabMouseDown, this);
38239 this.el.on("click", this.onTabClick, this);
38241 if(config.closable){
38242 var c = Roo.get(els.close, true);
38243 c.dom.title = this.closeText;
38244 c.addClassOnOver("close-over");
38245 c.on("click", this.closeClick, this);
38251 * Fires when this tab becomes the active tab.
38252 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38253 * @param {Roo.TabPanelItem} this
38257 * @event beforeclose
38258 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38259 * @param {Roo.TabPanelItem} this
38260 * @param {Object} e Set cancel to true on this object to cancel the close.
38262 "beforeclose": true,
38265 * Fires when this tab is closed.
38266 * @param {Roo.TabPanelItem} this
38270 * @event deactivate
38271 * Fires when this tab is no longer the active tab.
38272 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38273 * @param {Roo.TabPanelItem} this
38275 "deactivate" : true
38277 this.hidden = false;
38279 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38282 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38284 purgeListeners : function(){
38285 Roo.util.Observable.prototype.purgeListeners.call(this);
38286 this.el.removeAllListeners();
38289 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38292 this.pnode.addClass("active");
38295 this.tabPanel.stripWrap.repaint();
38297 this.fireEvent("activate", this.tabPanel, this);
38301 * Returns true if this tab is the active tab.
38302 * @return {Boolean}
38304 isActive : function(){
38305 return this.tabPanel.getActiveTab() == this;
38309 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38312 this.pnode.removeClass("active");
38314 this.fireEvent("deactivate", this.tabPanel, this);
38317 hideAction : function(){
38318 this.bodyEl.hide();
38319 this.bodyEl.setStyle("position", "absolute");
38320 this.bodyEl.setLeft("-20000px");
38321 this.bodyEl.setTop("-20000px");
38324 showAction : function(){
38325 this.bodyEl.setStyle("position", "relative");
38326 this.bodyEl.setTop("");
38327 this.bodyEl.setLeft("");
38328 this.bodyEl.show();
38332 * Set the tooltip for the tab.
38333 * @param {String} tooltip The tab's tooltip
38335 setTooltip : function(text){
38336 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38337 this.textEl.dom.qtip = text;
38338 this.textEl.dom.removeAttribute('title');
38340 this.textEl.dom.title = text;
38344 onTabClick : function(e){
38345 e.preventDefault();
38346 this.tabPanel.activate(this.id);
38349 onTabMouseDown : function(e){
38350 e.preventDefault();
38351 this.tabPanel.activate(this.id);
38354 getWidth : function(){
38355 return this.inner.getWidth();
38358 setWidth : function(width){
38359 var iwidth = width - this.pnode.getPadding("lr");
38360 this.inner.setWidth(iwidth);
38361 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38362 this.pnode.setWidth(width);
38366 * Show or hide the tab
38367 * @param {Boolean} hidden True to hide or false to show.
38369 setHidden : function(hidden){
38370 this.hidden = hidden;
38371 this.pnode.setStyle("display", hidden ? "none" : "");
38375 * Returns true if this tab is "hidden"
38376 * @return {Boolean}
38378 isHidden : function(){
38379 return this.hidden;
38383 * Returns the text for this tab
38386 getText : function(){
38390 autoSize : function(){
38391 //this.el.beginMeasure();
38392 this.textEl.setWidth(1);
38394 * #2804 [new] Tabs in Roojs
38395 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38397 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38398 //this.el.endMeasure();
38402 * Sets the text for the tab (Note: this also sets the tooltip text)
38403 * @param {String} text The tab's text and tooltip
38405 setText : function(text){
38407 this.textEl.update(text);
38408 this.setTooltip(text);
38409 //if(!this.tabPanel.resizeTabs){
38410 // this.autoSize();
38414 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38416 activate : function(){
38417 this.tabPanel.activate(this.id);
38421 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38423 disable : function(){
38424 if(this.tabPanel.active != this){
38425 this.disabled = true;
38426 this.pnode.addClass("disabled");
38431 * Enables this TabPanelItem if it was previously disabled.
38433 enable : function(){
38434 this.disabled = false;
38435 this.pnode.removeClass("disabled");
38439 * Sets the content for this TabPanelItem.
38440 * @param {String} content The content
38441 * @param {Boolean} loadScripts true to look for and load scripts
38443 setContent : function(content, loadScripts){
38444 this.bodyEl.update(content, loadScripts);
38448 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38449 * @return {Roo.UpdateManager} The UpdateManager
38451 getUpdateManager : function(){
38452 return this.bodyEl.getUpdateManager();
38456 * Set a URL to be used to load the content for this TabPanelItem.
38457 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38458 * @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)
38459 * @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)
38460 * @return {Roo.UpdateManager} The UpdateManager
38462 setUrl : function(url, params, loadOnce){
38463 if(this.refreshDelegate){
38464 this.un('activate', this.refreshDelegate);
38466 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38467 this.on("activate", this.refreshDelegate);
38468 return this.bodyEl.getUpdateManager();
38472 _handleRefresh : function(url, params, loadOnce){
38473 if(!loadOnce || !this.loaded){
38474 var updater = this.bodyEl.getUpdateManager();
38475 updater.update(url, params, this._setLoaded.createDelegate(this));
38480 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38481 * Will fail silently if the setUrl method has not been called.
38482 * This does not activate the panel, just updates its content.
38484 refresh : function(){
38485 if(this.refreshDelegate){
38486 this.loaded = false;
38487 this.refreshDelegate();
38492 _setLoaded : function(){
38493 this.loaded = true;
38497 closeClick : function(e){
38500 this.fireEvent("beforeclose", this, o);
38501 if(o.cancel !== true){
38502 this.tabPanel.removeTab(this.id);
38506 * The text displayed in the tooltip for the close icon.
38509 closeText : "Close this tab"
38512 * This script refer to:
38513 * Title: International Telephone Input
38514 * Author: Jack O'Connor
38515 * Code version: v12.1.12
38516 * Availability: https://github.com/jackocnr/intl-tel-input.git
38519 Roo.bootstrap.PhoneInputData = function() {
38522 "Afghanistan (افغانستان)",
38527 "Albania (Shqipëri)",
38532 "Algeria (الجزائر)",
38557 "Antigua and Barbuda",
38567 "Armenia (Հայաստան)",
38583 "Austria (Österreich)",
38588 "Azerbaijan (Azərbaycan)",
38598 "Bahrain (البحرين)",
38603 "Bangladesh (বাংলাদেশ)",
38613 "Belarus (Беларусь)",
38618 "Belgium (België)",
38648 "Bosnia and Herzegovina (Босна и Херцеговина)",
38663 "British Indian Ocean Territory",
38668 "British Virgin Islands",
38678 "Bulgaria (България)",
38688 "Burundi (Uburundi)",
38693 "Cambodia (កម្ពុជា)",
38698 "Cameroon (Cameroun)",
38707 ["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"]
38710 "Cape Verde (Kabu Verdi)",
38715 "Caribbean Netherlands",
38726 "Central African Republic (République centrafricaine)",
38746 "Christmas Island",
38752 "Cocos (Keeling) Islands",
38763 "Comoros (جزر القمر)",
38768 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38773 "Congo (Republic) (Congo-Brazzaville)",
38793 "Croatia (Hrvatska)",
38814 "Czech Republic (Česká republika)",
38819 "Denmark (Danmark)",
38834 "Dominican Republic (República Dominicana)",
38838 ["809", "829", "849"]
38856 "Equatorial Guinea (Guinea Ecuatorial)",
38876 "Falkland Islands (Islas Malvinas)",
38881 "Faroe Islands (Føroyar)",
38902 "French Guiana (Guyane française)",
38907 "French Polynesia (Polynésie française)",
38922 "Georgia (საქართველო)",
38927 "Germany (Deutschland)",
38947 "Greenland (Kalaallit Nunaat)",
38984 "Guinea-Bissau (Guiné Bissau)",
39009 "Hungary (Magyarország)",
39014 "Iceland (Ísland)",
39034 "Iraq (العراق)",
39050 "Israel (ישראל)",
39077 "Jordan (الأردن)",
39082 "Kazakhstan (Казахстан)",
39103 "Kuwait (الكويت)",
39108 "Kyrgyzstan (Кыргызстан)",
39118 "Latvia (Latvija)",
39123 "Lebanon (لبنان)",
39138 "Libya (ليبيا)",
39148 "Lithuania (Lietuva)",
39163 "Macedonia (FYROM) (Македонија)",
39168 "Madagascar (Madagasikara)",
39198 "Marshall Islands",
39208 "Mauritania (موريتانيا)",
39213 "Mauritius (Moris)",
39234 "Moldova (Republica Moldova)",
39244 "Mongolia (Монгол)",
39249 "Montenegro (Crna Gora)",
39259 "Morocco (المغرب)",
39265 "Mozambique (Moçambique)",
39270 "Myanmar (Burma) (မြန်မာ)",
39275 "Namibia (Namibië)",
39290 "Netherlands (Nederland)",
39295 "New Caledonia (Nouvelle-Calédonie)",
39330 "North Korea (조선 민주주의 인민 공화국)",
39335 "Northern Mariana Islands",
39351 "Pakistan (پاکستان)",
39361 "Palestine (فلسطين)",
39371 "Papua New Guinea",
39413 "Réunion (La Réunion)",
39419 "Romania (România)",
39435 "Saint Barthélemy",
39446 "Saint Kitts and Nevis",
39456 "Saint Martin (Saint-Martin (partie française))",
39462 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39467 "Saint Vincent and the Grenadines",
39482 "São Tomé and Príncipe (São Tomé e Príncipe)",
39487 "Saudi Arabia (المملكة العربية السعودية)",
39492 "Senegal (Sénégal)",
39522 "Slovakia (Slovensko)",
39527 "Slovenia (Slovenija)",
39537 "Somalia (Soomaaliya)",
39547 "South Korea (대한민국)",
39552 "South Sudan (جنوب السودان)",
39562 "Sri Lanka (ශ්රී ලංකාව)",
39567 "Sudan (السودان)",
39577 "Svalbard and Jan Mayen",
39588 "Sweden (Sverige)",
39593 "Switzerland (Schweiz)",
39598 "Syria (سوريا)",
39643 "Trinidad and Tobago",
39648 "Tunisia (تونس)",
39653 "Turkey (Türkiye)",
39663 "Turks and Caicos Islands",
39673 "U.S. Virgin Islands",
39683 "Ukraine (Україна)",
39688 "United Arab Emirates (الإمارات العربية المتحدة)",
39710 "Uzbekistan (Oʻzbekiston)",
39720 "Vatican City (Città del Vaticano)",
39731 "Vietnam (Việt Nam)",
39736 "Wallis and Futuna (Wallis-et-Futuna)",
39741 "Western Sahara (الصحراء الغربية)",
39747 "Yemen (اليمن)",
39771 * This script refer to:
39772 * Title: International Telephone Input
39773 * Author: Jack O'Connor
39774 * Code version: v12.1.12
39775 * Availability: https://github.com/jackocnr/intl-tel-input.git
39779 * @class Roo.bootstrap.PhoneInput
39780 * @extends Roo.bootstrap.TriggerField
39781 * An input with International dial-code selection
39783 * @cfg {String} defaultDialCode default '+852'
39784 * @cfg {Array} preferedCountries default []
39787 * Create a new PhoneInput.
39788 * @param {Object} config Configuration options
39791 Roo.bootstrap.PhoneInput = function(config) {
39792 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39795 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39797 listWidth: undefined,
39799 selectedClass: 'active',
39801 invalidClass : "has-warning",
39803 validClass: 'has-success',
39805 allowed: '0123456789',
39808 * @cfg {String} defaultDialCode The default dial code when initializing the input
39810 defaultDialCode: '+852',
39813 * @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
39815 preferedCountries: false,
39817 getAutoCreate : function()
39819 var data = Roo.bootstrap.PhoneInputData();
39820 var align = this.labelAlign || this.parentLabelAlign();
39823 this.allCountries = [];
39824 this.dialCodeMapping = [];
39826 for (var i = 0; i < data.length; i++) {
39828 this.allCountries[i] = {
39832 priority: c[3] || 0,
39833 areaCodes: c[4] || null
39835 this.dialCodeMapping[c[2]] = {
39838 priority: c[3] || 0,
39839 areaCodes: c[4] || null
39851 cls : 'form-control tel-input',
39852 autocomplete: 'new-password'
39855 var hiddenInput = {
39858 cls: 'hidden-tel-input'
39862 hiddenInput.name = this.name;
39865 if (this.disabled) {
39866 input.disabled = true;
39869 var flag_container = {
39886 cls: this.hasFeedback ? 'has-feedback' : '',
39892 cls: 'dial-code-holder',
39899 cls: 'roo-select2-container input-group',
39906 if (this.fieldLabel.length) {
39909 tooltip: 'This field is required'
39915 cls: 'control-label',
39921 html: this.fieldLabel
39924 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39930 if(this.indicatorpos == 'right') {
39931 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39938 if(align == 'left') {
39946 if(this.labelWidth > 12){
39947 label.style = "width: " + this.labelWidth + 'px';
39949 if(this.labelWidth < 13 && this.labelmd == 0){
39950 this.labelmd = this.labelWidth;
39952 if(this.labellg > 0){
39953 label.cls += ' col-lg-' + this.labellg;
39954 input.cls += ' col-lg-' + (12 - this.labellg);
39956 if(this.labelmd > 0){
39957 label.cls += ' col-md-' + this.labelmd;
39958 container.cls += ' col-md-' + (12 - this.labelmd);
39960 if(this.labelsm > 0){
39961 label.cls += ' col-sm-' + this.labelsm;
39962 container.cls += ' col-sm-' + (12 - this.labelsm);
39964 if(this.labelxs > 0){
39965 label.cls += ' col-xs-' + this.labelxs;
39966 container.cls += ' col-xs-' + (12 - this.labelxs);
39976 var settings = this;
39978 ['xs','sm','md','lg'].map(function(size){
39979 if (settings[size]) {
39980 cfg.cls += ' col-' + size + '-' + settings[size];
39984 this.store = new Roo.data.Store({
39985 proxy : new Roo.data.MemoryProxy({}),
39986 reader : new Roo.data.JsonReader({
39997 'name' : 'dialCode',
40001 'name' : 'priority',
40005 'name' : 'areaCodes',
40012 if(!this.preferedCountries) {
40013 this.preferedCountries = [
40020 var p = this.preferedCountries.reverse();
40023 for (var i = 0; i < p.length; i++) {
40024 for (var j = 0; j < this.allCountries.length; j++) {
40025 if(this.allCountries[j].iso2 == p[i]) {
40026 var t = this.allCountries[j];
40027 this.allCountries.splice(j,1);
40028 this.allCountries.unshift(t);
40034 this.store.proxy.data = {
40036 data: this.allCountries
40042 initEvents : function()
40045 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40047 this.indicator = this.indicatorEl();
40048 this.flag = this.flagEl();
40049 this.dialCodeHolder = this.dialCodeHolderEl();
40051 this.trigger = this.el.select('div.flag-box',true).first();
40052 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40057 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40058 _this.list.setWidth(lw);
40061 this.list.on('mouseover', this.onViewOver, this);
40062 this.list.on('mousemove', this.onViewMove, this);
40063 this.inputEl().on("keyup", this.onKeyUp, this);
40065 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40067 this.view = new Roo.View(this.list, this.tpl, {
40068 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40071 this.view.on('click', this.onViewClick, this);
40072 this.setValue(this.defaultDialCode);
40075 onTriggerClick : function(e)
40077 Roo.log('trigger click');
40082 if(this.isExpanded()){
40084 this.hasFocus = false;
40086 this.store.load({});
40087 this.hasFocus = true;
40092 isExpanded : function()
40094 return this.list.isVisible();
40097 collapse : function()
40099 if(!this.isExpanded()){
40103 Roo.get(document).un('mousedown', this.collapseIf, this);
40104 Roo.get(document).un('mousewheel', this.collapseIf, this);
40105 this.fireEvent('collapse', this);
40109 expand : function()
40113 if(this.isExpanded() || !this.hasFocus){
40117 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40118 this.list.setWidth(lw);
40121 this.restrictHeight();
40123 Roo.get(document).on('mousedown', this.collapseIf, this);
40124 Roo.get(document).on('mousewheel', this.collapseIf, this);
40126 this.fireEvent('expand', this);
40129 restrictHeight : function()
40131 this.list.alignTo(this.inputEl(), this.listAlign);
40132 this.list.alignTo(this.inputEl(), this.listAlign);
40135 onViewOver : function(e, t)
40137 if(this.inKeyMode){
40140 var item = this.view.findItemFromChild(t);
40143 var index = this.view.indexOf(item);
40144 this.select(index, false);
40149 onViewClick : function(view, doFocus, el, e)
40151 var index = this.view.getSelectedIndexes()[0];
40153 var r = this.store.getAt(index);
40156 this.onSelect(r, index);
40158 if(doFocus !== false && !this.blockFocus){
40159 this.inputEl().focus();
40163 onViewMove : function(e, t)
40165 this.inKeyMode = false;
40168 select : function(index, scrollIntoView)
40170 this.selectedIndex = index;
40171 this.view.select(index);
40172 if(scrollIntoView !== false){
40173 var el = this.view.getNode(index);
40175 this.list.scrollChildIntoView(el, false);
40180 createList : function()
40182 this.list = Roo.get(document.body).createChild({
40184 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40185 style: 'display:none'
40188 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40191 collapseIf : function(e)
40193 var in_combo = e.within(this.el);
40194 var in_list = e.within(this.list);
40195 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40197 if (in_combo || in_list || is_list) {
40203 onSelect : function(record, index)
40205 if(this.fireEvent('beforeselect', this, record, index) !== false){
40207 this.setFlagClass(record.data.iso2);
40208 this.setDialCode(record.data.dialCode);
40209 this.hasFocus = false;
40211 this.fireEvent('select', this, record, index);
40215 flagEl : function()
40217 var flag = this.el.select('div.flag',true).first();
40224 dialCodeHolderEl : function()
40226 var d = this.el.select('input.dial-code-holder',true).first();
40233 setDialCode : function(v)
40235 this.dialCodeHolder.dom.value = '+'+v;
40238 setFlagClass : function(n)
40240 this.flag.dom.className = 'flag '+n;
40243 getValue : function()
40245 var v = this.inputEl().getValue();
40246 if(this.dialCodeHolder) {
40247 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40252 setValue : function(v)
40254 var d = this.getDialCode(v);
40256 //invalid dial code
40257 if(v.length == 0 || !d || d.length == 0) {
40259 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40260 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40266 this.setFlagClass(this.dialCodeMapping[d].iso2);
40267 this.setDialCode(d);
40268 this.inputEl().dom.value = v.replace('+'+d,'');
40269 this.hiddenEl().dom.value = this.getValue();
40274 getDialCode : function(v)
40278 if (v.length == 0) {
40279 return this.dialCodeHolder.dom.value;
40283 if (v.charAt(0) != "+") {
40286 var numericChars = "";
40287 for (var i = 1; i < v.length; i++) {
40288 var c = v.charAt(i);
40291 if (this.dialCodeMapping[numericChars]) {
40292 dialCode = v.substr(1, i);
40294 if (numericChars.length == 4) {
40304 this.setValue(this.defaultDialCode);
40308 hiddenEl : function()
40310 return this.el.select('input.hidden-tel-input',true).first();
40313 onKeyUp : function(e){
40315 var k = e.getKey();
40316 var c = e.getCharCode();
40319 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40320 this.allowed.indexOf(String.fromCharCode(c)) === -1
40325 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40328 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40332 this.setValue(this.getValue());
40337 * @class Roo.bootstrap.MoneyField
40338 * @extends Roo.bootstrap.ComboBox
40339 * Bootstrap MoneyField class
40342 * Create a new MoneyField.
40343 * @param {Object} config Configuration options
40346 Roo.bootstrap.MoneyField = function(config) {
40348 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40352 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40355 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40357 allowDecimals : true,
40359 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40361 decimalSeparator : ".",
40363 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40365 decimalPrecision : 0,
40367 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40369 allowNegative : true,
40371 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40375 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40377 minValue : Number.NEGATIVE_INFINITY,
40379 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40381 maxValue : Number.MAX_VALUE,
40383 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40385 minText : "The minimum value for this field is {0}",
40387 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40389 maxText : "The maximum value for this field is {0}",
40391 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40392 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40394 nanText : "{0} is not a valid number",
40396 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40400 * @cfg {String} defaults currency of the MoneyField
40401 * value should be in lkey
40403 defaultCurrency : false,
40405 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40407 thousandsDelimiter : false,
40417 getAutoCreate : function()
40419 var align = this.labelAlign || this.parentLabelAlign();
40431 cls : 'form-control roo-money-amount-input',
40432 autocomplete: 'new-password'
40435 var hiddenInput = {
40439 cls: 'hidden-number-input'
40443 hiddenInput.name = this.name;
40446 if (this.disabled) {
40447 input.disabled = true;
40450 var clg = 12 - this.inputlg;
40451 var cmd = 12 - this.inputmd;
40452 var csm = 12 - this.inputsm;
40453 var cxs = 12 - this.inputxs;
40457 cls : 'row roo-money-field',
40461 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40465 cls: 'roo-select2-container input-group',
40469 cls : 'form-control roo-money-currency-input',
40470 autocomplete: 'new-password',
40472 name : this.currencyName
40476 cls : 'input-group-addon',
40490 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40494 cls: this.hasFeedback ? 'has-feedback' : '',
40505 if (this.fieldLabel.length) {
40508 tooltip: 'This field is required'
40514 cls: 'control-label',
40520 html: this.fieldLabel
40523 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40529 if(this.indicatorpos == 'right') {
40530 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40537 if(align == 'left') {
40545 if(this.labelWidth > 12){
40546 label.style = "width: " + this.labelWidth + 'px';
40548 if(this.labelWidth < 13 && this.labelmd == 0){
40549 this.labelmd = this.labelWidth;
40551 if(this.labellg > 0){
40552 label.cls += ' col-lg-' + this.labellg;
40553 input.cls += ' col-lg-' + (12 - this.labellg);
40555 if(this.labelmd > 0){
40556 label.cls += ' col-md-' + this.labelmd;
40557 container.cls += ' col-md-' + (12 - this.labelmd);
40559 if(this.labelsm > 0){
40560 label.cls += ' col-sm-' + this.labelsm;
40561 container.cls += ' col-sm-' + (12 - this.labelsm);
40563 if(this.labelxs > 0){
40564 label.cls += ' col-xs-' + this.labelxs;
40565 container.cls += ' col-xs-' + (12 - this.labelxs);
40576 var settings = this;
40578 ['xs','sm','md','lg'].map(function(size){
40579 if (settings[size]) {
40580 cfg.cls += ' col-' + size + '-' + settings[size];
40587 initEvents : function()
40589 this.indicator = this.indicatorEl();
40591 this.initCurrencyEvent();
40593 this.initNumberEvent();
40596 initCurrencyEvent : function()
40599 throw "can not find store for combo";
40602 this.store = Roo.factory(this.store, Roo.data);
40603 this.store.parent = this;
40607 this.triggerEl = this.el.select('.input-group-addon', true).first();
40609 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40614 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40615 _this.list.setWidth(lw);
40618 this.list.on('mouseover', this.onViewOver, this);
40619 this.list.on('mousemove', this.onViewMove, this);
40620 this.list.on('scroll', this.onViewScroll, this);
40623 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40626 this.view = new Roo.View(this.list, this.tpl, {
40627 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40630 this.view.on('click', this.onViewClick, this);
40632 this.store.on('beforeload', this.onBeforeLoad, this);
40633 this.store.on('load', this.onLoad, this);
40634 this.store.on('loadexception', this.onLoadException, this);
40636 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40637 "up" : function(e){
40638 this.inKeyMode = true;
40642 "down" : function(e){
40643 if(!this.isExpanded()){
40644 this.onTriggerClick();
40646 this.inKeyMode = true;
40651 "enter" : function(e){
40654 if(this.fireEvent("specialkey", this, e)){
40655 this.onViewClick(false);
40661 "esc" : function(e){
40665 "tab" : function(e){
40668 if(this.fireEvent("specialkey", this, e)){
40669 this.onViewClick(false);
40677 doRelay : function(foo, bar, hname){
40678 if(hname == 'down' || this.scope.isExpanded()){
40679 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40687 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40691 initNumberEvent : function(e)
40693 this.inputEl().on("keydown" , this.fireKey, this);
40694 this.inputEl().on("focus", this.onFocus, this);
40695 this.inputEl().on("blur", this.onBlur, this);
40697 this.inputEl().relayEvent('keyup', this);
40699 if(this.indicator){
40700 this.indicator.addClass('invisible');
40703 this.originalValue = this.getValue();
40705 if(this.validationEvent == 'keyup'){
40706 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40707 this.inputEl().on('keyup', this.filterValidation, this);
40709 else if(this.validationEvent !== false){
40710 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40713 if(this.selectOnFocus){
40714 this.on("focus", this.preFocus, this);
40717 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40718 this.inputEl().on("keypress", this.filterKeys, this);
40720 this.inputEl().relayEvent('keypress', this);
40723 var allowed = "0123456789";
40725 if(this.allowDecimals){
40726 allowed += this.decimalSeparator;
40729 if(this.allowNegative){
40733 if(this.thousandsDelimiter) {
40737 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40739 var keyPress = function(e){
40741 var k = e.getKey();
40743 var c = e.getCharCode();
40746 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40747 allowed.indexOf(String.fromCharCode(c)) === -1
40753 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40757 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40762 this.inputEl().on("keypress", keyPress, this);
40766 onTriggerClick : function(e)
40773 this.loadNext = false;
40775 if(this.isExpanded()){
40780 this.hasFocus = true;
40782 if(this.triggerAction == 'all') {
40783 this.doQuery(this.allQuery, true);
40787 this.doQuery(this.getRawValue());
40790 getCurrency : function()
40792 var v = this.currencyEl().getValue();
40797 restrictHeight : function()
40799 this.list.alignTo(this.currencyEl(), this.listAlign);
40800 this.list.alignTo(this.currencyEl(), this.listAlign);
40803 onViewClick : function(view, doFocus, el, e)
40805 var index = this.view.getSelectedIndexes()[0];
40807 var r = this.store.getAt(index);
40810 this.onSelect(r, index);
40814 onSelect : function(record, index){
40816 if(this.fireEvent('beforeselect', this, record, index) !== false){
40818 this.setFromCurrencyData(index > -1 ? record.data : false);
40822 this.fireEvent('select', this, record, index);
40826 setFromCurrencyData : function(o)
40830 this.lastCurrency = o;
40832 if (this.currencyField) {
40833 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40835 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40838 this.lastSelectionText = currency;
40840 //setting default currency
40841 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40842 this.setCurrency(this.defaultCurrency);
40846 this.setCurrency(currency);
40849 setFromData : function(o)
40853 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40855 this.setFromCurrencyData(c);
40860 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40862 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40865 this.setValue(value);
40869 setCurrency : function(v)
40871 this.currencyValue = v;
40874 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40879 setValue : function(v)
40881 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40887 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40889 this.inputEl().dom.value = (v == '') ? '' :
40890 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40892 if(!this.allowZero && v === '0') {
40893 this.hiddenEl().dom.value = '';
40894 this.inputEl().dom.value = '';
40901 getRawValue : function()
40903 var v = this.inputEl().getValue();
40908 getValue : function()
40910 return this.fixPrecision(this.parseValue(this.getRawValue()));
40913 parseValue : function(value)
40915 if(this.thousandsDelimiter) {
40917 r = new RegExp(",", "g");
40918 value = value.replace(r, "");
40921 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40922 return isNaN(value) ? '' : value;
40926 fixPrecision : function(value)
40928 if(this.thousandsDelimiter) {
40930 r = new RegExp(",", "g");
40931 value = value.replace(r, "");
40934 var nan = isNaN(value);
40936 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40937 return nan ? '' : value;
40939 return parseFloat(value).toFixed(this.decimalPrecision);
40942 decimalPrecisionFcn : function(v)
40944 return Math.floor(v);
40947 validateValue : function(value)
40949 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40953 var num = this.parseValue(value);
40956 this.markInvalid(String.format(this.nanText, value));
40960 if(num < this.minValue){
40961 this.markInvalid(String.format(this.minText, this.minValue));
40965 if(num > this.maxValue){
40966 this.markInvalid(String.format(this.maxText, this.maxValue));
40973 validate : function()
40975 if(this.disabled || this.allowBlank){
40980 var currency = this.getCurrency();
40982 if(this.validateValue(this.getRawValue()) && currency.length){
40987 this.markInvalid();
40991 getName: function()
40996 beforeBlur : function()
41002 var v = this.parseValue(this.getRawValue());
41009 onBlur : function()
41013 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41014 //this.el.removeClass(this.focusClass);
41017 this.hasFocus = false;
41019 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41023 var v = this.getValue();
41025 if(String(v) !== String(this.startValue)){
41026 this.fireEvent('change', this, v, this.startValue);
41029 this.fireEvent("blur", this);
41032 inputEl : function()
41034 return this.el.select('.roo-money-amount-input', true).first();
41037 currencyEl : function()
41039 return this.el.select('.roo-money-currency-input', true).first();
41042 hiddenEl : function()
41044 return this.el.select('input.hidden-number-input',true).first();